BT410 notes: the master performance found whole -- 5.6KB @004a9b5c isolated from a Ghidra mis-merge, full call skeleton mapped
Not code -- evidence. The mech's master Performance was never decompiled because Ghidra sized the stream-builder's strcmp resolver at 6.6KB and swallowed everything behind it; the ctor's member-pointer constants prove @004a9b5c (master) and @004ab9d8 (replicant, hidden the same way inside @004ab430's range). One body, one ret, 1509 instructions, and its 111-call skeleton maps block-for-block onto our fragment-built Mech::Simulate -- validating the 5.3.90-104 arc -- while locating exactly what remains staged: the duck driver (three SetAnim/Mark/SetLevel arms), the status->mode limp promotion, the gyro rumble feed points, and twenty FUN_004a4c54 update-record dirty marks our empty ForceUpdate never makes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,85 +1,116 @@
|
||||
# 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.98–100) 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:15206–15416) 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 | @4aa630–4aa6cc | 5.3.98 |
|
||||
| frame rejection + crash self-damage + knockdown | @4aa6cf–4aab0b | 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.
|
||||
# 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.98–100) 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:15206–15416) 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 | @4aa630–4aa6cc | 5.3.98 |
|
||||
| frame rejection + crash self-damage + knockdown | @4aa6cf–4aab0b | 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**: three arms of {mech2 SetAnim 0x4a7fc4,
|
||||
MarkUpdate ×2, AlarmIndicator::SetLevel} — duck down/hold/up
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user