Wire ToggleCooling (msg 3): restore the coolant on/off button + handler chain

Coolant priority (Lynx: weapon MFD -> Display -> Coolant) was unreachable: the
ToggleCooling handler (@004ad6f8, HeatableSubsystem table @0x50E41C id 3) was
undefined AND the handler chain skipped it -- PoweredSubsystem::GetMessageHandlers
chained straight to the Receiver root, bypassing HeatSink/HeatableSubsystem, so a
weapon's dispatch never saw id 3.

Reconstructed ToggleCooling from the disassembly (tools/disas2.py 0x4ad6f8): a
per-subsystem coolant on/off TOGGLE -- novice-locked (owner->BTPlayer->
roleClassIndex+0x274==0, the same guard as MoveValve), press-only, then flip
coolantAvailable(+0x134) 0<->1 and coolantFlowScale(+0x15C) 0.0f<->1.0f.  Cutting a
system's cooling frees the shared loop for the rest -- the emergent "coolant
priority" (the mechanism is a toggle, not a multi-level cycle).

- heat.hpp / heatfamily_reslice.cpp: HeatSink::ToggleCoolingMessageHandler (id 3)
  + HeatSink::GetMessageHandlers.  Registered at HeatSink (the abstract
  HeatableSubsystem never instantiates; every concrete heatable subsystem is a
  HeatSink, where the coolant fields live) -- identical coverage to the binary's
  base-table registration, no downcast.
- powersub.cpp + Condenser/Reservoir: chain their handler sets through
  HeatSink::GetMessageHandlers so id 3 reaches every heatable subsystem.
- mech4.cpp: BT_COOLTOGGLE_TEST scripted inject (dispatch id 3 to the first weapon)
  for verification.

Verified (BT_COOLTOGGLE_TEST + BT_COOL_LOG, expert egg): "[cool] PPC_1 ToggleCooling
reached" then coolant OFF(flow 0.0) <-> ON(flow 1.0) each press -- chain routes,
handler toggles, novice guard honored.  Both build/ + build-glass/ compile clean;
existing ids 4-8 unaffected (found before id 3).  NEXT: the #2 MFD button routing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-20 23:04:04 -05:00
co-authored by Claude Opus 4.8
parent cae616dc7c
commit cbc5ff9532
5 changed files with 126 additions and 4 deletions
+12 -2
View File
@@ -249,8 +249,18 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a
RealMaxSpeed@0049f604, **0x16 BalanceCoolant@0049f728**, 0x17 SetBurningState@0049f674, 0x18
ClearBurningState@0049f700, **0x19 EjectPilot@0049f854**, **0x1a DuckRequest@0049fa00** (the
manual's CROUCH; streamed button 0x13 sends it — unreconstructed).
- **HeatableSubsystem** @0x50E41C: {3, "ToggleCooling"→@004ad6f8} (the Eng-page msg-3 button on
every weapon/avionics — unwired in the reconstruction).
- **HeatableSubsystem** @0x50E41C: {3, "ToggleCooling"→@004ad6f8}. **Disassembled 2026-07-20**
(`tools/disas2.py 0x4ad6f8`): a per-subsystem coolant on/off TOGGLE (NOT a multi-level "cycle
priority" — that's the emergent effect, not the mechanism):
`if (FUN_004ac9c8(this)) return;` (== `BTPlayerRoleLocksAdvanced` NOVICE lockout, owner→
BTPlayer→`+0x274`==0) → `if (msg->dataContents(+0xc) <= 0) return;` (press only) → toggle
`coolantAvailable(+0x134)` 0↔1 and `coolantFlowScale(+0x15C)` 0.0f↔1.0f. So a pilot can cut a
subsystem's coolant flow (raising its heat) to free the shared loop for others. **UNWIRED cause
found:** the reconstruction's `PoweredSubsystem::GetMessageHandlers` chains straight onto the
`Receiver` root (powersub.cpp:144), SKIPPING `HeatSink``HeatableSubsystem` — so a weapon's
handler chain never reaches id 3. Faithful wiring = define `HeatableSubsystem::GetMessageHandlers`
{3, ToggleCooling} + restore the chain `PoweredSubsystem→HeatSink→HeatableSubsystem`. Sibling
template: `Condenser::MoveValveMessageHandler` (id 4, same novice guard). See [[experience-levels]].
- **Generator** @0x50FB90: {4, "ToggleGeneratorOnOff"→@004b1ed0} (streamed buttons 0x1A-0x1D →
GeneratorA-D — unreconstructed).
- **Searchlight** @0x51120C: {3, "ToggleLamp"→@004b860c}; PowerWatcher/ThermalSight-side variant