diag(motion): #39 — per-tick [CURRNODE] for sequencer node identity

Visual-verify of fix #2 (commit 863d96b) showed [SCFULL] correctly reports
currNodeIsCyclic=True after each direct Walk↔Run SetCycle (the link is
removed and _currNode is set to _firstCyclic). User report still:

- Animation continues running visually after Shift toggle to Walk
- Body slows ("speed decreases"), causing rubber-banding
- Adding a turn motion in that state makes the cycle finally transition
  to walking

So either:
- _currNode is reset to a stale node BETWEEN SetCycle and Advance
- _currNode is correctly on the new cycle but its AnimRef is wrong
  (e.g., the same Animation as the previous cycle, dat-side issue)
- BuildBlendedFrame reads from somewhere other than _currNode

Adds CurrentNodeDiag + FirstCyclicAnimRefHash properties on
AnimationSequencer that expose the active node's Animation
identity-hash, IsLooping, Framerate, frame range, and FramePosition.
TickAnimations logs them on every SEQSTATE tick (1 Hz throttle, gated
on ACDREAM_REMOTE_VEL_DIAG=1).

The [CURRNODE] line with animRef vs firstCyclicAnimRef proves whether
_currNode is actually on the new cycle's anim or has drifted to
something else. Compared across SetCycle SCFULL log lines + the
following CURRNODE ticks, we can see the exact moment the cycle
diverges from what SetCycle set.

No code-behavior changes. Pure read-only instrumentation. Per
Phase 4.5 of systematic-debugging — STOP attempting fixes; gather
evidence first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-06 08:17:56 +02:00
parent 863d96bb23
commit bb026b7991
2 changed files with 50 additions and 0 deletions

View file

@ -254,6 +254,39 @@ public sealed class AnimationSequencer
public int QueueCount => _queue.Count;
public bool HasCurrentNode => _currNode != null;
/// <summary>
/// Diagnostic snapshot of <c>_currNode</c>'s identity + frame state, for
/// the per-tick CURRNODE log line in <c>GameWindow.TickAnimations</c>.
/// Lets the caller see whether the actual node being read by Advance /
/// BuildBlendedFrame is what SetCycle was supposed to leave it on.
/// AnimRefHash uses object-identity hashing on the Animation reference
/// so different Walk vs Run anim resources can be distinguished even
/// without exposing the full Animation type.
/// </summary>
public (int AnimRefHash, bool IsLooping, double Framerate, int StartFrame, int EndFrame, double FramePosition, int QueueCount) CurrentNodeDiag
{
get
{
if (_currNode is null)
return (0, false, 0.0, 0, 0, 0.0, _queue.Count);
var n = _currNode.Value;
int hash = System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(n.Anim);
return (hash, n.IsLooping, n.Framerate, n.StartFrame, n.EndFrame, _framePosition, _queue.Count);
}
}
/// <summary>
/// Diagnostic: the AnimRefHash for the FIRST cyclic node in the queue
/// (i.e., what SetCycle is trying to land us on for a locomotion cycle).
/// Compare against <see cref="CurrentNodeDiag"/>'s AnimRefHash to see
/// whether <c>_currNode</c> is actually pointing at the new cycle or
/// something stale.
/// </summary>
public int FirstCyclicAnimRefHash =>
_firstCyclic is null
? 0
: System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(_firstCyclic.Value.Anim);
// ── Private state ────────────────────────────────────────────────────────
private readonly Setup _setup;