fix(anim): preserve Falling cycle while remote is airborne + reset on land

Two issues from the K-fix16 jump-now-renders launch:

1. Mid-air movement broke the jump animation.
   When a remote turned or ran while airborne, ACE broadcast
   UpdateMotion with the new motion state. OnLiveMotionUpdated's
   SetCycle call swapped Falling -> RunForward / TurnRight /
   etc., breaking the visible jump pose. The arc still played
   out (physics integrated body position correctly) but the
   legs ran instead of folded.

   Fix: skip the SetCycle in OnLiveMotionUpdated when
   rm.Airborne is true. The InterpretedState DoMotion calls
   below it still fire, so the body's velocity matches the
   new motion command and the body keeps moving correctly --
   only the visible cycle stays Falling.

2. Stuck in Falling pose after landing.
   K-fix15 cleared rm.Airborne + restored ground state on
   landing, but never told the sequencer to swap cycles. The
   remote stayed in the Falling pose forever (legs folded)
   until the server happened to send a fresh UpdateMotion
   (e.g. when the player walked again). Idle landing left
   them frozen.

   Fix: post-land, read InterpretedState.ForwardCommand and
   call SetCycle with that command + the recorded
   ForwardSpeed. Default to Ready / 1.0 when the state is
   blank. The next UpdateMotion from the server will refine
   if needed (e.g. mid-strafe land), but the legs come out
   of Falling immediately.

Drive-by: stripped K-fix16's unconditional [VU.recv] log
now that the parser is verified working.

Tests stay 1222 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-26 22:08:55 +02:00
parent 0ebf0cad09
commit ff504e9ec1

View file

@ -2180,7 +2180,22 @@ public sealed class GameWindow : IDisposable
animSpeed = MathF.Abs(update.MotionState.TurnSpeed ?? 1f);
}
}
ae.Sequencer.SetCycle(fullStyle, animCycle, animSpeed);
// K-fix17 (2026-04-26): preserve the Falling cycle while
// the remote is airborne. ACE broadcasts UpdateMotion
// mid-arc when the player turns / runs — the previous
// SetCycle here swapped Falling → RunForward, breaking
// the visible jump animation. The arc still played out
// physics-wise (body went up/down), but the legs ran
// instead of folded. Skip the cycle swap when airborne;
// the InterpretedState updates below still fire so the
// body's velocity matches the new motion command, AND
// the post-resolve landing path restores the cycle to
// whatever the interpreted state says when the body
// lands.
bool remoteIsAirborne = _remoteDeadReckon.TryGetValue(update.Guid, out var rmCheck)
&& rmCheck.Airborne;
if (!remoteIsAirborne)
ae.Sequencer.SetCycle(fullStyle, animCycle, animSpeed);
// Retail runs the full MotionInterp state machine on every
// remote. Route each wire command (forward, sidestep, turn)
@ -4755,6 +4770,35 @@ public sealed class GameWindow : IDisposable
rm.Body.Velocity = new System.Numerics.Vector3(
rm.Body.Velocity.X, rm.Body.Velocity.Y, 0f);
rm.Motion.HitGround();
// K-fix17 (2026-04-26): reset the sequencer cycle
// from Falling back to whatever the interpreted
// motion state says they should be doing now.
// Without this, the remote stays in the Falling
// pose forever (legs folded) until the next
// server-sent UpdateMotion arrives. Use the
// sequencer's current style (preserved across
// jump) and pick the cycle from
// InterpretedState.ForwardCommand: Ready
// (idle), WalkForward, RunForward, WalkBackward.
// SideStep / Turn aren't strict locomotion
// priorities — the next UM the server sends will
// refine the cycle if the player is mid-strafe
// when they land; this just gets the legs out
// of Falling immediately.
if (ae.Sequencer is not null)
{
uint style = ae.Sequencer.CurrentStyle != 0
? ae.Sequencer.CurrentStyle
: 0x8000003Du;
uint landingCmd = rm.Motion.InterpretedState.ForwardCommand;
if (landingCmd == 0)
landingCmd = AcDream.Core.Physics.MotionCommand.Ready;
float landingSpeed = rm.Motion.InterpretedState.ForwardSpeed;
if (landingSpeed <= 0f) landingSpeed = 1f;
ae.Sequencer.SetCycle(style, landingCmd, landingSpeed);
}
if (Environment.GetEnvironmentVariable("ACDREAM_DUMP_MOTION") == "1")
Console.WriteLine($"VU.land guid=0x{serverGuid:X8} Z={rm.Body.Position.Z:F2}");
}