fix(motion): full queue reset on locomotion-cycle direct transitions

When AnimationSequencer.SetCycle transitions between forward-locomotion
cycles (Walk↔Run, Walk↔WalkBackward, etc.) — i.e. when both old and new
motion's low byte is in {0x05 WalkForward, 0x06 WalkBackward, 0x07
RunForward} — do a full queue drain + _currNode/_firstCyclic reset
(matching the existing skipTransitionLink branch) instead of just
ClearCyclicTail. Without this, _currNode is left pointing into the
previous cycle's non-cyclic head (link frames from the prior Ready→walk
transition), and the visible legs continue playing those head frames
before reaching the new run cycle.

Investigation findings (cdb live trace of retail at
tools/cdb-scripts/walk_run_motion_trace.log):

  Retail's actual approach is "additive add_to_queue with no truncate" —
  MotionTableManager handles the natural progression via per-tick
  CheckForCompletedMotions / remove_redundant_links cleanup. Acdream
  doesn't have that machinery, so this fix is the closest viable
  emulation: force the queue back to a clean state and rebuild from
  scratch on the locomotion-cycle transition.

User-reported symptom this addresses (walk→run direct transition,
release shift while W held): visible animation cycle did not switch
until next motion event. Verified via FWD_WIRE + SETCYCLE diags that
both ACE and our SetCycle are firing correctly on the transition.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-03 17:00:55 +02:00
parent b1d8e122ed
commit c06b6c51e1
3 changed files with 408 additions and 2 deletions

View file

@ -434,7 +434,29 @@ public sealed class AnimationSequencer
// cycle. Without this, the old RunForward → ??? link would
// continue draining for ~100 ms before the new Falling cycle
// starts, defeating the "skip the link" intent.
if (skipTransitionLink)
//
// 2026-05-03: ALSO do a full drain when transitioning between
// FORWARD-LOCOMOTION cycles (Walk↔Run, Walk↔WalkBackward, etc.)
// — i.e. when both old and new motion's low byte is in the
// {0x05 WalkForward, 0x06 WalkBackward, 0x07 RunForward} set.
// ClearCyclicTail alone leaves _currNode in the previous cycle's
// non-cyclic head (link frames from a Ready→walk transition),
// and the visible legs continue playing those head frames before
// reaching the new run cycle. The user-reported symptom: walk→run
// direct transition (release shift while W held) did not visibly
// switch the leg cycle — body advanced at walk pace until the
// next motion event (turn / stop) re-fired SetCycle and finally
// aligned the queue. Live cdb trace of retail acclient.exe
// 2026-05-03 (tools/cdb-scripts/walk_run_motion_trace.log) shows
// retail uses an additive add_to_queue with no truncate — the
// MotionTableManager's per-tick CheckForCompletedMotions handles
// the natural progression. We don't have that machinery, so we
// emulate via a hard reset on the locomotion-cycle transition.
uint oldLow = CurrentMotion & 0xFFu;
uint newLow = motion & 0xFFu;
bool oldIsForwardLoc = oldLow == 0x05u || oldLow == 0x06u || oldLow == 0x07u;
bool newIsForwardLoc = newLow == 0x05u || newLow == 0x06u || newLow == 0x07u;
if (skipTransitionLink || (oldIsForwardLoc && newIsForwardLoc))
{
_queue.Clear();
_currNode = null;