feat(R2-Q5): RemoteMotionSink DELETED — funnel dispatches straight into PerformMovement; AP-73 retired
The funnel's retail-ordered dispatches now go directly into the entity's motion-table stack via Motion/MotionTableDispatchSink (Core): ApplyMotion → PerformMovement(InterpretedCommand), StopMotion → PerformMovement(StopInterpretedCommand). No axis collection, no single-cycle priority pick, no Commit pass, no HasCycle probe, no Run→Walk→Ready fallback chain — GetObjectSequence 0x00522860 + is_allowed decide (closes H4, H5-callers, H11-callers, H15, H17-carry). Run-while-turning now blends for real: the turn is a Branch-4 modifier combined over the untouched run substate (AP-73 DELETED from the register). AnimationSequencer gains the public PerformMovement passthrough (lazy initialize_state + locomotion velocity synthesis on success — AP-75; remote body translation via PositionManager.ComputeOffset depends on CurrentVelocity) and InitializeState(); HasCycle DELETED (the miss hazard is structurally gone — GetObjectSequence checks the cycle before any surgery; new conformance test pins sequence+state untouched on a miss). GameWindow: sink swap with the TurnApplied/TurnStopped ObservedOmega callbacks (H17 carried verbatim — register AP-76, retire R6); spawn + door sites run retail's enter-world order (initialize_state installs the table default, the wire's initial motion dispatches unguarded — the L.1c fallback chains deleted); despawn drains the pending queue via HandleExitWorld (MotionDone success:false per entry, 0x0051bda0). 5 new MotionTableDispatchSink conformance tests (lazy init, Branch-4 turn blend + callback, Case-B stop unwind, Case-A stop re-drive, miss no-op). Full suite green: 3,462 passed. Live smoke vs ACE: in-world, NPC emote/Ready UMs dispatching through the new path, [MOTIONDONE] completions firing across entities — first live proof of the Q3→Q4→Q5 chain. Zero exceptions. Registers: AP-73 deleted; AP-76 added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c072b73686
commit
d82f07d4e5
8 changed files with 399 additions and 395 deletions
|
|
@ -224,32 +224,16 @@ public sealed class AnimationSequencerTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void HasCycle_PresentInTable_ReturnsTrue()
|
||||
public void SetCycle_MissingCycle_LeavesSequenceAndStateUntouched()
|
||||
{
|
||||
// Phase L.1c followup (2026-04-28): regression guard for
|
||||
// "torso on the ground" — caller (GameWindow MoveTo path) needs
|
||||
// to query the table before SetCycle to avoid the
|
||||
// ClearCyclicTail wipe on a missing cycle.
|
||||
const uint Style = 0x003Cu; // HandCombat
|
||||
const uint Motion = 0x0003u; // Ready
|
||||
const uint AnimId = 0x03000001u;
|
||||
|
||||
var setup = Fixtures.MakeSetup(2);
|
||||
var mt = Fixtures.MakeMtable(Style, Motion, AnimId);
|
||||
var loader = new FakeLoader();
|
||||
loader.Register(AnimId, Fixtures.MakeTwoFrameAnim(2, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity));
|
||||
var seq = new AnimationSequencer(setup, mt, loader);
|
||||
|
||||
// Caller passes the SAME shape SetCycle expects: full style with
|
||||
// class byte (0x80000000) and full motion (0x40000000 / 0x10000000).
|
||||
Assert.True(seq.HasCycle(0x8000003Cu, 0x41000003u));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasCycle_MissingFromTable_ReturnsFalse()
|
||||
{
|
||||
const uint Style = 0x003Cu;
|
||||
const uint ReadyMotion = 0x0003u;
|
||||
// R2-Q5: HasCycle + the caller-side fallback chains are DELETED.
|
||||
// The retail mechanism replacing the L.1c "torso on the ground"
|
||||
// guard: GetObjectSequence (0x00522860) checks the cycle BEFORE any
|
||||
// list surgery — a missing cycle leaves the sequence AND MotionState
|
||||
// untouched, so whatever was playing (here the initialize_state
|
||||
// default) keeps playing.
|
||||
const uint Style = 0x8000003Cu; // HandCombat (full command)
|
||||
const uint ReadyMotion = 0x41000003u;
|
||||
const uint AnimId = 0x03000001u;
|
||||
|
||||
var setup = Fixtures.MakeSetup(2);
|
||||
|
|
@ -258,9 +242,17 @@ public sealed class AnimationSequencerTests
|
|||
loader.Register(AnimId, Fixtures.MakeTwoFrameAnim(2, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity));
|
||||
var seq = new AnimationSequencer(setup, mt, loader);
|
||||
|
||||
// RunForward (0x44000007) is NOT in the table — caller should
|
||||
// see false and fall back to a known motion (WalkForward / Ready).
|
||||
Assert.False(seq.HasCycle(0x8000003Cu, 0x44000007u));
|
||||
seq.InitializeState();
|
||||
Assert.Equal(ReadyMotion, seq.CurrentMotion);
|
||||
int nodesBefore = seq.QueueCount;
|
||||
|
||||
// RunForward (0x44000007) is NOT in the table — the dispatch fails
|
||||
// and nothing changes (no cyclic-tail wipe, no state overwrite).
|
||||
seq.SetCycle(Style, 0x44000007u);
|
||||
|
||||
Assert.Equal(ReadyMotion, seq.CurrentMotion);
|
||||
Assert.Equal(nodesBefore, seq.QueueCount);
|
||||
Assert.True(seq.HasCurrentNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue