Test #1
No files matched your search
@@ -1,20 +0,0 @@
|
||||
if not _G.__GAC_FACECAM_PROBE_BATTLE then
|
||||
local ok,err=pcall(function()
|
||||
local flags=CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags),60)
|
||||
local manager=CS.RPG.Client.GlobalVars.s_GamePhaseManager
|
||||
local phase=typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase',flags):GetValue(manager)
|
||||
if not phase or not typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase) then return end
|
||||
_G.__GAC_FACECAM_PROBE_BATTLE=true
|
||||
local t=phase:GetType()
|
||||
CS.UnityEngine.Debug.Log('[FaceCam battle] phase='..tostring(t))
|
||||
for _,members in ipairs({t:GetFields(flags),t:GetProperties(flags)}) do
|
||||
for i=0,math.min(members.Length-1,199) do
|
||||
local m=members[i];local n=tostring(m.Name):lower()
|
||||
if n:find('actor') or n:find('entity') or n:find('avatar') or n:find('turn') or n:find('battle') or n:find('stage') then
|
||||
CS.UnityEngine.Debug.Log('[FaceCam battle] member='..tostring(m.Name)..' detail='..tostring(m))
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
if not ok then _G.__GAC_FACECAM_PROBE_BATTLE=true;CS.UnityEngine.Debug.Log('[FaceCam battle] error='..tostring(err)) end
|
||||
end
|
||||
+85
-11
@@ -33,6 +33,9 @@ local function rainbow_watermark(now)
|
||||
end
|
||||
|
||||
local function show_ui(msg)
|
||||
if msg:find('[FreeCam:', 1, true) or msg:find('[FaceCam:', 1, true) or msg:find('FaceCam:', 1, true) then
|
||||
_G.__FC_STATUS_UNTIL = (Time.unscaledTime or 0) + 3
|
||||
end
|
||||
pcall(function()
|
||||
local obj = GameObject.Find("VersionText")
|
||||
if obj then
|
||||
@@ -62,11 +65,52 @@ local function get_active_cam()
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Resolve the character through the active camera's own follow target. A
|
||||
-- humanoid head bone gives a model-relative point as the character moves.
|
||||
-- Do not guess a world position if the Client has no usable target.
|
||||
-- Resolve the controlled member or current battle actor through the Client's
|
||||
-- entity state, and anchor to that model's observed head bone. If the current
|
||||
-- phase does not expose either, do not guess a world position.
|
||||
local function current_facecam_target()
|
||||
local manager = CS.RPG.Client.GlobalVars.s_GamePhaseManager
|
||||
local flags = CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags), 60)
|
||||
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
|
||||
if typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase) then
|
||||
local instance = phase.BattleInstanceRef
|
||||
local 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.
|
||||
if mode then
|
||||
local attackers = mode:GetAttackingEntityList()
|
||||
if attackers and attackers.Count > 0 then entity = attackers[attackers.Count - 1] end
|
||||
end
|
||||
elseif phase:GetType().Name == 'AdventurePhase' then
|
||||
entity = phase.CurrentPlayingTeamMember
|
||||
else
|
||||
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
|
||||
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
|
||||
end
|
||||
return nil, nil, nil, true
|
||||
end
|
||||
|
||||
local function facecam_target(cam)
|
||||
local ok, head, root = pcall(function()
|
||||
local battleHead, battleRoot, id, tracked = current_facecam_target()
|
||||
if tracked then
|
||||
_G.__FC_FACE_ENTITY = id
|
||||
return battleHead, battleRoot
|
||||
end
|
||||
local brain = cam:GetComponent(typeof(CS.Cinemachine.CinemachineBrain))
|
||||
local virtual = brain and brain.ActiveVirtualCamera
|
||||
local follow = virtual and virtual.Follow
|
||||
@@ -84,15 +128,37 @@ local function facecam_target(cam)
|
||||
end
|
||||
|
||||
local function update_facecam(cam, settings)
|
||||
local now = Time.unscaledTime or 0
|
||||
if now >= (_G.__FC_FACE_NEXT or 0) then
|
||||
_G.__FC_FACE_NEXT = now + 0.15
|
||||
local ok, head, root, id, tracked = pcall(current_facecam_target)
|
||||
if ok and tracked and head and root then
|
||||
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT, _G.__FC_FACE_ENTITY = head, root, id
|
||||
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 distance = tonumber(settings.distance) or 2
|
||||
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
|
||||
distance = math.max(0.5, math.min(5, distance))
|
||||
_G.__FC_FACE_DISTANCE = distance
|
||||
local target = head.position
|
||||
local direction = root.forward
|
||||
local position = target + direction * distance
|
||||
cam.transform.position = position
|
||||
cam.transform.rotation = CS.UnityEngine.Quaternion.LookRotation(target - position, Vector3.up)
|
||||
local anchor = _G.__FC_FACE_ANCHOR
|
||||
if not anchor or anchor.transform.parent ~= head then
|
||||
if anchor then CS.UnityEngine.Object.Destroy(anchor) end
|
||||
anchor = GameObject('GaC_FaceCamAnchor')
|
||||
anchor.transform:SetParent(head, false)
|
||||
_G.__FC_FACE_ANCHOR = anchor
|
||||
-- Capture the character's forward direction in the head's local
|
||||
-- space: this respects the model's bone axes and follows head turns.
|
||||
_G.__FC_FACE_LOCAL_FORWARD = CS.UnityEngine.Quaternion.Inverse(head.rotation) * root.forward
|
||||
end
|
||||
local direction = head.rotation * _G.__FC_FACE_LOCAL_FORWARD
|
||||
anchor.transform.position = target + direction * distance
|
||||
anchor.transform.rotation = CS.UnityEngine.Quaternion.LookRotation(target - anchor.transform.position, Vector3.up)
|
||||
cam.transform.position = anchor.transform.position
|
||||
cam.transform.rotation = anchor.transform.rotation
|
||||
_G.__FC_POS = cam.transform.position
|
||||
_G.__FC_EULER = cam.transform.eulerAngles
|
||||
return true
|
||||
@@ -153,7 +219,8 @@ local function update_mouse_gate()
|
||||
local manager = CS.RPG.Client.GlobalVars.s_GamePhaseManager
|
||||
local phase = manager and gate.phaseField:GetValue(manager)
|
||||
local inBattle = phase and typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase)
|
||||
local wanted = _G.__FREECAM_ENABLED and inBattle
|
||||
local cameraSettings = _G.__GAC_CAMERA_SETTINGS
|
||||
local wanted = _G.__FREECAM_ENABLED and inBattle and (not cameraSettings or cameraSettings.mode ~= 'facecam')
|
||||
-- Releasing Freecam while holding RMB must not become a battle click.
|
||||
if not wanted then
|
||||
if gate.active and not Input.GetMouseButton(0) and not Input.GetMouseButton(1) then
|
||||
@@ -233,6 +300,7 @@ function _G.__FREECAM_TICK()
|
||||
if _G.__FREECAM_ENABLED then
|
||||
local settings = _G.__GAC_CAMERA_SETTINGS or {mode="freecam", distance=2}
|
||||
if settings.mode == "facecam" then
|
||||
_G.__FC_FACE_DISTANCE = tonumber(settings.distance) or 2
|
||||
local head, root
|
||||
if cam then head, root = facecam_target(cam) end
|
||||
if not head or not root then
|
||||
@@ -258,12 +326,15 @@ function _G.__FREECAM_TICK()
|
||||
set_cinemachine_enabled(cam, true)
|
||||
end
|
||||
_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
|
||||
if _G.__FC_FACE_ANCHOR then CS.UnityEngine.Object.Destroy(_G.__FC_FACE_ANCHOR); _G.__FC_FACE_ANCHOR = nil end
|
||||
show_ui(rainbow_watermark(Time.unscaledTime or 0))
|
||||
end
|
||||
end
|
||||
|
||||
if not _G.__FREECAM_ENABLED then
|
||||
local now = Time.unscaledTime or 0
|
||||
local now = Time.unscaledTime or 0
|
||||
if not _G.__FREECAM_ENABLED or now >= (_G.__FC_STATUS_UNTIL or 0) then
|
||||
if now >= (_G.__FC_WATERMARK_NEXT or 0) then
|
||||
_G.__FC_WATERMARK_NEXT = now + 0.05
|
||||
show_ui(rainbow_watermark(now))
|
||||
@@ -285,6 +356,9 @@ function _G.__FREECAM_TICK()
|
||||
set_cinemachine_enabled(cam, true)
|
||||
_G.__FREECAM_ENABLED = false
|
||||
_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
|
||||
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>")
|
||||
end
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user