BT410 5.3.101: MECH4 pre-TU notes -- the ProcessCollision decode, two donor hazards that do not apply, and the re-hosting ledger

Captures the @004abb40 decode (base head + Mover-family branch with the
separating gate and mech-vs-mech ram + CulturalIcon branch with the 0x8000
stopping flag and the 0.00123f walk-through sentinel + the material tail)
before implementation, so the analysis survives session boundaries.

Also records two hazards BT411 fought that do NOT apply to this engine --
the compounded-reflection economy guard (our ProcessCollisionList restores
velocity per contact, MOVER.CPP:1203) and the AlarmIndicator ODR split --
plus the ledger of master-perf pieces currently re-hosted in Mech::Simulate
at STAGED seams, for the day mech4.cpp exists and they move home.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-03 14:12:03 -05:00
co-authored by Claude Fable 5
parent e40abc86ea
commit 3790262aad
+85
View File
@@ -0,0 +1,85 @@
# 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. Ghidra never decompiled it
(objdump territory). Owns: the turn-in-place dispatcher (arms state 4 on
both channels in lockstep), the status→mode promotion, likely the
`runSpeedMax` (+0x7a0) writer, the gyro rumble timer.
* `@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.