Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions CombatMode/Core/Runtime/CVarManager.lua
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,16 @@ end

--- Motion Sickness Protection: do not drive ActionCam feature CVars / leave MS alone.
local function RespectMotionSickness()
-- Delegated: SteadyCam's own Motion Sickness Protection decides (it also gates
-- Focus Locked Target here).
if CM.SteadyCam then
local api = _G.SteadyCamAPI
return api ~= nil and api.RespectsMotionSickness ~= nil and api.RespectsMotionSickness() == true
end
local g = CM.DB and CM.DB.global
return g and g.respectMotionSickness == true
end
CM.MotionSicknessProtectionOn = RespectMotionSickness

local function ShoulderFadeDuration()
return (CM.Constants and CM.Constants.MouseLookCameraFadeDuration) or 0.35
Expand Down Expand Up @@ -396,6 +403,10 @@ end

--- Desired shoulder, or nil to stop writing (DynamicCam owns the CVar).
local function DesiredShoulderOffset()
-- SteadyCam frames the character on foot and on every mount: never write.
if CM.SteadyCam then
return nil
end
-- Protection clears CM-owned shoulder; with DynamicCam, stop writing entirely.
if RespectMotionSickness() then
if CM.DynamicCam then
Expand Down Expand Up @@ -563,7 +574,7 @@ function CM.SetShoulderOffset()
local desired = DesiredShoulderOffset()
EnsureShoulderAtDesired(desired)
if desired == nil then
CM.DebugPrint("Shoulder Offset relinquished to DynamicCam")
CM.DebugPrint("Shoulder Offset relinquished to " .. tostring(CM.CameraOwner))
else
CM.DebugPrint("Shoulder Offset target " .. tostring(desired))
end
Expand Down Expand Up @@ -603,7 +614,7 @@ end
--- Do not key this off freelook alone: flipping MS with Mouse Look lock/unlock snaps
--- skyriding FOV/zoom when Dynamic Pitch is off (pitch-on keeps MS sticky at 0).
local function ShoulderNeedsActionCam()
if CM.DynamicCam then
if CM.CameraOwner then
return false
end
if ShoulderFollowsMouseLook() then
Expand Down Expand Up @@ -654,15 +665,15 @@ end
--- Restore MS CVars from prior snapshot (or Blizzard-on defaults). Used when enabling
--- respectMotionSickness or when ActionCam no longer needs MS off.
function CM.RestoreMotionSicknessFromSnapshot()
if CM.DynamicCam then
if CM.CameraOwner then
return
end
local keep, reduce = MotionSicknessReleaseValues()
WriteMotionSicknessCVars(keep, reduce)
end

function CM.ApplyActionCamMotionSicknessGate()
if CM.DynamicCam then
if CM.CameraOwner then
return
end
if RespectMotionSickness() then
Expand All @@ -686,7 +697,7 @@ end
--- Sticky Dynamic Pitch (option-gated, not freelook). Pads + master CVar + MS gate
--- so unlock / option toggles actually take effect. Forced off under Motion Sickness Protection.
function CM.SetDynamicPitch()
if CM.DynamicCam then
if CM.CameraOwner then
return
end
local wantPitch = CM.DB
Expand All @@ -703,7 +714,7 @@ end

--- Apply Mouse Look camera chrome while freelook is locked (MS off when we own it, shoulder).
function CM.ApplyMouseLookCamera()
if not CM.DynamicCam then
if not CM.CameraOwner then
CM.ApplyActionCamMotionSicknessGate()
end
CM.SetShoulderOffset()
Expand All @@ -713,7 +724,7 @@ end
--- Clear Mouse Look camera chrome while freelook is unlocked (tween shoulder to 0; MS gate).
function CM.ClearMouseLookCamera()
CM.SetShoulderOffset()
if not CM.DynamicCam then
if not CM.CameraOwner then
CM.ApplyActionCamMotionSicknessGate()
end
CM.DebugPrint("Mouse Look camera cleared")
Expand Down Expand Up @@ -742,7 +753,7 @@ function CM.SyncTargetFocusFromFocusUnit()
CM.SetCVar("test_cameraTargetFocusEnemyEnable", want and 1 or 0)
if want then
WriteMotionSicknessCVars(0, 0)
elseif not CM.DynamicCam then
elseif not CM.CameraOwner then
CM.ApplyActionCamMotionSicknessGate()
end
end
Expand All @@ -758,7 +769,7 @@ function CM.ApplyMotionSicknessProtectionState()
if CM.SyncTargetFocusFromFocusUnit then
CM.SyncTargetFocusFromFocusUnit()
end
if not CM.DynamicCam then
if not CM.CameraOwner then
CM.ApplyActionCamMotionSicknessGate()
end
end
Expand All @@ -778,7 +789,7 @@ function CM.ConfigStickyCrosshair(CVarType)
end

function CM.SetMouseLookSpeed()
if CM.DynamicCam then
if CM.CameraOwner then
return
end

