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

133 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.