125 lines
7.1 KiB
C#
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}"); }
|
|
}
|
|
}
|