fix(anim): Phase L.1c sequencer cycle fallback for missing MoveTo motion
User-observed regression on commit37de771: monsters in combat with another client appear as "just a torso on the ground" until they move. User correctly identified this as a regression I introduced. Cause traced to the SEQUENCER side, not the InterpretedState side. AnimationSequencer.SetCycle (AnimationSequencer.cs:392-396) unconditionally calls ClearCyclicTail() BEFORE looking up the requested cycle in the MotionTable. If the cycle is missing (_mtable.Cycles.TryGetValue returns false), the body is left without ANY cyclic tail at all — and every part snaps to its setup-default offset on the next Advance(). Most creatures' setup-defaults put all limbs at the torso origin, so the visual collapses to "just a torso on the ground" until a different (working) cycle arrives. This is specifically a regression from commit186a584(Phase L.1c port). Pre-fix, MoveTo packets fell through to fullMotion=Ready (every MotionTable contains a Ready cycle). Post-fix, MoveTo packets seed fullMotion=RunForward via PlanMoveToStart. Some combat-stance creatures (e.g. monsters in HandCombat 0x003C) have no (combat, RunForward) cycle in their MotionTable — they're meant to walk in combat, with retail's apply_run_to_command upgrading WalkForward → RunForward at the velocity layer rather than the animation-cycle layer. Fix: add `AnimationSequencer.HasCycle(style, motion)` query and gate the SetCycle call site in GameWindow.OnLiveMotionUpdated behind it. Fall back chain: requested motion → WalkForward → Ready → no-op-don't-clear. The InterpretedState.ForwardCommand bulk-copy (commit37de771) is unchanged — body still gets RunForward velocity even when the visible animation falls back to WalkForward or Ready. Tests: 1420 → 1422. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
37de771778
commit
34d7f4def2
3 changed files with 114 additions and 1 deletions
|
|
@ -2578,7 +2578,53 @@ public sealed class GameWindow : IDisposable
|
|||
// whatever the interpreted state says when the body
|
||||
// lands.
|
||||
if (!remoteIsAirborne)
|
||||
ae.Sequencer.SetCycle(fullStyle, animCycle, animSpeed);
|
||||
{
|
||||
// Fallback chain for missing cycles in the MotionTable.
|
||||
// SetCycle unconditionally calls ClearCyclicTail() before
|
||||
// looking up the cycle; if the cycle is absent, the body
|
||||
// ends up with no cyclic tail at all and every part snaps
|
||||
// to its setup-default offset — visible as "torso on the
|
||||
// ground" because most creatures' setup-default puts all
|
||||
// limbs at the torso origin.
|
||||
//
|
||||
// This is specifically a regression from commit 186a584
|
||||
// (Phase L.1c port): pre-fix, MoveTo packets fell through
|
||||
// to fullMotion=Ready (which always exists in every
|
||||
// MotionTable). Post-fix, MoveTo packets seed
|
||||
// fullMotion=RunForward, but some creatures (especially
|
||||
// when stance=HandCombat) lack a (combat, RunForward)
|
||||
// cycle. Fall through RunForward → WalkForward → Ready
|
||||
// until we find one the table actually contains.
|
||||
//
|
||||
// Note: this fallback is for the SEQUENCER (visible
|
||||
// animation) only. InterpretedState.ForwardCommand still
|
||||
// gets the wire's (or seeded) ForwardCommand verbatim
|
||||
// so apply_current_movement produces correct velocity.
|
||||
uint cycleToPlay = animCycle;
|
||||
if (!ae.Sequencer.HasCycle(fullStyle, cycleToPlay))
|
||||
{
|
||||
// RunForward (0x44000007) → WalkForward (0x45000005)
|
||||
if ((cycleToPlay & 0xFFu) == 0x07
|
||||
&& ae.Sequencer.HasCycle(fullStyle, 0x45000005u))
|
||||
{
|
||||
cycleToPlay = 0x45000005u;
|
||||
}
|
||||
// WalkForward → Ready (0x41000003)
|
||||
else if (ae.Sequencer.HasCycle(fullStyle, 0x41000003u))
|
||||
{
|
||||
cycleToPlay = 0x41000003u;
|
||||
}
|
||||
// Ready missing too — leave the existing cycle alone
|
||||
// by not calling SetCycle at all (avoids the
|
||||
// ClearCyclicTail wipe).
|
||||
else
|
||||
{
|
||||
cycleToPlay = 0;
|
||||
}
|
||||
}
|
||||
if (cycleToPlay != 0)
|
||||
ae.Sequencer.SetCycle(fullStyle, cycleToPlay, animSpeed);
|
||||
}
|
||||
|
||||
// Retail runs the full MotionInterp state machine on every
|
||||
// remote. Route each wire command (forward, sidestep, turn)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue