mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-01 03:21:41 +00:00
140 lines
5.4 KiB
Lua
140 lines
5.4 KiB
Lua
local camera = require('openmw.camera')
|
|
local settings = require('openmw.settings')
|
|
local util = require('openmw.util')
|
|
local self = require('openmw.self')
|
|
local nearby = require('openmw.nearby')
|
|
|
|
local MODE = camera.MODE
|
|
local STATE = { RightShoulder = 0, LeftShoulder = 1, Combat = 2, Swimming = 3 }
|
|
|
|
local M = {
|
|
baseDistance = settings._getFloatFromSettingsCfg('Camera', 'third person camera distance'),
|
|
preferredDistance = 0,
|
|
standingPreview = false,
|
|
noOffsetControl = 0,
|
|
}
|
|
|
|
local viewOverShoulder = settings._getBoolFromSettingsCfg('Camera', 'view over shoulder')
|
|
local autoSwitchShoulder = settings._getBoolFromSettingsCfg('Camera', 'auto switch shoulder')
|
|
local shoulderOffset = settings._getVector2FromSettingsCfg('Camera', 'view over shoulder offset')
|
|
local zoomOutWhenMoveCoef = settings._getFloatFromSettingsCfg('Camera', 'zoom out when move coef')
|
|
|
|
local defaultShoulder = (shoulderOffset.x > 0 and STATE.RightShoulder) or STATE.LeftShoulder
|
|
local rightShoulderOffset = util.vector2(math.abs(shoulderOffset.x), shoulderOffset.y)
|
|
local leftShoulderOffset = util.vector2(-math.abs(shoulderOffset.x), shoulderOffset.y)
|
|
local combatOffset = util.vector2(0, 15)
|
|
|
|
local state = defaultShoulder
|
|
|
|
local rayOptions = {collisionType = nearby.COLLISION_TYPE.Default - nearby.COLLISION_TYPE.Actor}
|
|
local function ray(from, angle, limit)
|
|
local to = from + util.transform.rotateZ(angle) * util.vector3(0, limit, 0)
|
|
local res = nearby.castRay(from, to, rayOptions)
|
|
if res.hit then
|
|
return (res.hitPos - from):length()
|
|
else
|
|
return limit
|
|
end
|
|
end
|
|
|
|
local function trySwitchShoulder()
|
|
local limitToSwitch = 120 -- switch to other shoulder if wall is closer than this limit
|
|
local limitToSwitchBack = 300 -- switch back to default shoulder if there is no walls at this distance
|
|
|
|
local pos = camera.getTrackedPosition()
|
|
local rayRight = ray(pos, camera.getYaw() + math.rad(90), limitToSwitchBack + 1)
|
|
local rayLeft = ray(pos, camera.getYaw() - math.rad(90), limitToSwitchBack + 1)
|
|
local rayRightForward = ray(pos, camera.getYaw() + math.rad(30), limitToSwitchBack + 1)
|
|
local rayLeftForward = ray(pos, camera.getYaw() - math.rad(30), limitToSwitchBack + 1)
|
|
|
|
local distRight = math.min(rayRight, rayRightForward)
|
|
local distLeft = math.min(rayLeft, rayLeftForward)
|
|
|
|
if distLeft < limitToSwitch and distRight > limitToSwitchBack then
|
|
state = STATE.RightShoulder
|
|
elseif distRight < limitToSwitch and distLeft > limitToSwitchBack then
|
|
state = STATE.LeftShoulder
|
|
elseif distRight > limitToSwitchBack and distLeft > limitToSwitchBack then
|
|
state = defaultShoulder
|
|
end
|
|
end
|
|
|
|
local function calculateDistance(smoothedSpeed)
|
|
local smoothedSpeedSqr = smoothedSpeed * smoothedSpeed
|
|
return (M.baseDistance + math.max(camera.getPitch(), 0) * 50
|
|
+ smoothedSpeedSqr / (smoothedSpeedSqr + 300*300) * zoomOutWhenMoveCoef)
|
|
end
|
|
|
|
local noThirdPersonLastFrame = true
|
|
|
|
local function updateState()
|
|
local mode = camera.getMode()
|
|
local oldState = state
|
|
if (self:isInWeaponStance() or self:isInMagicStance()) and mode == MODE.ThirdPerson then
|
|
state = STATE.Combat
|
|
elseif self:isSwimming() then
|
|
state = STATE.Swimming
|
|
elseif oldState == STATE.Combat or oldState == STATE.Swimming then
|
|
state = defaultShoulder
|
|
end
|
|
if autoSwitchShoulder and (mode == MODE.ThirdPerson or state ~= oldState or noThirdPersonLastFrame)
|
|
and (state == STATE.LeftShoulder or state == STATE.RightShoulder) then
|
|
trySwitchShoulder()
|
|
end
|
|
if oldState ~= state or noThirdPersonLastFrame then
|
|
-- State was changed, start focal point transition.
|
|
if mode == MODE.Vanity then
|
|
-- Player doesn't touch controls for a long time. Transition should be very slow.
|
|
camera.setFocalTransitionSpeed(0.2)
|
|
elseif (oldState == STATE.Combat or state == STATE.Combat) and
|
|
(mode ~= MODE.Preview or M.standingPreview) then
|
|
-- Transition to/from combat mode and we are not in preview mode. Should be fast.
|
|
camera.setFocalTransitionSpeed(5.0)
|
|
else
|
|
camera.setFocalTransitionSpeed(1.0) -- Default transition speed.
|
|
end
|
|
|
|
if state == STATE.RightShoulder then
|
|
camera.setFocalPreferredOffset(rightShoulderOffset)
|
|
elseif state == STATE.LeftShoulder then
|
|
camera.setFocalPreferredOffset(leftShoulderOffset)
|
|
else
|
|
camera.setFocalPreferredOffset(combatOffset)
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.update(dt, smoothedSpeed)
|
|
local mode = camera.getMode()
|
|
if mode == MODE.FirstPerson or mode == MODE.Static then
|
|
noThirdPersonLastFrame = true
|
|
return
|
|
end
|
|
if not viewOverShoulder then
|
|
M.preferredDistance = M.baseDistance
|
|
camera.setPreferredThirdPersonDistance(M.baseDistance)
|
|
noThirdPersonLastFrame = false
|
|
return
|
|
end
|
|
|
|
if M.noOffsetControl == 0 then
|
|
updateState()
|
|
else
|
|
state = nil
|
|
end
|
|
|
|
M.preferredDistance = calculateDistance(smoothedSpeed)
|
|
if noThirdPersonLastFrame then -- just switched to third person view
|
|
camera.setPreferredThirdPersonDistance(M.preferredDistance)
|
|
camera.instantTransition()
|
|
noThirdPersonLastFrame = false
|
|
else
|
|
local maxIncrease = dt * (100 + M.baseDistance)
|
|
camera.setPreferredThirdPersonDistance(math.min(
|
|
M.preferredDistance, camera.getThirdPersonDistance() + maxIncrease))
|
|
end
|
|
end
|
|
|
|
return M
|
|
|