feat(R3-W2): pending_motions lifecycle + the MotionDone consumer — the R2 seam lands (closes J1, J17)
MotionInterpreter implements IMotionDoneSink: pending_motions (LinkedList<MotionNode>, AD-34 wording) + add_to_queue 0x00527b80 + motions_pending 0x00527fe0 + MotionDone 0x00527ec0 verbatim (action-class head → UnstickFromObject no-op seam (→R5 StickyManager) + BOTH states' action-FIFO pops; head popped UNCONDITIONALLY — A7: never match-by-id, params unread in this build) + HandleExitWorld 0x00527f30 (the raw decompile's null-physics-obj branch would infinite-loop if translated literally — adjudicated as retail dead code, documented on the method) + is_standing_still 0x00527fa0 + MotionAllowsJump 0x005279e0 with the A1-pinned literal blocklist (28-case boundary table test: Fallen blocks, Falling passes — ACE's transposition NOT copied). Queue producers wired into the funnel per the decomp anchors (re-verified from the raw text): DispatchInterpretedMotion's success path computes jump_error_code via retail's double-check shape (motion_allows_jump(motion), then forward_command when non-action; the DisableJumpDuringLink params leg is TODO(W5) — no MovementParameters threaded yet) + add_to_queue; apply_interpreted_movement's turn-stop re-queues the Ready node (@305766-305785 — the plan's producer #2 and #3 are the SAME site; StopInterpretedMotion's internal add_to_queue @305657 is W5 scope). ContextId=0 with TODO(W5): retail's own compiled code reads an UNINITIALIZED local for it at this call site. GameWindow: MotionDoneTarget now binds the entity's REAL consumer — player via PlayerMovementController.Motion (new internal property), remotes via RemoteMotion.Motion — resolved AT FIRE TIME (a remote's RemoteMotion can be created after its first anim tick; an eager capture would drop completions forever). Despawn runs BOTH layers' exit-world drains (manager then interp) per §4. AD-36 narrowed (consumed for creature-class; doors/statics recorder-only until R5). 51 new conformance cases incl. the end-to-end chain test: MotionTableManager.AnimationDone → sink → interp pending head pops in step. 183-case observer-trace + funnel suites green unchanged (queue side effects additive). Full suite: 3,582 passed. Implemented by a dedicated agent (MotionInterpreter side) against the W0-pinned spec; producer placements + MotionAllowsJump branch algebra independently reviewed against the raw decomp; GameWindow wiring by the orchestrator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8664959152
commit
371679915e
5 changed files with 696 additions and 13 deletions
|
|
@ -98,7 +98,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
| 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` 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) |
|
||||
| AD-36 | `IMotionDoneSink.MotionDone` is OBSERVED, not consumed: R2-Q4 binds the seam to an ACDREAM_DUMP_MOTION diagnostic recorder (`GameWindow` TickAnimations bind site); retail's chain continues `CPhysicsObj::MotionDone` → `MovementManager::MotionDone` → `CMotionInterp::MotionDone` popping `pending_motions` (R2-Q4, 2026-07-02) | `src/AcDream.Core/Physics/AnimationSequencer.cs` (`MotionDoneTarget`) | Stage boundary: `CMotionInterp.pending_motions` does not exist until R3-W2, which rebinds the seam to `MotionInterpreter.MotionDone` (r3-port-plan.md §4) | Until R3, nothing tracks queued-motion completion on the interp side — jump_is_allowed's pending-head peek and action RemoveAction mirrors are inert (they don't exist yet either) | `CPhysicsObj::MotionDone` 0x0050fdb0; r2-port-plan.md §4 |
|
||||
| AD-36 | `IMotionDoneSink.MotionDone` consumed for CREATURE-class entities only: R3-W2 binds the seam to the entity's `MotionInterpreter.MotionDone` (player via `PlayerMovementController.Motion`, remotes via `RemoteMotion.Motion`, resolved at fire time); interp-less entities (doors, statics) keep a diagnostic-recorder-only target — retail gives every CPhysicsObj a MovementManager/CMotionInterp (R2-Q4 seam, narrowed R3-W2, 2026-07-02) | `src/AcDream.App/Rendering/GameWindow.cs` (TickAnimations MotionDoneTarget bind) | Door/static motion (On/Off cycles) completes via the manager queue alone; nothing consumes their MotionDone until R5 gives every entity class the full MovementSystem pipeline | A door behavior depending on pending_motions bookkeeping (none known — doors have no jump/action semantics) would silently no-op | `CPhysicsObj::MotionDone` 0x0050fdb0; retire in R5 (MovementSystem for all classes) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue