Follow transformed battle characters in FaceCam

This commit is contained in:
CAtlas0784 committed 2026-10-29 00:47:38 +07:00
1 parent 3fbb70a30c
commit 448ea61a68
1 file changed
+37 -8
+37 -8
View File
@@ -74,10 +74,10 @@ local function current_facecam_target()
local field = typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase', flags) local field = typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase', flags)
local phase = manager and field and field:GetValue(manager) local phase = manager and field and field:GetValue(manager)
if not phase then return nil, nil, nil, false end if not phase then return nil, nil, nil, false end
local entity local entity, mode
if typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase) then if typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase) then
local instance = phase.BattleInstanceRef local instance = phase.BattleInstanceRef
local mode = instance and instance.TurnBasedGameModeRef mode = instance and instance.TurnBasedGameModeRef
entity = mode and mode.CurrentTurnOwnerEntity entity = mode and mode.CurrentTurnOwnerEntity
-- Inserted attacks can act outside the turn owner's turn. Prefer the -- Inserted attacks can act outside the turn owner's turn. Prefer the
-- most recently attacking entity while the Client reports one. -- most recently attacking entity while the Client reports one.
@@ -91,15 +91,35 @@ local function current_facecam_target()
return nil, nil, nil, false return nil, nil, nil, false
end end
if not entity then return nil, nil, nil, true end if not entity then return nil, nil, nil, true end
if _G.__FC_FACE_ENTITY == entity.RuntimeID and _G.__FC_FACE_HEAD and _G.__FC_FACE_ROOT then local function model_for(candidate)
local ok, result = pcall(function()
local component = candidate and candidate:GetComponent(typeof(CS.RPG.GameCore.CharacterModelComponent))
return component and component:GetModelGO()
end)
if ok then return result end
return nil
end
local model = model_for(entity)
-- In a transformed turn the Client can report a model-less turn owner;
-- the live battle's MVP entity points to the visible transformed actor.
if not model and mode then
local fallback = mode.CurrentMVPEntity
local fallbackModel = model_for(fallback)
if fallbackModel then entity, model = fallback, fallbackModel end
end
if not model then return nil, nil, nil, true end
local cachedAlive, cachedActive = pcall(function()
return _G.__FC_FACE_HEAD and _G.__FC_FACE_ROOT
and _G.__FC_FACE_HEAD.gameObject.activeInHierarchy and _G.__FC_FACE_ROOT.gameObject.activeInHierarchy
end)
if _G.__FC_FACE_ENTITY == entity.RuntimeID and cachedAlive and cachedActive then
return _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT, entity.RuntimeID, true return _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT, entity.RuntimeID, true
end end
local component = entity:GetComponent(typeof(CS.RPG.GameCore.CharacterModelComponent))
local model = component and component:GetModelGO()
if not model then return nil, nil, nil, true end
local nodes = model:GetComponentsInChildren(typeof(CS.UnityEngine.Transform), true) local nodes = model:GetComponentsInChildren(typeof(CS.UnityEngine.Transform), true)
for i = 0, nodes.Length - 1 do for i = 0, nodes.Length - 1 do
if nodes[i].name == 'Head_M' then return nodes[i], model.transform, entity.RuntimeID, true end if nodes[i].name == 'Head_M' and nodes[i].gameObject.activeInHierarchy then
return nodes[i], model.transform, entity.RuntimeID, true
end
end end
return nil, nil, nil, true return nil, nil, nil, true
end end
@@ -137,7 +157,14 @@ local function update_facecam(cam, settings)
end end
end end
local head, root = _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT local head, root = _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT
if not head or not root or not head.gameObject.activeInHierarchy then return false end local alive, active = pcall(function() return head and root and head.gameObject.activeInHierarchy end)
if not alive or not active then
-- Character transformations can briefly unload a model while keeping
-- the same entity ID. Give the replacement time to become available.
_G.__FC_FACE_MISSING_SINCE = _G.__FC_FACE_MISSING_SINCE or now
return now - _G.__FC_FACE_MISSING_SINCE < 2
end
_G.__FC_FACE_MISSING_SINCE = nil
local distance = _G.__FC_FACE_DISTANCE or tonumber(settings.distance) or 2 local distance = _G.__FC_FACE_DISTANCE or tonumber(settings.distance) or 2
local scroll = Input.mouseScrollDelta.y local scroll = Input.mouseScrollDelta.y
if scroll ~= 0 then distance = distance - scroll * 0.2 end if scroll ~= 0 then distance = distance - scroll * 0.2 end
@@ -331,6 +358,7 @@ function _G.__FREECAM_TICK()
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = nil, nil _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = nil, nil
_G.__FC_FACE_ENTITY = nil _G.__FC_FACE_ENTITY = nil
_G.__FC_FACE_DISTANCE, _G.__FC_FACE_LOCAL_FORWARD = nil, nil _G.__FC_FACE_DISTANCE, _G.__FC_FACE_LOCAL_FORWARD = nil, nil
_G.__FC_FACE_MISSING_SINCE = nil
if _G.__FC_FACE_ANCHOR then CS.UnityEngine.Object.Destroy(_G.__FC_FACE_ANCHOR); _G.__FC_FACE_ANCHOR = nil end if _G.__FC_FACE_ANCHOR then CS.UnityEngine.Object.Destroy(_G.__FC_FACE_ANCHOR); _G.__FC_FACE_ANCHOR = nil end
local label = settings.mode == 'facecam' and 'FaceCam' or 'FreeCam' local label = settings.mode == 'facecam' and 'FaceCam' or 'FreeCam'
show_ui('<color=#00FF88><b>[' .. label .. ': OFF]</b></color>') show_ui('<color=#00FF88><b>[' .. label .. ': OFF]</b></color>')
@@ -362,6 +390,7 @@ function _G.__FREECAM_TICK()
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = nil, nil _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = nil, nil
_G.__FC_FACE_ENTITY = nil _G.__FC_FACE_ENTITY = nil
_G.__FC_FACE_DISTANCE, _G.__FC_FACE_LOCAL_FORWARD = nil, nil _G.__FC_FACE_DISTANCE, _G.__FC_FACE_LOCAL_FORWARD = nil, nil
_G.__FC_FACE_MISSING_SINCE = nil
if _G.__FC_FACE_ANCHOR then CS.UnityEngine.Object.Destroy(_G.__FC_FACE_ANCHOR); _G.__FC_FACE_ANCHOR = nil end if _G.__FC_FACE_ANCHOR then CS.UnityEngine.Object.Destroy(_G.__FC_FACE_ANCHOR); _G.__FC_FACE_ANCHOR = nil end
show_ui("<color=#FFAA44>FaceCam: character unavailable</color>") show_ui("<color=#FFAA44>FaceCam: character unavailable</color>")
end end