diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index a7d6cf320f..40c5456f8a 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -26,6 +26,8 @@ set(BUILTIN_DATA_FILES scripts/omw/camera/head_bobbing.lua scripts/omw/camera/third_person.lua scripts/omw/camera/settings.lua + scripts/omw/camera/move360.lua + scripts/omw/camera/first_person_auto_switch.lua scripts/omw/console/player.lua scripts/omw/console/global.lua scripts/omw/console/local.lua diff --git a/files/data/l10n/OMWCamera/en.yaml b/files/data/l10n/OMWCamera/en.yaml index 9d5e7ac7ee..eefc192960 100644 --- a/files/data/l10n/OMWCamera/en.yaml +++ b/files/data/l10n/OMWCamera/en.yaml @@ -1,5 +1,5 @@ -Camera: "Camera" -settingsPageDescription: "OpenMW camera settings" +Camera: "OpenMW Camera" +settingsPageDescription: "OpenMW Camera settings" thirdPersonSettings: "Third person mode" @@ -42,6 +42,19 @@ ignoreNC: "Ignore 'No Collision' flag" ignoreNCDescription: > Prevents camera from clipping through the objects with the NC (No Collision) NIF flag. +move360: "Move 360" +move360Description: > + When without weapon in hands the character rotates to the direction of movement. I.e. looks to the camera when run backwards. + +move360TurnSpeed: "Move 360 turning speed" +move360TurnSpeedDescription: A multiplier for the turning speed (5.0 by default) + +slowViewChange: "Smooth view change" +slowViewChangeDescription: "Makes switching from 1st person to 3rd person view not instant." + +povAutoSwitch: "First person auto switch" +povAutoSwitchDescription: "Auto switch to 1st person view if there is an obstacle right behind the player." + headBobbingSettings: "Head bobbing in first person view" diff --git a/files/data/scripts/omw/camera/camera.lua b/files/data/scripts/omw/camera/camera.lua index 26dcfc036c..ce1bd2d48b 100644 --- a/files/data/scripts/omw/camera/camera.lua +++ b/files/data/scripts/omw/camera/camera.lua @@ -11,11 +11,14 @@ local Actor = require('openmw.types').Actor local settings = require('scripts.omw.camera.settings').thirdPerson local head_bobbing = require('scripts.omw.camera.head_bobbing') local third_person = require('scripts.omw.camera.third_person') +local pov_auto_switch = require('scripts.omw.camera.first_person_auto_switch') +local move360 = require('scripts.omw.camera.move360') local MODE = camera.MODE local previewIfStandStill = false local showCrosshairInThirdPerson = false +local slowViewChange = false local function updateSettings() previewIfStandStill = settings:get('previewIfStandStill') @@ -27,6 +30,10 @@ local function updateSettings() collisionType = util.bitOr(collisionType, nearby.COLLISION_TYPE.VisualOnly) end camera.setCollisionType(collisionType) + move360.enabled = settings:get('move360') + move360.turnSpeed = settings:get('move360TurnSpeed') + pov_auto_switch.enabled = settings:get('povAutoSwitch') + slowViewChange = settings:get('slowViewChange') end local primaryMode @@ -167,6 +174,7 @@ local function onUpdate(dt) camera.setExtraRoll(0) camera.setFirstPersonOffset(util.vector3(0, 0, 0)) updateSmoothedSpeed(dt) + pov_auto_switch.onUpdate(dt) end local function onFrame(dt) @@ -188,6 +196,12 @@ local function onFrame(dt) applyControllerZoom(dt) third_person.update(dt, smoothedSpeed) if noHeadBobbing == 0 then head_bobbing.update(dt, smoothedSpeed) end + if slowViewChange then + local maxIncrease = dt * (100 + third_person.baseDistance) + camera.setPreferredThirdPersonDistance( + math.min(camera.getThirdPersonDistance() + maxIncrease, third_person.preferredDistance)) + end + move360.onFrame(dt) end return { @@ -255,6 +269,7 @@ return { elseif action == input.ACTION.ZoomOut then zoom(-10) end + move360.onInputAction(action) end, onActive = init, onLoad = function(data) @@ -265,4 +280,3 @@ return { end, }, } - diff --git a/files/data/scripts/omw/camera/first_person_auto_switch.lua b/files/data/scripts/omw/camera/first_person_auto_switch.lua new file mode 100644 index 0000000000..a1c0b863c9 --- /dev/null +++ b/files/data/scripts/omw/camera/first_person_auto_switch.lua @@ -0,0 +1,52 @@ +local camera = require('openmw.camera') +local util = require('openmw.util') +local nearby = require('openmw.nearby') +local self = require('openmw.self') + +local forcedFirstPerson = false +local limitSwitch = 40 +local limitReturn = 65 + +local rayOptions = {collisionType = nearby.COLLISION_TYPE.Default - nearby.COLLISION_TYPE.Actor} +local function castRayBackward() + local from = camera.getTrackedPosition() + local orient = util.transform.rotateZ(camera.getYaw()) * util.transform.rotateX(camera.getPitch()) + local resLeft = nearby.castRay(from, from + orient * util.vector3(-30, -limitReturn, 0), rayOptions) + local resRight = nearby.castRay(from, from + orient * util.vector3(30, -limitReturn, 0), rayOptions) + local distLeft = limitReturn + 1 + local distRight = limitReturn + 1 + if resLeft.hit then distLeft = (resLeft.hitPos - from):length() end + if resRight.hit then distRight = (resRight.hitPos - from):length() end + return math.min(distLeft, distRight) +end + +local M = { + enabled = false, +} + +function M.onUpdate(dt) + if camera.getMode() ~= camera.MODE.FirstPerson then forcedFirstPerson = false end + if not M.enabled then + if forcedFirstPerson then + camera.setMode(camera.MODE.ThirdPerson, false) + forcedFirstPerson = false + end + return + end + if camera.getMode() == camera.MODE.ThirdPerson and camera.getThirdPersonDistance() < limitSwitch + and math.abs(util.normalizeAngle(camera.getYaw() - self.rotation.z)) < math.rad(10) then + if castRayBackward() <= limitSwitch then + camera.setMode(camera.MODE.FirstPerson, true) + forcedFirstPerson = true + end + return + end + if forcedFirstPerson then + if castRayBackward() > limitReturn then + camera.setMode(camera.MODE.ThirdPerson, false) + forcedFirstPerson = false + end + end +end + +return M diff --git a/files/data/scripts/omw/camera/move360.lua b/files/data/scripts/omw/camera/move360.lua new file mode 100644 index 0000000000..5247c03a30 --- /dev/null +++ b/files/data/scripts/omw/camera/move360.lua @@ -0,0 +1,73 @@ +local core = require('openmw.core') +local camera = require('openmw.camera') +local input = require('openmw.input') +local self = require('openmw.self') +local util = require('openmw.util') +local I = require('openmw.interfaces') + +local Actor = require('openmw.types').Actor + +local MODE = camera.MODE + +local active = false + +local M = { + enabled = false, + turnSpeed = 5, +} + +local function turnOn() + I.Camera.disableStandingPreview() + active = true +end + +local function turnOff() + I.Camera.enableStandingPreview() + active = false + if camera.getMode() == MODE.Preview then + camera.setMode(MODE.ThirdPerson) + end +end + +function M.onFrame(dt) + if core.isWorldPaused() then return end + local newActive = M.enabled and Actor.stance(self) == Actor.STANCE.Nothing + if newActive and not active then + turnOn() + elseif not newActive and active then + turnOff() + end + if not active then return end + if camera.getMode() == MODE.Static then return end + if camera.getMode() == MODE.ThirdPerson then camera.setMode(MODE.Preview) end + if camera.getMode() == MODE.Preview and not input.isActionPressed(input.ACTION.TogglePOV) then + camera.showCrosshair(camera.getFocalPreferredOffset():length() > 5) + local move = util.vector2(self.controls.sideMovement, self.controls.movement) + local yawDelta = camera.getYaw() - self.rotation.z + move = move:rotate(-yawDelta) + self.controls.sideMovement = move.x + self.controls.movement = move.y + self.controls.pitchChange = camera.getPitch() * math.cos(yawDelta) - self.rotation.x + if move:length() > 0.05 then + local delta = math.atan2(move.x, move.y) + local maxDelta = math.max(delta, 1) * M.turnSpeed * dt + self.controls.yawChange = util.clamp(delta, -maxDelta, maxDelta) + else + self.controls.yawChange = 0 + end + end +end + +function M.onInputAction(action) + if not active or core.isWorldPaused() then return end + if action == input.ACTION.ZoomIn and camera.getMode() == MODE.Preview + and I.Camera.getBaseThirdPersonDistance() == 30 then + self.controls.yawChange = camera.getYaw() - self.rotation.z + camera.setMode(MODE.FirstPerson) + elseif action == input.ACTION.ZoomOut and camera.getMode() == MODE.FirstPerson then + camera.setMode(MODE.Preview) + I.Camera.setBaseThirdPersonDistance(30) + end +end + +return M diff --git a/files/data/scripts/omw/camera/settings.lua b/files/data/scripts/omw/camera/settings.lua index ec7f8a9c07..06cbbfc6f0 100644 --- a/files/data/scripts/omw/camera/settings.lua +++ b/files/data/scripts/omw/camera/settings.lua @@ -48,6 +48,10 @@ I.Settings.registerGroup({ boolSetting('', 'previewIfStandStill', true), boolSetting('', 'deferredPreviewRotation', true), boolSetting('', 'ignoreNC', true), + boolSetting('', 'move360', false), + floatSetting('', 'move360TurnSpeed', 5), + boolSetting('', 'slowViewChange', false), + boolSetting('', 'povAutoSwitch', false), }, }) @@ -72,11 +76,14 @@ local settings = { } local function updateViewOverShoulderDisabled() - local disabled = not settings.thirdPerson:get('viewOverShoulder') - I.Settings.updateRendererArgument(thirdPersonGroup, 'shoulderOffsetX', {disabled = disabled}) - I.Settings.updateRendererArgument(thirdPersonGroup, 'shoulderOffsetY', {disabled = disabled}) - I.Settings.updateRendererArgument(thirdPersonGroup, 'autoSwitchShoulder', {disabled = disabled}) - I.Settings.updateRendererArgument(thirdPersonGroup, 'zoomOutWhenMoveCoef', {disabled = disabled}) + local shoulderDisabled = not settings.thirdPerson:get('viewOverShoulder') + I.Settings.updateRendererArgument(thirdPersonGroup, 'shoulderOffsetX', {disabled = shoulderDisabled}) + I.Settings.updateRendererArgument(thirdPersonGroup, 'shoulderOffsetY', {disabled = shoulderDisabled}) + I.Settings.updateRendererArgument(thirdPersonGroup, 'autoSwitchShoulder', {disabled = shoulderDisabled}) + I.Settings.updateRendererArgument(thirdPersonGroup, 'zoomOutWhenMoveCoef', {disabled = shoulderDisabled}) + + local move360Disabled = not settings.thirdPerson:get('move360') + I.Settings.updateRendererArgument(thirdPersonGroup, 'move360TurnSpeed', {disabled = move360Disabled}) end local function updateHeadBobbingDisabled()