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>