Cockpit: GENERATOR POWER ROUTING -- assign weapons to generators (task #12)

The pilot's second heat-management tool: the PoweredSubsystem message table
@0x50F4EC (ids 4-8) registered and implemented -- SelectGeneratorA-D
(@004b099c..@004b0a74: FindGeneratorByNumber roster walk on generatorNumber
@0x1E0 -> AttachToVoltageSource re-tap with tap accounting -> modeAlarm
Connected) and ToggleGeneratorMode (@004b0abc: Manual -> Auto -> detach+Manual
cycle).  Weapons inherit via the MechWeapon handler chain.  Desktop: F5-F8
assign the selected weapon (BT_CONFIG_SLOT) to Generator A-D, F9 toggles
reconnect mode; BT_GENSEL_TEST scripts a headless verify.

Verified live end-to-end: dispatch -> handler -> re-tap ("PPC_1 ->
GeneratorD (tapped)") -> the charging I^2R physically moved (GenD cold ~90K
baseline -> ~1570K carrying the PPC; GenA relieved), stable over a sustained
autofire soak with thermal-breaker trips.

FOUR defects found and fixed on the way [all T1/T2]:
- THE e17 HEAT EXPLOSION: Generator::SourceLevel misread *(this[0x38]+0x158)
  as linkedSinks->heatEnergy; it is the engine-base DamageZone @0xE0 ->
  damageLevel [0..1] (the same named-member pattern as the bank radiator's
  zone read).  A breaker-restarting generator emitted (1 - 4e8) x 10000
  volts; squared through the customers' I^2R feed, one restart blew the
  whole thermal network to e17.  Authentic: a damaged generator yields
  proportionally less voltage.
- FUN_004ac9c8 is NOT "IsDamaged": raw body = owner -> mech+0x190 player ->
  roleClassIndex(+0x274) == 0 -- the ROOKIE-role lockout for advanced
  cockpit systems.  New bridge BTPlayerRoleLocksAdvanced (NULL player =
  unlocked [T3]; bring-up role 2 = unlocked).  The old stand-in gated the
  handlers off permanently (healthy subsystems have simulationState==1).
- MESSAGE_ENTRY tables must be FUNCTION-LOCAL statics inside the accessor:
  as namespace-scope arrays they are read by other TUs' static-init chains
  before their own initializers run -- Build copies zeros and every id in
  the table is silently dropped (ids 9/10 only worked by TU-order luck;
  both tables relocated; gotcha recorded, reconstruction-gotchas #9).
- The AutoConnect hunt scanned GetSegment() -- the raw @004b0bd0 walks the
  subsystem ROSTER (+0x124/+0x128); EntitySegments were being cast to
  Subsystems (the classic +0x128 gotcha).

Also: the dense handler table's GAP slots (skipped ids) are uninitialized
heap -- the name-based Find strcmp-walks them and AVs; diagnostic probes use
the id-based Find only (the 1995 binary's own tables carry the same holes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-11 23:16:48 -05:00
co-authored by Claude Fable 5
parent 092408041c
commit d7b900d108
9 changed files with 355 additions and 29 deletions
+25
View File
@@ -1467,3 +1467,28 @@ void BTPostKillScore(Entity *victim, Scalar damage) // Step 7: KILL (+ MP deat
victim_player->Dispatch(&dead);
}
}
//#############################################################################
// BTPlayerRoleLocksAdvanced -- complete-type bridge (task #12)
//
// FUN_004ac9c8 [T1]: `return *(*(*(sub+0xD0) + 0x190) + 0x274) == 0` -- the
// subsystem's owner Mech -> the owning BTPlayer (mech+0x190, GetPlayerLink)
// -> roleClassIndex (+0x274, role resource +0xE4). TRUE = the ROOKIE role
// (class 0) LOCKS the advanced cockpit systems (generator routing, coolant
// valves); nonzero role classes unlock them. The old reconstruction
// mislabeled this fn "Subsystem::IsDamaged" -- with healthy subsystems at
// simulationState==1, that stand-in gated the power-routing handlers OFF
// permanently. A NULL player (the target dummy / unbound solo mech) would AV
// in the binary (every pod mech has a player); the port reads NULL as
// UNLOCKED so dev rigs work [T3].
//
int BTPlayerRoleLocksAdvanced(void *owner_mech)
{
if (owner_mech == 0)
return 0;
BTPlayer *player = MECH_OWNING_PLAYER(owner_mech);
if (player == 0)
return 0; // dev-permissive [T3]
return (player->roleClassIndex == 0) ? 1 : 0; // player+0x274 == 0
}