mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-15 10:09:42 +00:00
Control camera settings in-game
This commit is contained in:
parent
f47e64b0f8
commit
860d5899c4
9 changed files with 241 additions and 41 deletions
|
@ -66,6 +66,6 @@ $DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw/*lua
|
|||
cd $FILES_DIR/data
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw_aux/*lua
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/ai.lua
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/camera.lua
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/camera/camera.lua
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/mwui/init.lua
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/settings/player.lua
|
||||
|
|
|
@ -2,5 +2,5 @@ Interface Camera
|
|||
================
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/scripts_omw_camera.html
|
||||
:file: generated_html/scripts_omw_camera_camera.html
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ set(BUILTIN_DATA_FILES
|
|||
|
||||
l10n/Calendar/en.yaml
|
||||
l10n/Interface/en.yaml
|
||||
l10n/OMWCamera/en.yaml
|
||||
|
||||
openmw_aux/util.lua
|
||||
openmw_aux/time.lua
|
||||
|
@ -21,9 +22,10 @@ set(BUILTIN_DATA_FILES
|
|||
builtin.omwscripts
|
||||
|
||||
scripts/omw/ai.lua
|
||||
scripts/omw/camera.lua
|
||||
scripts/omw/head_bobbing.lua
|
||||
scripts/omw/third_person.lua
|
||||
scripts/omw/camera/camera.lua
|
||||
scripts/omw/camera/head_bobbing.lua
|
||||
scripts/omw/camera/third_person.lua
|
||||
scripts/omw/camera/settings.lua
|
||||
scripts/omw/console/player.lua
|
||||
scripts/omw/console/global.lua
|
||||
scripts/omw/console/local.lua
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
PLAYER: scripts/omw/camera.lua
|
||||
PLAYER: scripts/omw/mwui/init.lua
|
||||
GLOBAL: scripts/omw/settings/global.lua
|
||||
PLAYER: scripts/omw/settings/player.lua
|
||||
PLAYER: scripts/omw/camera/camera.lua
|
||||
NPC,CREATURE: scripts/omw/ai.lua
|
||||
PLAYER: scripts/omw/console/player.lua
|
||||
GLOBAL: scripts/omw/console/global.lua
|
||||
CUSTOM: scripts/omw/console/local.lua
|
||||
PLAYER: scripts/omw/mwui/init.lua
|
||||
GLOBAL: scripts/omw/settings/global.lua
|
||||
PLAYER: scripts/omw/settings/player.lua
|
||||
|
|
59
files/data/l10n/OMWCamera/en.yaml
Normal file
59
files/data/l10n/OMWCamera/en.yaml
Normal file
|
@ -0,0 +1,59 @@
|
|||
Camera: "Camera"
|
||||
settingsPageDescription: "OpenMW camera settings"
|
||||
|
||||
thirdPersonSettings: "Third person mode"
|
||||
|
||||
viewOverShoulder: "View over the shoulder"
|
||||
viewOverShoulderDescription: |
|
||||
Controls third person view mode.
|
||||
No: view is centered on the character's head. Crosshair is hidden.
|
||||
Yes: in non-combat mode camera is positioned behind the character's shoulder, crosshair is always visible.
|
||||
|
||||
shoulderOffsetX: "Shoulder view horizontal offset"
|
||||
shoulderOffsetXDescription: >
|
||||
Horizontal offset of the camera in the view-over-the-shoulder mode.
|
||||
For the left shoulder use a negative value.
|
||||
|
||||
shoulderOffsetY: "Shoulder view vertical offset"
|
||||
shoulderOffsetYDescription: >
|
||||
Vertical offset of the camera in the view-over-the-shoulder mode.
|
||||
|
||||
autoSwitchShoulder: "Auto switch shoulder"
|
||||
autoSwitchShoulderDescription: >
|
||||
When player is close to an obstacle, automatically switches camera
|
||||
to the shoulder that is farther away from the obstacle.
|
||||
|
||||
zoomOutWhenMoveCoef: "Zoom out when move coef"
|
||||
zoomOutWhenMoveCoefDescription: >
|
||||
Slightly pulls camera away (or closer in case of a negative value) when the character moves.
|
||||
Works only if "view over the shoulder" is enabled. To disable set it to zero (default: 20.0).
|
||||
|
||||
previewIfStandStill: "Preview if stand still"
|
||||
previewIfStandStillDescription: >
|
||||
If enabled then the character rotation is not synchonized with the camera rotation
|
||||
while the character doesn't move and not in combat mode.
|
||||
|
||||
deferredPreviewRotation: "Deferred preview rotation"
|
||||
deferredPreviewRotationDescription: |
|
||||
If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode.
|
||||
If disabled then the camera rotates rather than the character.
|
||||
|
||||
ignoreNC: "Ignore 'No Collision' flag"
|
||||
ignoreNCDescription: >
|
||||
Prevents camera from clipping through the objects with the NC (No Collision) NIF flag.
|
||||
|
||||
|
||||
headBobbingSettings: "Head bobbing in first person view"
|
||||
|
||||
headBobbing_enabled: "Enabled"
|
||||
headBobbing_enabledDescription: ""
|
||||
|
||||
headBobbing_step: "Base step length"
|
||||
headBobbing_stepDescription: "The length of each step (default: 90.0)."
|
||||
|
||||
headBobbing_height: "Step height"
|
||||
headBobbing_heightDescription: "The amplitude of the head bobbing (default: 3.0)."
|
||||
|
||||
headBobbing_roll: "Max roll angle"
|
||||
headBobbing_rollDescription: "The maximum roll angle in degrees (default: 0.2)."
|
||||
|
|
@ -1,20 +1,33 @@
|
|||
local camera = require('openmw.camera')
|
||||
local core = require('openmw.core')
|
||||
local input = require('openmw.input')
|
||||
local settings = require('openmw.settings')
|
||||
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 head_bobbing = require('scripts.omw.head_bobbing')
|
||||
local third_person = require('scripts.omw.third_person')
|
||||
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 MODE = camera.MODE
|
||||
|
||||
local previewIfStandSill = settings._getBoolFromSettingsCfg('Camera', 'preview if stand still')
|
||||
local showCrosshairInThirdPerson = settings._getBoolFromSettingsCfg('Camera', 'view over shoulder')
|
||||
local previewIfStandStill = false
|
||||
local showCrosshairInThirdPerson = 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)
|
||||
end
|
||||
|
||||
local primaryMode
|
||||
|
||||
|
@ -24,17 +37,18 @@ local noHeadBobbing = 0
|
|||
local noZoom = 0
|
||||
|
||||
local function init()
|
||||
camera.setCollisionType(util.bitOr(util.bitAnd(nearby.COLLISION_TYPE.Default, util.bitNot(nearby.COLLISION_TYPE.Actor)), nearby.COLLISION_TYPE.Camera))
|
||||
camera.setFieldOfView(camera.getBaseFieldOfView())
|
||||
camera.allowCharacterDeferredRotation(settings._getBoolFromSettingsCfg('Camera', 'deferred preview rotation'))
|
||||
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
|
||||
|
||||
|
@ -60,7 +74,7 @@ local function updatePOV(dt)
|
|||
end
|
||||
|
||||
local idleTimer = 0
|
||||
local vanityDelay = settings.getGMST('fVanityDelay')
|
||||
local vanityDelay = core.getGMST('fVanityDelay')
|
||||
|
||||
local function updateVanity(dt)
|
||||
if input.isIdle() then
|
||||
|
@ -126,7 +140,7 @@ end
|
|||
|
||||
local function updateStandingPreview()
|
||||
local mode = camera.getMode()
|
||||
if not previewIfStandSill or noStandingPreview > 0
|
||||
if not previewIfStandStill or noStandingPreview > 0
|
||||
or mode == MODE.FirstPerson or mode == MODE.Static or mode == MODE.Vanity then
|
||||
third_person.standingPreview = false
|
||||
return
|
||||
|
@ -204,7 +218,7 @@ return {
|
|||
enableModeControl = function() noModeControl = math.max(0, noModeControl - 1) end,
|
||||
|
||||
--- @function [parent=#Camera] isStandingPreviewEnabled
|
||||
isStandingPreviewEnabled = function() return previewIfStandSill and noStandingPreview == 0 end,
|
||||
isStandingPreviewEnabled = function() return previewIfStandStill and noStandingPreview == 0 end,
|
||||
--- @function [parent=#Camera] disableStandingPreview
|
||||
disableStandingPreview = function() noStandingPreview = noStandingPreview + 1 end,
|
||||
--- @function [parent=#Camera] enableStandingPreview
|
|
@ -1,21 +1,29 @@
|
|||
local camera = require('openmw.camera')
|
||||
local self = require('openmw.self')
|
||||
local settings = require('openmw.settings')
|
||||
local util = require('openmw.util')
|
||||
local async = require('openmw.async')
|
||||
|
||||
local Actor = require('openmw.types').Actor
|
||||
|
||||
local doubleStepLength = settings._getFloatFromSettingsCfg('Camera', 'head bobbing step') * 2
|
||||
local stepHeight = settings._getFloatFromSettingsCfg('Camera', 'head bobbing height')
|
||||
local maxRoll = math.rad(settings._getFloatFromSettingsCfg('Camera', 'head bobbing roll'))
|
||||
local M = {}
|
||||
|
||||
local settings = require('scripts.omw.camera.settings').headBobbing
|
||||
|
||||
local doubleStepLength, stepHeight, maxRoll
|
||||
|
||||
local function updateSettings()
|
||||
M.enabled = settings:get('enabled')
|
||||
doubleStepLength = settings:get('step') * 2
|
||||
stepHeight = settings:get('height')
|
||||
maxRoll = math.rad(settings:get('roll'))
|
||||
end
|
||||
|
||||
updateSettings()
|
||||
settings:subscribe(async:callback(updateSettings))
|
||||
|
||||
local effectWeight = 0
|
||||
local totalMovement = 0
|
||||
|
||||
local M = {
|
||||
enabled = settings._getBoolFromSettingsCfg('Camera', 'head bobbing')
|
||||
}
|
||||
|
||||
-- Trajectory of each step is a scaled arc of 60 degrees.
|
||||
local halfArc = math.rad(30)
|
||||
local sampleArc = function(x) return 1 - math.cos(x * halfArc) end
|
95
files/data/scripts/omw/camera/settings.lua
Normal file
95
files/data/scripts/omw/camera/settings.lua
Normal file
|
@ -0,0 +1,95 @@
|
|||
local storage = require('openmw.storage')
|
||||
local async = require('openmw.async')
|
||||
local I = require('openmw.interfaces')
|
||||
|
||||
I.Settings.registerPage({
|
||||
key = 'OMWCamera',
|
||||
l10n = 'OMWCamera',
|
||||
name = 'Camera',
|
||||
description = 'settingsPageDescription',
|
||||
})
|
||||
|
||||
local thirdPersonGroup = 'SettingsOMWCameraThirdPerson'
|
||||
local headBobbingGroup = 'SettingsOMWCameraHeadBobbing'
|
||||
|
||||
local function boolSetting(prefix, key, default)
|
||||
return {
|
||||
key = key,
|
||||
renderer = 'checkbox',
|
||||
name = prefix..key,
|
||||
description = prefix..key..'Description',
|
||||
default = default,
|
||||
}
|
||||
end
|
||||
|
||||
local function floatSetting(prefix, key, default)
|
||||
return {
|
||||
key = key,
|
||||
renderer = 'number',
|
||||
name = prefix..key,
|
||||
description = prefix..key..'Description',
|
||||
default = default,
|
||||
}
|
||||
end
|
||||
|
||||
I.Settings.registerGroup({
|
||||
key = thirdPersonGroup,
|
||||
page = 'OMWCamera',
|
||||
l10n = 'OMWCamera',
|
||||
name = 'thirdPersonSettings',
|
||||
permanentStorage = true,
|
||||
order = 0,
|
||||
settings = {
|
||||
boolSetting('', 'viewOverShoulder', true),
|
||||
floatSetting('', 'shoulderOffsetX', 30),
|
||||
floatSetting('', 'shoulderOffsetY', -10),
|
||||
boolSetting('', 'autoSwitchShoulder', true),
|
||||
floatSetting('', 'zoomOutWhenMoveCoef', 20),
|
||||
boolSetting('', 'previewIfStandStill', true),
|
||||
boolSetting('', 'deferredPreviewRotation', true),
|
||||
boolSetting('', 'ignoreNC', true),
|
||||
},
|
||||
})
|
||||
|
||||
I.Settings.registerGroup({
|
||||
key = headBobbingGroup,
|
||||
page = 'OMWCamera',
|
||||
l10n = 'OMWCamera',
|
||||
name = 'headBobbingSettings',
|
||||
permanentStorage = true,
|
||||
order = 1,
|
||||
settings = {
|
||||
boolSetting('headBobbing_', 'enabled', true),
|
||||
floatSetting('headBobbing_', 'step', 90),
|
||||
floatSetting('headBobbing_', 'height', 3),
|
||||
floatSetting('headBobbing_', 'roll', 0.2),
|
||||
},
|
||||
})
|
||||
|
||||
local settings = {
|
||||
thirdPerson = storage.playerSection(thirdPersonGroup),
|
||||
headBobbing = storage.playerSection(headBobbingGroup),
|
||||
}
|
||||
|
||||
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})
|
||||
end
|
||||
|
||||
local function updateHeadBobbingDisabled()
|
||||
local disabled = not settings.headBobbing:get('enabled')
|
||||
I.Settings.updateRendererArgument(headBobbingGroup, 'step', {disabled = disabled, min = 1})
|
||||
I.Settings.updateRendererArgument(headBobbingGroup, 'height', {disabled = disabled})
|
||||
I.Settings.updateRendererArgument(headBobbingGroup, 'roll', {disabled = disabled, min = 0, max = 90})
|
||||
end
|
||||
|
||||
updateViewOverShoulderDisabled()
|
||||
updateHeadBobbingDisabled()
|
||||
|
||||
settings.thirdPerson:subscribe(async:callback(updateViewOverShoulderDisabled))
|
||||
settings.headBobbing:subscribe(async:callback(updateHeadBobbingDisabled))
|
||||
|
||||
return settings
|
|
@ -1,31 +1,47 @@
|
|||
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 async = require('openmw.async')
|
||||
|
||||
local Actor = require('openmw.types').Actor
|
||||
|
||||
local settings = require('scripts.omw.camera.settings').thirdPerson
|
||||
|
||||
local MODE = camera.MODE
|
||||
local STATE = { RightShoulder = 0, LeftShoulder = 1, Combat = 2, Swimming = 3 }
|
||||
|
||||
local M = {
|
||||
baseDistance = settings._getFloatFromSettingsCfg('Camera', 'third person camera distance'),
|
||||
baseDistance = 192,
|
||||
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 viewOverShoulder, autoSwitchShoulder
|
||||
local shoulderOffset
|
||||
local zoomOutWhenMoveCoef
|
||||
|
||||
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 defaultShoulder, rightShoulderOffset, leftShoulderOffset
|
||||
local combatOffset = util.vector2(0, 15)
|
||||
|
||||
local noThirdPersonLastFrame = true
|
||||
|
||||
local function updateSettings()
|
||||
viewOverShoulder = settings:get('viewOverShoulder')
|
||||
autoSwitchShoulder = settings:get('autoSwitchShoulder')
|
||||
shoulderOffset = util.vector2(settings:get('shoulderOffsetX'),
|
||||
settings:get('shoulderOffsetY'))
|
||||
zoomOutWhenMoveCoef = settings:get('zoomOutWhenMoveCoef')
|
||||
|
||||
defaultShoulder = (shoulderOffset.x > 0 and STATE.RightShoulder) or STATE.LeftShoulder
|
||||
rightShoulderOffset = util.vector2(math.abs(shoulderOffset.x), shoulderOffset.y)
|
||||
leftShoulderOffset = util.vector2(-math.abs(shoulderOffset.x), shoulderOffset.y)
|
||||
noThirdPersonLastFrame = true
|
||||
end
|
||||
updateSettings()
|
||||
settings:subscribe(async:callback(updateSettings))
|
||||
|
||||
local state = defaultShoulder
|
||||
|
||||
local function ray(from, angle, limit)
|
||||
|
@ -66,8 +82,6 @@ local function calculateDistance(smoothedSpeed)
|
|||
+ smoothedSpeedSqr / (smoothedSpeedSqr + 300*300) * zoomOutWhenMoveCoef)
|
||||
end
|
||||
|
||||
local noThirdPersonLastFrame = true
|
||||
|
||||
local function updateState()
|
||||
local mode = camera.getMode()
|
||||
local oldState = state
|
||||
|
@ -80,9 +94,13 @@ local function updateState()
|
|||
elseif not state then
|
||||
state = defaultShoulder
|
||||
end
|
||||
if autoSwitchShoulder and (mode == MODE.ThirdPerson or state ~= oldState or noThirdPersonLastFrame)
|
||||
if (mode == MODE.ThirdPerson or Actor.currentSpeed(self) > 0 or state ~= oldState or noThirdPersonLastFrame)
|
||||
and (state == STATE.LeftShoulder or state == STATE.RightShoulder) then
|
||||
trySwitchShoulder()
|
||||
if autoSwitchShoulder then
|
||||
trySwitchShoulder()
|
||||
else
|
||||
state = defaultShoulder
|
||||
end
|
||||
end
|
||||
if oldState ~= state or noThirdPersonLastFrame then
|
||||
-- State was changed, start focal point transition.
|
||||
|
@ -116,7 +134,11 @@ function M.update(dt, smoothedSpeed)
|
|||
if not viewOverShoulder then
|
||||
M.preferredDistance = M.baseDistance
|
||||
camera.setPreferredThirdPersonDistance(M.baseDistance)
|
||||
noThirdPersonLastFrame = false
|
||||
if noThirdPersonLastFrame then
|
||||
camera.setFocalPreferredOffset(util.vector2(0, 0))
|
||||
camera.instantTransition()
|
||||
noThirdPersonLastFrame = false
|
||||
end
|
||||
return
|
||||
end
|
||||
|
Loading…
Reference in a new issue