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>
/// True while a server MoveToObject/MoveToPosition packet is the
/// active locomotion source. Retail runs these through MoveToManager
/// and CMotionInterp using the packet's runRate; deriving velocity
/// from sparse UpdatePosition deltas under-speeds combat chases.
/// and CMotionInterp using the packet's runRate; until we port the
/// full target solver, use this only to protect packet-derived
/// animation speed from velocity-cycle clobbering.
/// </summary>
public bool ServerMoveToActive;
/// <summary>
@ -2452,16 +2453,6 @@ public sealed class GameWindow : IDisposable
if (_remoteDeadReckon.TryGetValue(update.Guid, out var remoteMot))
{
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).
remoteMot.Motion.DoInterpretedMotion(
@ -2685,6 +2676,11 @@ public sealed class GameWindow : IDisposable
if (IsPlayerGuid(serverGuid)) return;
if (rm.Airborne) 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
.PlanFromVelocity(velocity);
@ -2771,7 +2767,6 @@ public sealed class GameWindow : IDisposable
System.Numerics.Vector3? serverVelocity = update.Velocity;
if (serverVelocity is null
&& !IsPlayerGuid(update.Guid)
&& !rmState.ServerMoveToActive
&& rmState.LastServerPosTime > 0.0)
{
double elapsed = nowSec - rmState.LastServerPosTime;
@ -5047,6 +5042,15 @@ public sealed class GameWindow : IDisposable
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
{
rm.Motion.apply_current_movement(cancelMoveTo: false, allowJump: false);