diff --git a/scripts/freecam.lua b/scripts/freecam.lua
index 5bb5cd4..15c8ba4 100644
--- a/scripts/freecam.lua
+++ b/scripts/freecam.lua
@@ -74,10 +74,10 @@ local function current_facecam_target()
local field = typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase', flags)
local phase = manager and field and field:GetValue(manager)
if not phase then return nil, nil, nil, false end
- local entity
+ local entity, mode
if typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase) then
local instance = phase.BattleInstanceRef
- local mode = instance and instance.TurnBasedGameModeRef
+ mode = instance and instance.TurnBasedGameModeRef
entity = mode and mode.CurrentTurnOwnerEntity
-- Inserted attacks can act outside the turn owner's turn. Prefer the
-- most recently attacking entity while the Client reports one.
@@ -91,15 +91,35 @@ local function current_facecam_target()
return nil, nil, nil, false
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
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)
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
return nil, nil, nil, true
end
@@ -137,7 +157,14 @@ local function update_facecam(cam, settings)
end
end
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 scroll = Input.mouseScrollDelta.y
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_ENTITY = 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
local label = settings.mode == 'facecam' and 'FaceCam' or 'FreeCam'
show_ui('[' .. label .. ': OFF]')
@@ -362,6 +390,7 @@ function _G.__FREECAM_TICK()
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = nil, nil
_G.__FC_FACE_ENTITY = 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
show_ui("FaceCam: character unavailable")
end