fix(anim): Phase L.1c anchor monster MoveTo prediction

Keep the retail MoveTo speed/runRate parsing from 9812965 for animation playback, but do not use the partial MoveTo state as a body-position solver. Until the full retail MoveToManager target path is ported, retain UpdatePosition-derived velocity for server-controlled creature position and prevent that velocity from clobbering the packet-derived animation cycle speed.
This commit is contained in:
Erik 2026-04-28 21:12:03 +02:00
parent 9812965183
commit 882a07cfde

View file

@ -226,8 +226,9 @@ public sealed class GameWindow : IDisposable
/// <summary> /// <summary>
/// True while a server MoveToObject/MoveToPosition packet is the /// True while a server MoveToObject/MoveToPosition packet is the
/// active locomotion source. Retail runs these through MoveToManager /// active locomotion source. Retail runs these through MoveToManager
/// and CMotionInterp using the packet's runRate; deriving velocity /// and CMotionInterp using the packet's runRate; until we port the
/// from sparse UpdatePosition deltas under-speeds combat chases. /// full target solver, use this only to protect packet-derived
/// animation speed from velocity-cycle clobbering.
/// </summary> /// </summary>
public bool ServerMoveToActive; public bool ServerMoveToActive;
/// <summary> /// <summary>
@ -2452,16 +2453,6 @@ public sealed class GameWindow : IDisposable
if (_remoteDeadReckon.TryGetValue(update.Guid, out var remoteMot)) if (_remoteDeadReckon.TryGetValue(update.Guid, out var remoteMot))
{ {
remoteMot.ServerMoveToActive = update.MotionState.IsServerControlledMoveTo; remoteMot.ServerMoveToActive = update.MotionState.IsServerControlledMoveTo;
if (remoteMot.ServerMoveToActive && !IsPlayerGuid(update.Guid))
{
// Retail MoveTo packets already carry enough state
// for CMotionInterp to drive velocity. A velocity
// inferred from sparse UpdatePosition packets lags
// during combat chases and visibly under-speeds the
// run cycle until the next hard snap.
remoteMot.HasServerVelocity = false;
remoteMot.ServerVelocity = System.Numerics.Vector3.Zero;
}
// Forward axis (Ready / WalkForward / RunForward / WalkBackward). // Forward axis (Ready / WalkForward / RunForward / WalkBackward).
remoteMot.Motion.DoInterpretedMotion( remoteMot.Motion.DoInterpretedMotion(
@ -2685,6 +2676,11 @@ public sealed class GameWindow : IDisposable
if (IsPlayerGuid(serverGuid)) return; if (IsPlayerGuid(serverGuid)) return;
if (rm.Airborne) return; if (rm.Airborne) return;
if (ae.Sequencer is null) return; if (ae.Sequencer is null) return;
// MoveTo packets already seeded the retail speed/runRate cycle.
// Keep UpdatePosition-derived velocity for render position only;
// using it to choose the cycle reverts fast chases back to slow
// velocity-estimated animation.
if (rm.ServerMoveToActive) return;
var plan = AcDream.Core.Physics.ServerControlledLocomotion var plan = AcDream.Core.Physics.ServerControlledLocomotion
.PlanFromVelocity(velocity); .PlanFromVelocity(velocity);
@ -2771,7 +2767,6 @@ public sealed class GameWindow : IDisposable
System.Numerics.Vector3? serverVelocity = update.Velocity; System.Numerics.Vector3? serverVelocity = update.Velocity;
if (serverVelocity is null if (serverVelocity is null
&& !IsPlayerGuid(update.Guid) && !IsPlayerGuid(update.Guid)
&& !rmState.ServerMoveToActive
&& rmState.LastServerPosTime > 0.0) && rmState.LastServerPosTime > 0.0)
{ {
double elapsed = nowSec - rmState.LastServerPosTime; double elapsed = nowSec - rmState.LastServerPosTime;
@ -5047,6 +5042,15 @@ public sealed class GameWindow : IDisposable
rm.Body.Velocity = rm.ServerVelocity; rm.Body.Velocity = rm.ServerVelocity;
} }
} }
else if (!IsPlayerGuid(serverGuid) && rm.ServerMoveToActive)
{
// We only parse enough of MoveTo to recover retail
// animation speed. Do not let apply_current_movement
// extrapolate position from an incomplete target
// solver; hold until the next UpdatePosition-derived
// velocity arrives.
rm.Body.Velocity = System.Numerics.Vector3.Zero;
}
else else
{ {
rm.Motion.apply_current_movement(cancelMoveTo: false, allowJump: false); rm.Motion.apply_current_movement(cancelMoveTo: false, allowJump: false);