fix(motion): restore retail object manager order

Process animation completion at the retail process_hooks boundary, then run targeting, movement, PartArray completion, and PositionManager in the named UpdateObjectInternal order for local, remote, hidden, and position-less animated objects. Retire TS-42 with deterministic conformance coverage.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-19 21:40:14 +02:00
parent 0f996db747
commit c5ab99081c
17 changed files with 493 additions and 98 deletions

View file

@ -45,9 +45,22 @@ public sealed class AnimationHookFrameQueue
{
ArgumentNullException.ThrowIfNull(sequencer);
ArgumentNullException.ThrowIfNull(hooks);
// Retail CPhysicsObj::process_hooks (0x00511550) executes AnimDone
// inside UpdatePositionInternal, before the transition and every
// UpdateObjectInternal manager. Complete the semantic motion state at
// capture time so that this object's later Target/Movement/PartArray
// tail sees the new queue in the same quantum. The hook remains in the
// deferred entry so presentation sinks still observe the complete
// authored hook stream after the final part poses are published.
for (int i = 0; i < hooks.Count; i++)
{
if (hooks[i] is AnimationDoneHook)
sequencer.Manager.AnimationDone(success: true);
}
_entries.Add(new Entry(
ownerLocalId,
sequencer,
hooks));
}
@ -69,13 +82,7 @@ public sealed class AnimationHookFrameQueue
continue;
if (hasRootPose)
_router.OnHook(entry.OwnerLocalId, worldPosition, hook);
if (hook is AnimationDoneHook)
entry.Sequencer.Manager.AnimationDone(success: true);
}
// Retail sweeps zero-tick motion entries even on frames without a
// hook. It remains paired with every sequencer advanced this frame.
entry.Sequencer.Manager.UseTime();
}
_entries.Clear();
}
@ -84,6 +91,5 @@ public sealed class AnimationHookFrameQueue
private readonly record struct Entry(
uint OwnerLocalId,
AnimationSequencer Sequencer,
IReadOnlyList<AnimationHook> Hooks);
}