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:
co-authored by
Claude Fable 5
parent
e1ae92264a
commit
e996be249d
@@ -3227,6 +3227,39 @@ void
|
||||
sVolDn = dn;
|
||||
sVolUp = up;
|
||||
}
|
||||
// PANIC/EJECT (edge): dispatch Mech msg 0x19 (@0049f854) at
|
||||
// ourselves -- the handler owns every gate (crippled-mech
|
||||
// permission @0x414, IsDisabled), so a stray press on a
|
||||
// healthy mech is a logged no-op.
|
||||
{
|
||||
static int sEjectHeld = 0;
|
||||
int ejectPress = gBTInput.eject && !sEjectHeld;
|
||||
sEjectHeld = gBTInput.eject;
|
||||
// BENCH (BT_EJECT_AT=<frame>): synthesize the SAME press
|
||||
// at that frame and every 300 after -- path-identical to
|
||||
// the key (one message, one dispatch), so a refused-then-
|
||||
// permitted sequence is provable in a single headless run.
|
||||
{
|
||||
static int sEjFrame = 0;
|
||||
static int sEjAt = -2;
|
||||
if (sEjAt == -2)
|
||||
{
|
||||
const char *e = getenv("BT_EJECT_AT");
|
||||
sEjAt = (e && *e) ? atoi(e) : -1;
|
||||
}
|
||||
++sEjFrame;
|
||||
if (sEjAt > 0 && sEjFrame >= sEjAt
|
||||
&& ((sEjFrame - sEjAt) % 300) == 0)
|
||||
ejectPress = 1;
|
||||
}
|
||||
if (ejectPress)
|
||||
{
|
||||
ReceiverDataMessageOf<int> punch_out(
|
||||
Mech::EjectPilotMessageID,
|
||||
sizeof(ReceiverDataMessageOf<int>), 1);
|
||||
Dispatch(&punch_out);
|
||||
}
|
||||
}
|
||||
// task #13: the Valve action cycles the coolant valve.
|
||||
gBTValveKey = gBTInput.valve;
|
||||
// Gitea #7: the Flush action HELD = the coolant MFD's
|
||||
@@ -6198,19 +6231,27 @@ void
|
||||
static int s_ksFrame = 0;
|
||||
if (++s_ksFrame == 900) // ~15 s in, after the mech settles
|
||||
{
|
||||
const char *want = getenv("BT_KILL_SUBSYS");
|
||||
for (int i = 2; i < GetSubsystemCount(); ++i)
|
||||
// COMMA-LIST (eject bench): "GeneratorA,GeneratorB,..." kills
|
||||
// each named subsystem; single-name behavior unchanged.
|
||||
char want_list[256];
|
||||
strncpy(want_list, getenv("BT_KILL_SUBSYS"), sizeof(want_list) - 1);
|
||||
want_list[sizeof(want_list) - 1] = 0;
|
||||
for (char *want = strtok(want_list, ","); want != 0;
|
||||
want = strtok(0, ","))
|
||||
{
|
||||
Subsystem *s = GetSubsystem(i);
|
||||
if (s == 0 || s->GetName() == 0) continue;
|
||||
if (stricmp(s->GetName(), want) != 0) continue;
|
||||
if (!s->IsDerivedFrom(MechSubsystem::ClassDerivations)) continue;
|
||||
((MechSubsystem *)s)->SetSubsystemDamageLevel(1.0f);
|
||||
((MechSubsystem *)s)->ForceCriticalFailure();
|
||||
DEBUG_STREAM << "[killsub] '" << s->GetName()
|
||||
<< "' force-destroyed (statusAlarm=1, zone=1.0)"
|
||||
<< std::endl << std::flush;
|
||||
break;
|
||||
for (int i = 2; i < GetSubsystemCount(); ++i)
|
||||
{
|
||||
Subsystem *s = GetSubsystem(i);
|
||||
if (s == 0 || s->GetName() == 0) continue;
|
||||
if (stricmp(s->GetName(), want) != 0) continue;
|
||||
if (!s->IsDerivedFrom(MechSubsystem::ClassDerivations)) continue;
|
||||
((MechSubsystem *)s)->SetSubsystemDamageLevel(1.0f);
|
||||
((MechSubsystem *)s)->ForceCriticalFailure();
|
||||
DEBUG_STREAM << "[killsub] '" << s->GetName()
|
||||
<< "' force-destroyed (statusAlarm=1, zone=1.0)"
|
||||
<< std::endl << std::flush;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user