Files
TeslaRel410/restoration/source410/BT/MECH4.NOTES.md
T
CydandClaude Fable 5 6b9811473b BT410 5.3.120: the mech ducks -- the master perf's duck driver landed whole, and the collision cylinder breathes with it
The first block reconstructed from the isolated master performance
(@004a9f61..@004aa155, instruction-level read of the ndisasm listing), and
with it the Mech's authentic message roster straight from the binary's own
handler table: RealMaxSpeed 0x15, BalanceCoolant 0x16, SetBurningState
0x17, ClearBurningState 0x18, EjectPilot 0x19, DuckRequest 0x1A -- the
enum now exists in MECH.HPP with those names.

The system end to end: a DuckRequest message (@0049fa00) latches ONE
evaluation -- the latch is consumed every master frame and cleared by
Reset. The phase machine reads the raw simulation state, the mapper's
duck button cell and the analog demand (deadzone 1e-4): from gait 0/4 it
arms the sqd clip on the LEG channel only (slot 2 -- the slot map, not
the enum's walk names), from the ducked hold it arms squ (slot 3), and a
mech that starts limping while ducked is stood up by force. The
collision-volume alarm (level 1 standing / 0 ducked, guarded by the
authentic "Whoa! Bad Collision Volume State!") swaps the collision
template's maxY -- the box TOP; the ground probe's minY is untouched --
between the ctor-captured height and 0.6x it (the double at @004a2d38).
duckState publishes for the cockpit's crouch selector.

Live-verified end to end: BT_FORCE_DUCK (an authentic press every 4s on
its own clock) cycles press -> sqd -> volume 9.91->5.94 -> ducked hold ->
press -> squ -> standing, continuously, no faults. En route the test
machine-checked three neighbors for free: the squat clips resolve and
play at the idle rate, the clip-finish transitions for slots 2/3 fire,
and the machine correctly REFUSES to duck while walking (legState 5).

Deferred, recorded: the per-arm MarkUpdate masks (8, 1) ride the
still-empty ForceUpdate (the MP update-record feed), and the mapper duck
cell's streamed binding id awaits a BT_MAP_LOG audit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 17:23:24 -05:00

7.8 KiB
Raw Blame History

MECH4 — pre-TU reconstruction notes

Status: no MECH4.CPP exists yet. The ground snap, frame rejection, knockdown stagger and collision economy that the binary keeps in mech4's master performance are currently RE-HOSTED inside Mech::Simulate (MECH, 5.3.98100) at seams marked STAGED. This file carries the decode work for the pieces not yet written, so it survives session boundaries.

Mech::ProcessCollision (@004abb40) — decoded, not yet reconstructed

The override of the engine's protected virtual (MOVER.CPP:1354), called per contact by Mover::ProcessCollisionList. The decomp (part_012.c:1520615416) reads as the BASE function plus four additions:

  1. The base head, verbatim: collisionVolume->ProcessCollision(collision, worldLinearVelocity, lastCollisionList, &damage->surfaceNormal, &penetration); on a miss NOTHING runs. Clamp penetration to the slice, ratio = pen/dt, damage->damageAmount = StaticBounce(old, dt, r, normal, &elasticity, minimumBounceSpeed, &friction) with the elasticity/friction locals seeded from the mover's coefficients.

  2. The Mover-family branch (owner = the collided solid's owner entity, solid+0x1c; family test FUN_0041a1a4 against the "Mover" derivation GUID @0x4e4518):

    • SEPARATING-CONTACT GATE: (ourWorldVel other->WorldVel[vtbl+0x20]) · normal < 1e-4 (_DAT_004ac044) → return, no damage, no dispatch.
    • If the other's classID == 0xbb9 (mech), build a TakeDamage message (raw wire shape {id, len, inflictor = DAT_0050b9ac global, zone 1, the Damage verbatim}) and Dispatch it AT THE OTHER — the mech-vs-mech ram. Use our Entity::TakeDamageMessage ctor as the weapon path does.
  3. The CulturalIcon branch (GUID @0x4e7038):

    • stopping = other->instanceFlags & 0x8000 (the StoppingCollisionVolume flag, entity+0x28).
    • Same separating gate against a ZERO other-velocity (icons are static).
    • Dispatch the crunch TakeDamage at the icon (crushable props have no zones → their base handler no-ops it, but the dispatch is real).
    • If NOT stopping: damage->damageAmount = 0.00123f (bits 0x3aa137f4) — the WALK-THROUGH SENTINEL. The caller (the response block in Simulate) must treat exactly 0.00123f as "the move stands": restore the post-snap saves, zero the amount, feed the gyro crunch (torque 0.4 along the normalized force, upward impulse 0.2 — binary @4aa81e/@4aa86c).
  4. The material tail: the elasticity/friction OUT values from StaticBounce are compared against _DAT_004ac048; sets collisionTemporaryState (+0x44c) to 1, or 2 when both match and the state was 0. The state is pushed into the collisionState StateIndicator once per frame by the response block (0→1 fires the impact sound). Needs the _DAT_004ac048 constant identified before writing (likely the default-material coefficient — check the literal pool at 0x4ac048).

Two hazards the donor fought that DO NOT apply here

  • Compounded StaticBounce reflections: our engine's Mover::ProcessCollisionList (MOVER.CPP:1203) restores worldLinearVelocity = initial_velocity and position PER CONTACT before each ProcessCollision call, and averages the results. The port's frameEntryWorldVelocity economy guard has no counterpart hazard in this engine — do not import it.
  • The AlarmIndicator ODR split: a port-only problem; our tree reads MovementMode() / the alarms directly.

Already re-hosted in Mech::Simulate (move here when mech4.cpp exists)

piece binary landed
ground probe + snap @4aa6304aa6cc 5.3.98
frame rejection + crash self-damage + knockdown @4aa6cf4aab0b 5.3.99
collision economy divert + distributor @0x4a0368 → @0049ffcc 5.3.100 (in the damage hub, where the binary has it)
status→mode limp promotion master perf (un-decompiled) 5.3.97, STAGED
forwardCycleRate re-pick (ground/airborne) IntegrateMotion head partially — the ctor sets it once; the per-frame re-pick needs +0x3f4

Still to decode

  • FUN_004a9b5c — the master performance. ISOLATED AND MAPPED (2026-08-04): one single 5,676-byte body @004a9b5c..@004ab168 (one ret), hidden inside the manifest's mis-sized @004a9770 entry (that fn is really the stream-builder's 6.6KB name→SharedData strcmp resolver, and Ghidra swallowed everything to @004ab188 behind it). Registration proven from the ctor decomp: (flags & 0xC) == 4 → replicant perf @004ab9d8 (also hidden, inside @004ab430's claimed range); else master @004a9b5c. Recipe: extract CODE bytes (va 0x1000 → file 0xe00), ndisasm -b 32 -o 0x4a9b5c.

    The ordered call skeleton (111 calls) — blocks in execution order, matching our Simulate's structure and locating every staged gap:

    1. controls/demand intake (mech.cpp helpers 0x4a4eab/0x4a4ee9/0x4a4f9e)
    2. three cerr+SetLevel triplets — illegal-state alarm guards
    3. THE DUCK DRIVER — DECODED + LANDED 5.3.120, live-verified (BT_FORCE_DUCK cycles down/hold/up continuously on the rig). The full system: DuckRequest message 0x1A (authentic name from the binary's handler table; roster = RealMaxSpeed 0x15, BalanceCoolant 0x16, SetBurningState 0x17, ClearBurningState 0x18, EjectPilot 0x19, DuckRequest 0x1A) latches one evaluation (+0x398, one-shot, Reset clears); the phase machine reads the RAW simulation state, the mapper's duck cell (+0x25c) and the analog demand (+0x79c, deadzone 1e-4 @0x4ab16c): gait 0/4 + input -> down (LEG slot 2, sqd), gait 1 -> up (slot 3, squ); limp modes 3/4 force stand-up; the collisionVolumeState alarm (+0x4c4; 1=standing, 0=ducked; "Whoa! Bad Collision Volume State!" guard, line 417) swaps the collision template's maxY (ExtentBox +0xc == the box TOP; the ground probe's minY untouched) between the ctor-captured height and 0.6x it (the double @004a2d38); duckState publishes for the cockpit selector. Still open here: the two MarkUpdate masks (8 then 1) ride our ForceUpdate no-op, and the mapper duck cell's streamed binding id awaits a BT_MAP_LOG audit on a duck-bound RES.
    4. verify-guarded state read (mech4 accessors + Fail 0x40385c)
    5. gyro feed a (vec pair → gyro_b 0x4b2de4, gyro_a 0x4b2d8c)
    6. death arm: InDeathTransition 0x49fb54 → the 1.9KB mech2 advancer 0x4a71f4 under it (death-clip arming)
    7. status→mode LIMP PROMOTION (0x4a5028 + SetAnim/Mark/SetLevel)
    8. update-record block (0x421bac/0x40e5f0/0x4227e0/0x41db7c/0x422bac + Point3D copies) — the dead-reckon/update writer
    9. collision → gyro crunch (gyro feeds + alarms)
    10. knockdown/bump arms (EntityID pair + two SetAnim/Mark/SetLevel arms)
    11. IntegrateMotion @004ab1c8 + position dirty marks
    12. pose/joint tail: gyro_d/gyro_c → Torso::UpdateJoints @004b66b4 → five StateIndicator::SetState publishes + four GetState reads
    13. frame close (position latch)

    MarkUpdate = FUN_004a4c54 (×20!) — the update-record dirty-mask OR (death strips motion bits); our ForceUpdate(int) is an EMPTY STUB, so none of these marks exist in our tree yet (MP replication feed). Next sitting: instruction-level read of blocks 3 and 7 (the two STAGED items), then the mark masks.

  • @004ab430 ReplicantPerformance (1432B) — the replicant-side interior.

  • @004ab1c8 IntegrateMotion — decoded (see 5.3.95 notes) but only partially re-hosted: the local-velocity/orientation integration and the dead-reckon latency fold (+0x778/+0x77c) remain. +0x598 is a Point3D (its replicant branch writes it with a vector subtract), so motionEventName in MECH.HPP is mistyped as CString — fix when this lands.