1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-08 09:37:53 +00:00
OpenMW/files/data/scripts/omw/camera/camera.lua

293 lines
11 KiB
Lua

local camera = require('openmw.camera')
local core = require('openmw.core')
local input = require('openmw.input')
local util = require('openmw.util')
local self = require('openmw.self')
local nearby = require('openmw.nearby')
local async = require('openmw.async')
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')
showCrosshairInThirdPerson = settings:get('viewOverShoulder')
camera.allowCharacterDeferredRotation(settings:get('deferredPreviewRotation'))
local collisionType = util.bitAnd(nearby.COLLISION_TYPE.Default, util.bitNot(nearby.COLLISION_TYPE.Actor))
collisionType = util.bitOr(collisionType, nearby.COLLISION_TYPE.Camera)
if settings:get('ignoreNC') then
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
local noModeControl = 0
local noStandingPreview = 0
local noHeadBobbing = 0
local noZoom = 0
local function init()
camera.setFieldOfView(camera.getBaseFieldOfView())
if camera.getMode() == MODE.FirstPerson then
primaryMode = MODE.FirstPerson
else
primaryMode = MODE.ThirdPerson
camera.setMode(MODE.ThirdPerson)
end
updateSettings()
end
settings:subscribe(async:callback(updateSettings))
local smoothedSpeed = 0
local previewTimer = 0
local function updatePOV(dt)
local switchLimit = 0.25
if input.isActionPressed(input.ACTION.TogglePOV) and input.getControlSwitch(input.CONTROL_SWITCH.ViewMode) then
previewTimer = previewTimer + dt
if primaryMode == MODE.ThirdPerson or previewTimer >= switchLimit then
third_person.standingPreview = false
camera.setMode(MODE.Preview)
end
elseif previewTimer > 0 then
if previewTimer <= switchLimit then
if primaryMode == MODE.FirstPerson then
primaryMode = MODE.ThirdPerson
else
primaryMode = MODE.FirstPerson
end
end
camera.setMode(primaryMode)
if camera.getMode() == MODE.Preview then
-- If Preview -> FirstPerson change is queued (because of 3rd person animation),
-- then first exit Preview by switching to ThirdPerson, and then queue the switch to FirstPerson.
camera.setMode(MODE.ThirdPerson)
camera.setMode(MODE.FirstPerson)
end
previewTimer = 0
end
end
local idleTimer = 0
local vanityDelay = core.getGMST('fVanityDelay')
local function updateVanity(dt)
local vanityAllowed = input.getControlSwitch(input.CONTROL_SWITCH.VanityMode)
if vanityAllowed and idleTimer > vanityDelay and camera.getMode() ~= MODE.Vanity then
camera.setMode(MODE.Vanity)
end
if camera.getMode() == MODE.Vanity then
if not vanityAllowed or idleTimer == 0 then
camera.setMode(primaryMode)
else
camera.setYaw(camera.getYaw() + math.rad(3) * dt)
end
end
end
local function updateSmoothedSpeed(dt)
local speed = Actor.getCurrentSpeed(self)
speed = speed / (1 + speed / 500)
local maxDelta = 300 * dt
smoothedSpeed = smoothedSpeed + util.clamp(speed - smoothedSpeed, -maxDelta, maxDelta)
end
local minDistance = 30
local maxDistance = 800
local function zoom(delta)
if not input.getControlSwitch(input.CONTROL_SWITCH.ViewMode) or
not input.getControlSwitch(input.CONTROL_SWITCH.Controls) or
camera.getMode() == MODE.Static or noZoom > 0 then
return
end
if camera.getMode() ~= MODE.FirstPerson then
local obstacleDelta = third_person.preferredDistance - camera.getThirdPersonDistance()
if delta > 0 and third_person.baseDistance == minDistance and
(camera.getMode() ~= MODE.Preview or third_person.standingPreview) and noModeControl == 0 then
primaryMode = MODE.FirstPerson
camera.setMode(primaryMode)
elseif delta > 0 or obstacleDelta < -delta then
third_person.baseDistance = util.clamp(third_person.baseDistance - delta - obstacleDelta, minDistance, maxDistance)
end
elseif delta < 0 and noModeControl == 0 then
primaryMode = MODE.ThirdPerson
camera.setMode(primaryMode)
third_person.baseDistance = minDistance
end
end
local function applyControllerZoom(dt)
if input.isActionPressed(input.ACTION.TogglePOV) then
local triggerLeft = input.getAxisValue(input.CONTROLLER_AXIS.TriggerLeft)
local triggerRight = input.getAxisValue(input.CONTROLLER_AXIS.TriggerRight)
local controllerZoom = (triggerRight - triggerLeft) * 100 * dt
if controllerZoom ~= 0 then
zoom(controllerZoom)
end
end
end
local function updateStandingPreview()
local mode = camera.getMode()
if not previewIfStandStill or noStandingPreview > 0
or mode == MODE.FirstPerson or mode == MODE.Static or mode == MODE.Vanity then
third_person.standingPreview = false
return
end
local standingStill = Actor.getCurrentSpeed(self) == 0 and Actor.getStance(self) == Actor.STANCE.Nothing
if standingStill and mode == MODE.ThirdPerson then
third_person.standingPreview = true
camera.setMode(MODE.Preview)
elseif not standingStill and third_person.standingPreview then
third_person.standingPreview = false
camera.setMode(primaryMode)
end
end
local function updateCrosshair()
camera.showCrosshair(
camera.getMode() == MODE.FirstPerson or
(showCrosshairInThirdPerson and (camera.getMode() == MODE.ThirdPerson or third_person.standingPreview)))
end
local function onUpdate(dt)
camera.setExtraPitch(0)
camera.setExtraYaw(0)
camera.setExtraRoll(0)
camera.setFirstPersonOffset(util.vector3(0, 0, 0))
updateSmoothedSpeed(dt)
pov_auto_switch.onUpdate(dt)
end
local function updateIdleTimer(dt)
if not input.isIdle() then
idleTimer = 0
elseif self.controls.movement ~= 0 or self.controls.sideMovement ~= 0 or self.controls.jump or self.controls.use ~= 0 then
idleTimer = 0 -- also reset the timer in case of a scripted movement
else
idleTimer = idleTimer + dt
end
end
local function onFrame(dt)
if core.isWorldPaused() then return end
updateIdleTimer(dt)
local mode = camera.getMode()
if (mode == MODE.FirstPerson or mode == MODE.ThirdPerson) and not camera.getQueuedMode() then
primaryMode = mode
end
if mode ~= MODE.Static then
if noModeControl == 0 then
updatePOV(dt)
updateVanity(dt)
end
updateStandingPreview()
updateCrosshair()
end
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 {
interfaceName = 'Camera',
---
-- @module Camera
-- @usage require('openmw.interfaces').Camera
interface = {
--- Interface version
-- @field [parent=#Camera] #number version
version = 0,
--- Return primary mode (MODE.FirstPerson or MODE.ThirdPerson).
-- @function [parent=#Camera] getPrimaryMode
getPrimaryMode = function() return primaryMode end,
--- @function [parent=#Camera] getBaseThirdPersonDistance
getBaseThirdPersonDistance = function() return third_person.baseDistance end,
--- @function [parent=#Camera] setBaseThirdPersonDistance
setBaseThirdPersonDistance = function(v) third_person.baseDistance = v end,
--- @function [parent=#Camera] getTargetThirdPersonDistance
getTargetThirdPersonDistance = function() return third_person.preferredDistance end,
--- @function [parent=#Camera] isModeControlEnabled
isModeControlEnabled = function() return noModeControl == 0 end,
--- @function [parent=#Camera] disableModeControl
disableModeControl = function() noModeControl = noModeControl + 1 end,
--- @function [parent=#Camera] enableModeControl
enableModeControl = function() noModeControl = math.max(0, noModeControl - 1) end,
--- @function [parent=#Camera] isStandingPreviewEnabled
isStandingPreviewEnabled = function() return previewIfStandStill and noStandingPreview == 0 end,
--- @function [parent=#Camera] disableStandingPreview
disableStandingPreview = function() noStandingPreview = noStandingPreview + 1 end,
--- @function [parent=#Camera] enableStandingPreview
enableStandingPreview = function() noStandingPreview = math.max(0, noStandingPreview - 1) end,
--- @function [parent=#Camera] isHeadBobbingEnabled
isHeadBobbingEnabled = function() return head_bobbing.enabled and noHeadBobbing == 0 end,
--- @function [parent=#Camera] disableHeadBobbing
disableHeadBobbing = function() noHeadBobbing = noHeadBobbing + 1 end,
--- @function [parent=#Camera] enableHeadBobbing
enableHeadBobbing = function() noHeadBobbing = math.max(0, noHeadBobbing - 1) end,
--- @function [parent=#Camera] isZoomEnabled
isZoomEnabled = function() return noZoom == 0 end,
--- @function [parent=#Camera] disableZoom
disableZoom = function() noZoom = noZoom + 1 end,
--- @function [parent=#Camera] enableZoom
enableZoom = function() noZoom = math.max(0, noZoom - 1) end,
--- @function [parent=#Camera] isThirdPersonOffsetControlEnabled
isThirdPersonOffsetControlEnabled = function() return third_person.noOffsetControl == 0 end,
--- @function [parent=#Camera] disableThirdPersonOffsetControl
disableThirdPersonOffsetControl = function() third_person.noOffsetControl = third_person.noOffsetControl + 1 end,
--- @function [parent=#Camera] enableThirdPersonOffsetControl
enableThirdPersonOffsetControl = function() third_person.noOffsetControl = math.max(0, third_person.noOffsetControl - 1) end,
},
engineHandlers = {
onUpdate = onUpdate,
onFrame = onFrame,
onInputAction = function(action)
if core.isWorldPaused() then return end
if action == input.ACTION.ZoomIn then
zoom(10)
elseif action == input.ACTION.ZoomOut then
zoom(-10)
end
move360.onInputAction(action)
end,
onActive = init,
onLoad = function(data)
if data and data.distance then third_person.baseDistance = data.distance end
end,
onSave = function()
return {version = 0, distance = third_person.baseDistance}
end,
},
}