mirror of https://github.com/OpenMW/openmw.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
14 KiB
Lua
346 lines
14 KiB
Lua
local camera = require('openmw.camera')
|
|
local core = require('openmw.core')
|
|
local debug = require('openmw.debug')
|
|
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 storage = require('openmw.storage')
|
|
local I = require('openmw.interfaces')
|
|
|
|
local Actor = require('openmw.types').Actor
|
|
local Player = require('openmw.types').Player
|
|
|
|
input.registerAction {
|
|
key = 'TogglePOV',
|
|
l10n = 'OMWControls',
|
|
name = 'TogglePOV_name',
|
|
description = 'TogglePOV_description',
|
|
type = input.ACTION_TYPE.Boolean,
|
|
defaultValue = false,
|
|
}
|
|
|
|
input.registerAction {
|
|
key = 'Zoom3rdPerson',
|
|
l10n = 'OMWControls',
|
|
name = 'Zoom3rdPerson_name',
|
|
description = 'Zoom3rdPerson_description',
|
|
type = input.ACTION_TYPE.Number,
|
|
defaultValue = 0,
|
|
}
|
|
|
|
local settings = storage.playerSection('SettingsOMWCameraThirdPerson')
|
|
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 = {}
|
|
local noStandingPreview = {}
|
|
local noHeadBobbing = {}
|
|
local noZoom = {}
|
|
|
|
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.getBooleanActionValue('TogglePOV') and Player.getControlSwitch(self, Player.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 = Player.getControlSwitch(self, Player.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 Player.getControlSwitch(self, Player.CONTROL_SWITCH.ViewMode) or
|
|
not Player.getControlSwitch(self, Player.CONTROL_SWITCH.Controls) or
|
|
camera.getMode() == MODE.Static or next(noZoom) 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 not next(noModeControl) 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 not next(noModeControl) then
|
|
primaryMode = MODE.ThirdPerson
|
|
camera.setMode(primaryMode)
|
|
third_person.baseDistance = minDistance
|
|
end
|
|
end
|
|
|
|
local function updateStandingPreview()
|
|
local mode = camera.getMode()
|
|
if not previewIfStandStill or next(noStandingPreview)
|
|
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() or I.UI.getMode() 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
|
|
local paralysis = Actor.activeEffects(self):getEffect(core.magic.EFFECT_TYPE.Paralyze)
|
|
local paralyzed = not debug.isGodMode() and paralysis.magnitude > 0
|
|
if not next(noModeControl) and not paralyzed then
|
|
updatePOV(dt)
|
|
updateVanity(dt)
|
|
end
|
|
updateStandingPreview()
|
|
updateCrosshair()
|
|
end
|
|
|
|
do
|
|
local Zoom3rdPerson = input.getNumberActionValue('Zoom3rdPerson')
|
|
if Zoom3rdPerson ~= 0 then
|
|
zoom(Zoom3rdPerson)
|
|
end
|
|
end
|
|
|
|
third_person.update(dt, smoothedSpeed)
|
|
if not next(noHeadBobbing) 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 is 1
|
|
-- @field [parent=#Camera] #number version
|
|
version = 1,
|
|
|
|
--- Return primary mode (MODE.FirstPerson or MODE.ThirdPerson).
|
|
-- @function [parent=#Camera] getPrimaryMode
|
|
-- @return #number @{openmw.camera#MODE}
|
|
getPrimaryMode = function() return primaryMode end,
|
|
|
|
--- Get base third person distance (without applying angle and speed modifiers).
|
|
-- @function [parent=#Camera] getBaseThirdPersonDistance
|
|
-- @return #number
|
|
getBaseThirdPersonDistance = function() return third_person.baseDistance end,
|
|
--- Set base third person distance
|
|
-- @function [parent=#Camera] setBaseThirdPersonDistance
|
|
-- @param #number value
|
|
setBaseThirdPersonDistance = function(v) third_person.baseDistance = v end,
|
|
--- Get the desired third person distance if there would be no obstacles (with angle and speed modifiers)
|
|
-- @function [parent=#Camera] getTargetThirdPersonDistance
|
|
-- @return #number
|
|
getTargetThirdPersonDistance = function() return third_person.preferredDistance end,
|
|
|
|
--- Whether the built-in mode control logic is enabled.
|
|
-- @function [parent=#Camera] isModeControlEnabled
|
|
-- @return #boolean
|
|
isModeControlEnabled = function() return not next(noModeControl) end,
|
|
--- Disable with (optional) tag until the corresponding enable function is called with the same tag.
|
|
-- @function [parent=#Camera] disableModeControl
|
|
-- @param #string tag (optional, empty string by default) Will be disabled until the enabling function is called with the same tag
|
|
disableModeControl = function(tag) noModeControl[tag or ''] = true end,
|
|
--- Undo disableModeControl
|
|
-- @function [parent=#Camera] enableModeControl
|
|
-- @param #string tag (optional, empty string by default)
|
|
enableModeControl = function(tag) noModeControl[tag or ''] = nil end,
|
|
|
|
--- Whether the built-in standing preview logic is enabled.
|
|
-- @function [parent=#Camera] isStandingPreviewEnabled
|
|
-- @return #boolean
|
|
isStandingPreviewEnabled = function() return previewIfStandStill and not next(noStandingPreview) end,
|
|
--- Disable with (optional) tag until the corresponding enable function is called with the same tag.
|
|
-- @function [parent=#Camera] disableStandingPreview
|
|
-- @param #string tag (optional, empty string by default) Will be disabled until the enabling function is called with the same tag
|
|
disableStandingPreview = function(tag) noStandingPreview[tag or ''] = true end,
|
|
--- Undo disableStandingPreview
|
|
-- @function [parent=#Camera] enableStandingPreview
|
|
-- @param #string tag (optional, empty string by default)
|
|
enableStandingPreview = function(tag) noStandingPreview[tag or ''] = nil end,
|
|
|
|
--- Whether head bobbing is enabled.
|
|
-- @function [parent=#Camera] isHeadBobbingEnabled
|
|
-- @return #boolean
|
|
isHeadBobbingEnabled = function() return head_bobbing.enabled and not next(noHeadBobbing) end,
|
|
--- Disable with (optional) tag until the corresponding enable function is called with the same tag.
|
|
-- @function [parent=#Camera] disableHeadBobbing
|
|
-- @param #string tag (optional, empty string by default) Will be disabled until the enabling function is called with the same tag
|
|
disableHeadBobbing = function(tag) noHeadBobbing[tag or ''] = true end,
|
|
--- Undo disableHeadBobbing
|
|
-- @function [parent=#Camera] enableHeadBobbing
|
|
-- @param #string tag (optional, empty string by default)
|
|
enableHeadBobbing = function(tag) noHeadBobbing[tag or ''] = nil end,
|
|
|
|
--- Whether the built-in zooming is enabled.
|
|
-- @function [parent=#Camera] isZoomEnabled
|
|
-- @return #boolean
|
|
isZoomEnabled = function() return not next(noZoom) end,
|
|
--- Disable with (optional) tag until the corresponding enable function is called with the same tag.
|
|
-- @function [parent=#Camera] disableZoom
|
|
-- @param #string tag (optional, empty string by default) Will be disabled until the enabling function is called with the same tag
|
|
disableZoom = function(tag) noZoom[tag or ''] = true end,
|
|
--- Undo disableZoom
|
|
-- @function [parent=#Camera] enableZoom
|
|
-- @param #string tag (optional, empty string by default)
|
|
enableZoom = function(tag) noZoom[tag or ''] = nil end,
|
|
|
|
--- Whether the the third person offset can be changed by the built-in camera script.
|
|
-- @function [parent=#Camera] isThirdPersonOffsetControlEnabled
|
|
-- @return #boolean
|
|
isThirdPersonOffsetControlEnabled = function() return not next(third_person.noOffsetControl) end,
|
|
--- Disable with (optional) tag until the corresponding enable function is called with the same tag.
|
|
-- @function [parent=#Camera] disableThirdPersonOffsetControl
|
|
-- @param #string tag (optional, empty string by default) Will be disabled until the enabling function is called with the same tag
|
|
disableThirdPersonOffsetControl = function(tag) third_person.noOffsetControl[tag or ''] = true end,
|
|
--- Undo disableThirdPersonOffsetControl
|
|
-- @function [parent=#Camera] enableThirdPersonOffsetControl
|
|
-- @param #string tag (optional, empty string by default)
|
|
enableThirdPersonOffsetControl = function(tag) third_person.noOffsetControl[tag or ''] = nil end,
|
|
},
|
|
engineHandlers = {
|
|
onUpdate = onUpdate,
|
|
onFrame = onFrame,
|
|
onTeleported = function()
|
|
camera.instantTransition()
|
|
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,
|
|
},
|
|
}
|