EJECT/PANIC wired: Mech::EjectPilot (id 0x19 @0049f854) + the crippled-mech gate

The button died twice before reaching game logic: no handler (0x19 was
unregistered) and no sender (the pod's panic was a control bit, not a
mappable). Both halves reconstructed from raw disasm -- the handler AND
its permission evaluator sat in export gaps.

@0049f854 EjectPilot: press-only; gated on ejectPermitted (@0x414) and
!IsDisabled; console eject notice (relay wire = tracked tail; the
suppressConsole@0x258 latch that prevents the death double-notify IS
wired); graphicAlarm -> 10, which kills via the >=9 predicate; then a
self TakeDamage {inflicting=SELF, zone -1, Explosive, amount =
ScenarioRole::killBonus}. Role layout byte-settled via the role reader
@00429bec dest offsets + the ctor record copy: +0x1c IS killBonus, +0x20
is the 4.10-only SpecialCaseDeathPenalty, +0x28 returnFromDeath (all
prior citations reconciled). KillBonus authors NOWHERE in shipped
content -> the charge is 0 and the ALARM does the killing: our bench
outcome is the pod outcome.

@0049fa1c EvaluateEjectPermission: eject only from a CRIPPLED mech --
bank coolant fraction < 0.05 | zero live generators | live weapons
below mech+0x448 (no exported writer: zero, clause inert) | leg-gimped
novice. A healthy mech REFUSES the button; no free resets.

Input: binding-engine "Eject" action -- Backspace / pad LeftThumb
(default profile + shipped CONTROLS.MAP). Bridges per the databinding
rule: weapon/generator/bank/player reads land in their complete TUs;
MechSubsystem gains the both-cells destroyed accessors (gotcha #22).
Bench scalpels: BT_EJECT_AT=<frame> (path-identical synthetic press),
BT_KILL_SUBSYS now takes a comma list.

Verified single-node: healthy press REFUSED (x2), four generators
killed, next press PUNCH-OUT -> death, wreck smoke, respawn; the
respawned mech refuses again (permission re-evaluates after Reset).
Death rides the normal damage/death chain, so MP replication is the
proven path. Tails tracked: console relay notice, RIO 0x38 panic
control, alarm-10 eject audio/canopy, SpecialCaseDeathPenalty consumer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-08-02 16:45:54 -05:00
co-authored by Claude Fable 5
parent e1ae92264a
commit e996be249d
16 changed files with 345 additions and 14 deletions
+21
View File
@@ -319,6 +319,27 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a
Loaded ("full == the gear's seek voltage" is the arcade's own discharge algebra). Repro +
verify: BT_SEEKTEST seek-abuse -- pre-fix deadlocks at pct=0/alarm=3; post-fix 38 rescues,
ends Loaded/pct=1. `[seek]` log prints the per-gear table + rescue events.
- **EJECT/PANIC cluster DECODED + WIRED 2026-08-02 [T1 raw disasm]:** `@0049f854`
**Mech::EjectPilot** (table id 0x19; sat in the 0x49f854-0x49fa00 export gap) — gates
{msg+0xc > 0, ejectPermitted@0x414, !IsDisabled@0049fb54}, console eject notice
(`FUN_004c198c` ctor from ownerID@0x18c → network sender, code 5 — port tail),
player `suppressConsole`@0x258 = 1 (the following death must not double-notify),
`graphicAlarm → 10` (EJECT state; **kills via the ≥9 predicate** — IsMechDestroyed),
self TakeDamageMessage {inflicting=SELF, zone 1, Explosive, amount =
`ScenarioRole::killBonus` role+0x1c, burst 0}. Role layout byte-settled via the
reader `@00429bec` dest offsets + ctor record copy: +0xc inflicted, +0x10 received,
+0x14 ffPenalty, +0x18 damageBias, **+0x1c killBonus**, **+0x20
SpecialCaseDeathPenalty (4.10-only key)**, +0x28 returnFromDeath. `KillBonus`
authors NOWHERE in shipped content → charge 0, alarm does the killing — authentic.
`@0049fa1c` **EvaluateEjectPermission** (writes @0x414; unexported caller): roster
walk — 0xBBE bank coolant fraction (+0x12c/+0x128) < 0.05 (`@0049fb50`) | zero live
Generators (0xBC1, +0x40 ≠ 1 && stateAlarm@0x210 ≠ 4) | live MechWeapons
(0x511830; ProjectileWeapon family 0x5121a8 also needs weaponAlarm@0x364 ≠ 7 NoAmmo)
< mech+0x448 (**no exported writer — zero, clause inert** [T3]) | leg-gimped
(mode 3/4) novice (simLive 0). Port: mech.cpp handler+evaluator, bridges
BTWeaponCountsForEject / BTGeneratorCountsForEject / BTHeatSinkBankCoolantFraction /
BTPlayerEjectBookkeeping; input = binding-engine `Eject` action (Backspace /
pad LeftThumb; BENCH `BT_EJECT_AT=<frame>`, `BT_KILL_SUBSYS` now comma-list).
- **THE SIM TIME MODEL — CLOSED 2026-08-02 (issue #96) [T1]:** the arcade
`Simulation::PerformAndWatch` is **`FUN_0041c018`** (part_002.c:5101):
`slice = (till.ticks this[4].ticks) / DAT_0052140c; this[4] = till;