Files
Ananta-ps/Ananta.Server/Ananta.Handlers/Game/GameRouter.PoliceAnimationHeight.cs

125 lines
7.1 KiB
C#

using Ananta.SDK.Network;
using Ananta.SDK.Serialization;
using Ananta.Server.Protocol.Client4229938;
namespace Ananta.Server.Handlers.Game;
internal sealed partial class GameRouter
{
// Experimental visual-only correction for the recorded PoliceCar02a case.
// Never offset the entity root, seat attachment, or player model.
private static async Task PoliceAlightVisualHeight4229938(TcpSession session, ulong agent, bool restore, ulong vehicle = 0)
{
if (!session.Items.TryGetValue(PoliceGateSessionKey4229938, out var value)
|| value is not TcpSession gate || gate.ClosedToken.IsCancellationRequested) return;
var script = $$"""
local ok, err = pcall(function()
_G.__lixiAlightHeight = _G.__lixiAlightHeight or {}
local key = '{{agent}}'
local saved = _G.__lixiAlightHeight[key]
if {{(restore ? "true" : "false")}} then
_G.__lixiAlightHeight[key] = nil
if saved and saved.stop then saved.stop()
elseif saved and saved.root then saved.root.localPosition = saved.position end
return
end
if saved then return end
local npc = gCS.SceneDataMgr.GetUnit(ulong.new({{(uint)agent}}, {{agent >> 32}}))
if not npc or not npc.MeshRoot then return end
local root = npc.MeshRoot
local pos = root.localPosition
local car = LX6.Drive.DriveManager.Instance:GetBaseVehicle(ulong.new({{(uint)vehicle}}, {{vehicle >> 32}}))
if not car then return end
local transform = car.gameObject.transform
local function relativePosition()
local p = npc.Position
-- BaseUnit.Position is UXVector3 (uppercase X/Y/Z), NOT
-- Unity Vector3. The latter is returned by the transform.
return transform:InverseTransformPoint(UnityEngine.Vector3.New(p.X,p.Y,p.Z))
end
local start = relativePosition()
-- User-confirmed normal visual position is the original mesh
-- offset PLUS 0.414 m, not an inferred vehicle/root height gap.
-- Hold this full correction through the stationary door phase.
-- This calibration is ONLY enabled for 81004027 / 4065053 below.
local hook = 'lixi-alight-height-' .. key
local s = {root=root, position=UnityEngine.Vector3.New(pos.x,pos.y,pos.z),
started=UnityEngine.Time.realtimeSinceStartup, progress=0, stopped=false}
s.stop = function()
if s.stopped then return end
s.stopped = true
if s.hook == 'camera' then pcall(function() gCameraUtils.RemoveCameraUpdateEvent(hook) end) end
if s.timer then pcall(function() s.timer:Stop() end) end
pcall(function() root.localPosition = s.position end)
if _G.__lixiAlightHeight[key] == s then _G.__lixiAlightHeight[key] = nil end
end
s.tick = function()
if s.stopped then return end
local success, failure = pcall(function()
if UnityEngine.Time.realtimeSinceStartup - s.started >= 20
or gCS.SceneDataMgr.GetUnit(ulong.new({{(uint)agent}}, {{agent >> 32}})) ~= npc
or npc.MeshRoot ~= root then s.stop(); return end
local p = relativePosition()
local dx, dz = p.x-start.x, p.z-start.z
local distance = math.sqrt(dx*dx + dz*dz)
-- Door-opening time does not lower the model. Only real
-- vehicle-relative exit movement consumes compensation.
-- Never raise it again if the authored action steps back.
s.progress = math.max(s.progress, math.max(0, math.min(1, (distance-0.10)/0.90)))
local u = s.progress
local fade = 1-u*u*(3-2*u)
root.localPosition = UnityEngine.Vector3.New(s.position.x,s.position.y+0.414*fade,s.position.z)
end)
if not success then
s.stop()
print_error('[LIXI-ANIM] height-fixed0414-v2 stopped: ' .. tostring(failure))
end
end
_G.__lixiAlightHeight[key] = s
local installed = false
-- Verified in this client's FPM log: hook=camera. LateUpdateBeat
-- is absent in this runtime and must not be a prerequisite.
if gCameraUtils and gCameraUtils.AddCameraUpdateEvent and gCameraUtils.RemoveCameraUpdateEvent then
s.hook = 'camera'
installed = pcall(function() gCameraUtils.AddCameraUpdateEvent(hook,s.tick) end)
if not installed then
pcall(function() gCameraUtils.RemoveCameraUpdateEvent(hook) end)
s.hook = nil
end
end
if not installed and Timer and Timer.New then
installed = pcall(function()
s.timer = Timer.New(s.tick,0.02,-1,true)
s.timer:Start()
end)
if installed then s.hook = 'timer' end
end
if not installed then s.stop(); error('no camera/timer update hook') end
s.tick()
if not s.stopped then print_error('[LIXI-ANIM] height-fixed0414-v2 installed hook=' .. s.hook .. ' offset=0.414 agent=' .. key) end
end)
if not ok then print_error('[LIXI-ANIM] visual-height failed: ' .. tostring(err)) end
""";
await gate.NotifyAsync(MethodId.SyncDoToLuaMaster,
new UxWriter().UxString(script).ToArray(), session.ClosedToken);
}
private static async Task BeginPoliceAlightVisualHeight4229938(TcpSession session, uint child, ulong agent, ulong vehicle)
{
try
{
var vehicles = GetVehicleRuntimeState(session);
lock (vehicles.SyncRoot)
if (!vehicles.Vehicles.TryGetValue(vehicle, out var car) || car.ConfigId != 81004027) return;
// Caller already holds the police gate. Queue the visual controller
// before the native handoff; a one-second delay exposes the root snap.
if (!PoliceRuntimes.TryGetValue(session, out var r)) return;
if (r.Child != child || r.Agent != agent || !r.Alighting
|| !r.Npcs.TryGetValue(agent, out var npc) || npc.Template != 4065053) return;
await PoliceAlightVisualHeight4229938(session, agent, restore: false, vehicle: vehicle);
}
catch (OperationCanceledException) { }
catch (Exception ex) { session.Log.Warn($"[LIXI-ANIM] height correction stopped: {ex.GetType().Name}"); }
}
}