Expand Down
12 changes: 11 additions & 1 deletion CombatMode/Core/Runtime/Runtime.lua
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,17 @@ end
local function IsDCLoaded()
local loaded = IsAddOnLoaded("DynamicCam")
CM.DynamicCam = loaded and true or false
if CM.DynamicCam and not CM.DB.global.silenceAlerts then
-- SteadyCam owns turn speed, shoulder offset, dynamic pitch and the motion-sickness
-- gates (Target Focus stays here). It takes precedence over DynamicCam: SteadyCam
-- keeps running and offers the player to disable DynamicCam itself.
CM.SteadyCam = IsAddOnLoaded("SteadyCam") and true or false
CM.CameraOwner = (CM.SteadyCam and "SteadyCam") or (CM.DynamicCam and "DynamicCam") or nil
if CM.SteadyCam and not CM.DB.global.silenceAlerts then
print(
CM.Constants.BasePrintMsg
.. "|cff909090: |cffE52B50SteadyCam detected!|r Handing over turn speed, shoulder, and dynamic pitch.|r"
)
elseif CM.DynamicCam and not CM.DB.global.silenceAlerts then
print(
CM.Constants.BasePrintMsg
.. "|cff909090: |cffE52B50DynamicCam detected!|r Handing over turn speed, shoulder, and dynamic pitch.|r"
Expand Down
59 changes: 52 additions & 7 deletions CombatMode/UI/Options/Tabs/TabGeneral.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,43 @@ local INTERACT_UNIT_VALUES = {
}
local INTERACT_UNIT_ORDER = { "mouseover", "target" }

-- Shown over camera controls handed to SteadyCam, in the game's text language.
local DELEGATED_TO_STEADYCAM = {
enUS = "Delegated to SteadyCam",
esES = "Delegado a SteadyCam",
esMX = "Delegado a SteadyCam",
deDE = "An SteadyCam übergeben",
frFR = "Délégué à SteadyCam",
itIT = "Delegato a SteadyCam",
ptBR = "Delegado ao SteadyCam",
ruRU = "Передано SteadyCam",
koKR = "SteadyCam에 위임됨",
zhCN = "已交由 SteadyCam 控制",
zhTW = "已交由 SteadyCam 控制",
}

--- Watermark for controls another camera addon owns (nil when Combat Mode owns them).
local function CameraOwnerWatermark()
if CM.SteadyCam then
return DELEGATED_TO_STEADYCAM[_G.GetLocale()] or DELEGATED_TO_STEADYCAM.enUS
end
if CM.CameraOwner then
return "Control relinquished to " .. CM.CameraOwner
end
return nil
end

local function RespectMotionSicknessOn()
if CM.MotionSicknessProtectionOn then
return CM.MotionSicknessProtectionOn() == true -- SteadyCam's when delegated
end
return CM.DB.global.respectMotionSickness == true
end

--- Watermark when camera ActionCam controls are locked by DynamicCam or Respect MS.
local function CameraFeatureWatermark()
if CM.DynamicCam then
return "Control relinquished to DynamicCam"
if CM.CameraOwner then
return CameraOwnerWatermark()
end
if RespectMotionSicknessOn() then
return "Disabled while Motion Sickness Protection is on"
Expand All @@ -65,7 +94,7 @@ local function CameraFeatureWatermark()
end

local function CameraFeatureDisabled()
return CM.DynamicCam or RespectMotionSicknessOn()
return CM.CameraOwner ~= nil or RespectMotionSicknessOn()
end

--- Primary + alternate interact binding commands from CM.DB.global.interactUnit.
Expand Down Expand Up @@ -155,7 +184,7 @@ UI.Options.AddTab({
max = 180,
step = 10,
default = 100,
watermarkWhenDisabled = "Control relinquished to DynamicCam",
watermarkWhenDisabled = CameraOwnerWatermark,
get = function()
return CM.DB.global.mouseLookSpeed
end,
Expand All @@ -164,7 +193,7 @@ UI.Options.AddTab({
CM.SetMouseLookSpeed()
end,
disabled = function()
return CM.DynamicCam
return CM.CameraOwner ~= nil
end,
})
ctx:Toggle({
Expand Down Expand Up @@ -221,6 +250,16 @@ UI.Options.AddTab({
ctx:Toggle({
label = "Motion Sickness Protection",
desc = "Prevents Combat Mode from overriding Blizzard's Motion Sickness settings.",
-- Delegated with the rest of the camera: SteadyCam has the same option.
watermarkWhenDisabled = function()
if CM.SteadyCam then
return CameraOwnerWatermark()
end
return nil
end,
disabled = function()
return CM.SteadyCam
end,
get = function()
return RespectMotionSicknessOn()
end,
Expand Down Expand Up @@ -293,8 +332,12 @@ UI.Options.AddTab({
label = "Disable Offset With Mouselook",
desc = "Shoulder Offset eases to 0 when disabling Mouse Look."
.. "\nWhen off, Shoulder Offset stays constant at all times.",
-- Still editable with DynamicCam (unlock→0 / restore path); only MS Protection locks it.
-- Still editable with DynamicCam (unlock→0 / restore path); SteadyCam and MS
-- Protection lock it.
watermarkWhenDisabled = function()
if CM.SteadyCam then
return CameraOwnerWatermark()
end
if RespectMotionSicknessOn() then
return "Disabled while Motion Sickness Protection is on"
end
Expand All @@ -309,7 +352,9 @@ UI.Options.AddTab({
CM.SetShoulderOffset()
end
end,
disabled = RespectMotionSicknessOn,
disabled = function()
return CM.SteadyCam or RespectMotionSicknessOn()
end,
})

ctx:Gap()
Expand Down
5 changes: 3 additions & 2 deletions CombatMode/UI/Options/Tabs/TabReticleTargeting.lua
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ UI.Options.AddTab({
label = "Focus Locked Target",
desc = "Pulls the camera toward your locked target, keeping it in focus.",
watermarkWhenDisabled = function()
if CM.DB.global.respectMotionSickness == true then
if CM.MotionSicknessProtectionOn and CM.MotionSicknessProtectionOn() == true then
return "Disabled while Motion Sickness Protection is on"
end
return nil
Expand All @@ -233,7 +233,8 @@ UI.Options.AddTab({
end
end,
disabled = function()
return not CM.DB.char.reticleTargeting or CM.DB.global.respectMotionSickness == true
return not CM.DB.char.reticleTargeting
or (CM.MotionSicknessProtectionOn and CM.MotionSicknessProtectionOn() == true)
end,
})
end,
Expand Down
Loading