feat(R4-V2): MoveToManager verbatim - all 33 members + conformance harness (closes M1/M3/M4/M5/M6/M10/M14-core)

The retail server-directed-movement brain (0x00529010-0x0052a987),
Core-only with every App dependency as a ctor/property seam for the
V4/V5 cutovers: node-plan builders for all four movement types
(TurnToObject's desired-heading clobber quirk VERBATIM; TurnToHeading's
immediate BeginNextNode - ACE's one-tick-late gap not copied),
PerformMovement (cancel 0x36 + unstick first), BeginNextNode with the
sticky handoff order (radius/height/tlid read BEFORE CleanUp),
BeginMoveForward (GetCommand walk/run cascade + stored-params
write-back + progress-clock seed), HandleMoveToPosition (chase arrival
dist <= distance_to_object per the adjudicated BN inversion; fail
distance -> 0x3D; progress >= 0.25 units/s over >= 1 s, incremental AND
overall; fail_progress_count write-only - retail has NO give-up
threshold and none was invented), HandleTurnToHeading (20/340 aux
deadband; the Ghidra-confirmed heading_diff mirror), HandleUpdateTarget
0x0052a7d0 (deferred-start: object moves wait for the first Ok
callback; retargets reset the progress clock without requeueing),
UseTime's initialized gate, InitializeLocalVariables per retail (flags
word + context_id zeroed, floats stale, FLT_MAX resets - not ACE's
transpositions).

TDD catch: default(Quaternion) is the ZERO quaternion, not identity -
a fresh manager's heading computations would silently read 90 degrees;
explicit IdentityPosition resets match the decomp's identity-Frame
semantics. Also pinned: retail's explicit double adjust_motion in
_DoMotion/_StopMotion; entry points never drain pending_actions (only
PerformMovement's cancel does) - re-issues must route through
PerformMovement, documented + tested.

101 new conformance tests incl. three end-to-end scripted drives
(chase turn->run->walk-demote->arrive; flee; frozen-heading
TurnToObject through retarget). Full suite: 3,961 passed.

Implemented by a dedicated agent against the V0-pinned spec; scope +
suite independently verified.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-03 11:43:50 +02:00
parent e0d2492cbb
commit addc8e97a8
13 changed files with 3779 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
namespace AcDream.Core.Physics.Motion;
/// <summary>
/// R4-V2 — verbatim port of retail's <c>MoveToManager::MovementNode</c>
/// (<c>acclient.h:57702</c>, struct #6350):
/// <code>
/// struct __cppobj MoveToManager::MovementNode : DLListData
/// { // +0 dllist_next, +4 dllist_prev (DLListData)
/// MovementTypes::Type type; // +8 — only 7 (MoveToPosition) and 9 (TurnToHeading) ever queued
/// float heading; // +0xc — only meaningful for type 9
/// };
/// </code>
///
/// <para>
/// <b>NAME WATCH (r4-port-plan.md §3 "New code target"):</b> named
/// <c>MoveToNode</c>, NOT <c>MovementNode</c>, to avoid colliding with R2's
/// <see cref="MotionNode"/> (the UNRELATED <c>CMotionInterp::pending_motions</c>
/// node — a different queue on a different class; see the AD-34 register
/// wording on both node types' shared "managed collection standing in for
/// retail's intrusive DLList/LList" pattern).
/// </para>
///
/// <para>
/// Retail allocates these with <c>operator new(0x10)</c> and links them onto
/// <c>MoveToManager::pending_actions</c> (a <c>DLList</c> — doubly-linked,
/// unlike <c>CMotionInterp</c>'s singly-linked <c>LList</c>) via
/// <c>DLListBase::InsertAfter</c> (tail-append; r4-moveto-decomp.md §4a).
/// Ported as a managed <see cref="System.Collections.Generic.LinkedList{T}"/>
/// of this value type — same pattern as <see cref="MotionNode"/>'s port
/// (AD-34 wording): node identity semantics preserved via
/// <c>LinkedListNode&lt;MoveToNode&gt;</c> references rather than raw
/// prev/next pointers, FIFO order preserved via tail-append +
/// <c>RemoveFirst</c>.
/// </para>
/// </summary>
/// <param name="Type">Retail <c>type</c> (+8) — only
/// <see cref="MovementType.MoveToPosition"/> (7) and
/// <see cref="MovementType.TurnToHeading"/> (9) are ever queued
/// (r4-moveto-decomp.md §4a: <c>AddMoveToPositionNode</c> /
/// <c>AddTurnToHeadingNode</c> are the only two producers;
/// <see cref="MoveToManager.BeginNextNode"/>'s dispatch is a defensive
/// <c>if/if</c>, not a full switch — an unknown type stalls rather than
/// throwing, matching the raw's shape).</param>
/// <param name="Heading">Retail <c>heading</c> (+0xc) — only meaningful for
/// <see cref="MovementType.TurnToHeading"/> nodes; zero/unused for
/// <see cref="MovementType.MoveToPosition"/> nodes.</param>
public readonly record struct MoveToNode(MovementType Type, float Heading);