feat(R2-Q3): verbatim MotionTableManager — the pending-animation queue (closes H3, H15-core)
Ports retail's MotionTableManager (0x0051bxxx; r2-motiontable-decomp.md §11) above the Q2 CMotionTable: add_to_queue 0x0051bfe0 (append + immediate collapse), remove_redundant_links 0x0051bf20 (TAIL-ANCHORED single scan, NOT ACE's restructured loop — cycle-tail block mask 0xb0000000 = STYLE|MODIFIER|ACTION, style-tail 0x70000000 = CYCLE|MODIFIER|ACTION; the decomp's prose gloss of those masks was imprecise, the literal constants are ported), truncate_animation_list 0x0051bca0 (zero NumAnims IN PLACE from the tail through the matched node's successor + CSequence.RemoveLinkAnimations), AnimationDone 0x0051bce0 (counter-driven countdown chain: one call can pop MULTIPLE entries; action-class pops MotionState's action FIFO; drained-list counter reset), CheckForCompletedMotions 0x0051be00 / UseTime (zero-tick sweep, success hardcoded true), initialize_state 0x0051c030 (0x41000003 sentinel), HandleEnterWorld/HandleExitWorld drains (MotionDone success:false; enter also strips link anims), PerformMovement 0x0051c0b0 (error codes 7 / 0x43 / 0; StopCompletely queues the Ready sentinel UNCONDITIONALLY). IMotionDoneSink is the R2 seam standing in for CPhysicsObj::MotionDone — R3 binds MotionInterpreter.MotionDone (r2-port-plan.md §4 contract). This queue + remove_redundant_links IS the retail mechanism our old 'Fix B' rapid-motion collapse approximated — Q4 deletes Fix B and routes SetCycle/PlayAction through PerformMovement. 47 conformance tests: countdown-chain tables, truncate blocked/allowed matrices for both masks, zero-tick vs counter sweep, world drains, the walk-run-walk-run collapse trace, the 2026-05-03 walk-to-run golden (both nodes queued, truncate not firing), PerformMovement error matrix. Register: AD-34 extended (pending_animations managed LinkedList); AD-35 added (NotHandled sentinel vs retail's dead-code pointer-leak default case). Implemented by a dedicated agent against the committed Q3 spec; diff scope, mask semantics, truncation range, counter reset, and PerformMovement paths independently verified against the decomp raw text before commit. Build + full suite green (3,447 passed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8eff397801
commit
aa65990a1d
3 changed files with 1528 additions and 1 deletions
|
|
@ -97,7 +97,8 @@ accepted-divergence entries (#96, #49, #50).
|
|||
| AD-31 | Teleport transit covered by a full-screen black FADE (`FadeOverlay` + `TeleportAnimSequencer`) instead of retail's 3D portal-tunnel swirl (2026-06-22, spec C). Opaque black holds through the tunnel states; the world ramps back in on `WorldFadeIn`. | `src/AcDream.App/Rendering/FadeOverlay.cs` + `GameWindow.cs` (TAS transit tick; `_teleportFadeAlpha = ShowTunnel ? 1 : FadeAlpha`) | The fade is a functional cover that hides the (now-fast) destination load + the post-materialization object flood; the TAS state machine + golden timing constants are retail-verbatim — only the tunnel *graphic* is approximated. Sibling to AP-49 (fade-curve). | Visual-only: the transit shows a black cover, not the animated swirl. Retire by porting the `gmSmartBoxUI` 3D tunnel render. | `gmSmartBoxUI::UseTime` 0x004d6e30 (tunnel render, unported); `TELEPORT_ANIM_*` golden constants (spec §2.1) |
|
||||
| AD-32 | Movement-event staleness gate ADOPTS a newer-incarnation instance stamp and applies the event immediately; retail queues the blob for the not-yet-created object (`SmartBox::QueueBlobForObject`, dispatch return 4) and replays it once that incarnation exists (L.2g S1, 2026-07-02) | `src/AcDream.Core/Physics/MotionSequenceGate.cs:105` | acdream has no per-object blob queue; a newer instance seq means the new incarnation's CreateObject is in flight, and that CreateObject re-seeds the same gate (advance-only), so old-incarnation stragglers still drop | A motion event for a new incarnation applies to the OLD body for up to one CreateObject round-trip — brief wrong-cycle flicker on respawn/re-instance if the new incarnation's motion table differs | 0xF74C dispatch pc:357214-357239 (`is_newer(update_times[8], seq)` + `QueueBlobForObject`) |
|
||||
| AD-33 | `CSequence.frame_number` at C# `double` (64-bit); retail is x87 `long double` (80-bit extended) — every frame-boundary comparison ran at extended precision on retail (Phase R1, 2026-07-02) | `src/AcDream.Core/Physics/Motion/CSequence.cs` (`FrameNumber`) | `double` is the widest C# float type; the R1 port removes ACE-style boundary epsilons so comparisons are exact-int against bare boundaries, minimizing ULP sensitivity (ACE's `float` is far worse) | A frame landing within 1 double-ULP of an integer boundary could classify differently than retail's 80-bit compare — sub-frame timing skew at pathological framerate×dt combinations | `acclient.h:30747` (`long double frame_number`) |
|
||||
| AD-34 | Retail's intrusive `DLListBase`/`DLListData` anim-node list is a managed `LinkedList<AnimSequenceNode>`; node identity via `LinkedListNode<>` references (Phase R1, 2026-07-02) | `src/AcDream.Core/Physics/Motion/CSequence.cs` (`_animList`) | Same topology + cursor semantics (curr_anim/first_cyclic are node references); unlink/delete becomes `Remove(node)`; conformance tests pin the surgery state tables | Any retail behavior depending on the −4 pointer adjustment or node memory reuse (none observed in the decomp) would diverge | `acclient.h` DLListBase; `r1-csequence-decomp.md` §0 |
|
||||
| AD-34 | Retail's intrusive `DLListBase`/`DLListData` lists are managed `LinkedList<T>`s; node identity via `LinkedListNode<>` references (Phase R1 anim list, 2026-07-02; extended R2-Q3 to `MotionTableManager.pending_animations`) | `src/AcDream.Core/Physics/Motion/CSequence.cs` (`_animList`); `src/AcDream.Core/Physics/Motion/MotionTableManager.cs` (`_pendingAnimations`) | Same topology + cursor semantics (curr_anim/first_cyclic/tail-anchored scans are node references); unlink/delete becomes `Remove(node)`; conformance tests pin the surgery state tables | Any retail behavior depending on the −4 pointer adjustment or node memory reuse (none observed in the decomp) would diverge | `acclient.h` DLListBase; `r1-csequence-decomp.md` §0; `r2-motiontable-decomp.md` §11 |
|
||||
| AD-35 | `MotionTableManager.PerformMovement`'s unhandled-type default case returns the named sentinel `0xFFFFFFFF` (`MotionTableManagerError.NotHandled`); retail's compiled code leaks the `CSequence*` pointer reinterpreted as the return code (BN-confirmed artifact, dead/unreachable — callers gate on type first) (R2-Q3, 2026-07-02) | `src/AcDream.Core/Physics/Motion/MotionTableManager.cs` (`PerformMovement` default case) | No retail caller consults the return value for unhandled types (RawCommand/StopRawCommand/MoveTo\*/TurnTo\* route elsewhere); returning a stable non-zero sentinel preserves the only observable contract (non-zero = not success) without fabricating a pointer-shaped number | If a future port wires a caller that passes unhandled types AND branches on the exact return value, it would see `0xFFFFFFFF` where retail saw an arbitrary pointer — flag at that port | `PerformMovement` 0x0051c0b0 (`r2-motiontable-decomp.md` §11 default-case note) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue