The central heat bank is now its real class: the binary's 0x0BBE
AggregateHeatSink (the value our VDATA enum named HeatSinkClassID -- there is
no streamed plain HeatSink; the segment walk now builds the bank).
- Ctor (@4ae8d0): heatSinkCount from res +0xFC (bhk1 = 6, matching its six
condensers); thermalConductance x 0.1 x count (231000 -> 138600); ambient
setpoint 300 (the mission [mission] temperature overwrite joins the
Mech-PlayerLink wave).
- RadiatorSimulation (@4ae73c) replaces the base heat step on the bank -- THE
system's only heat exit: relax toward the ambient target with rate
k = conductance x (1-damage) x (coolant/capacity) x flowScale / mass; tail
tops the bank's coolant from the attached store via the DrawCoolant virtual
(base 0 until the reservoir-attach wave). VERIFIED signed-correct: the bank
warms 77 -> 300 from the cold start, then flips to actively radiating
(-48K..-167K/step) once fired heat pushes it past ambient. Until now heat
only ever POOLED in the central sink; the mech now genuinely sheds it.
- Reservoir master path: capacity = 0.05 x bankCount x streamed = 6 (the
authentic tank), refilled.
Zero Fail; the expert economy regression stays green (202 heat events under
forced spam).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The subsystem-family cockpit buttons now work through the authentic Receiver
dispatch -> per-class handler-table path. The id space decodes cleanly:
Receiver::NextMessageID == 3, so ToggleCooling = 3 on the HeatSink chain and
the per-class id 4 is MoveValve on a Condenser / InjectCoolant on the
Reservoir -- same number, different class, the binary's per-receiver-class
convention.
- HeatSink::ToggleCoolingMessageHandler (@004ad6f8, id 3): novice-locked,
press-only; toggles coolantAvailable + coolantFlowScale together.
- Condenser::MoveValveMessageHandler (@4ae464, id 4): novice-locked; cycles
the valve 1->5->50->0->1 and calls Condenser::RecomputeValves (@0049f788):
every condenser's coolantFlowScale = valve / sum-of-valves. The ctor now
streams the AUTHENTIC flowScale=0; the Mech ctor seeds equal shares once at
spawn -- the flowScale=1 interim is retired.
- Reservoir::InjectCoolantMessageHandler (@4aee70, id 4): novice-locked;
press arms the flush when the tank holds charge, release drops it.
- MechSubsystem::NoviceLockout() (@4ac9c8): owner -> playerLink -> experience
== novice; unlinked mechs read unlocked.
- DEV harness BT_PRESS_VALVE / BT_PRESS_FLUSH: dispatch the REAL messages ~6s
into the mission from Mech::Simulate.
VERIFIED: spawn shares 6 x 0.166667; one MoveValve press -> Condenser1
valve=5 flow=0.5, others 0.1 (valve/sum exact); flush arms via the real
button message; NOVICE locks both presses (valve lines stay spawn-only, zero
flush). Zero Fail throughout.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The player-experience system now gates the entire heat/jam economy, verified in
BOTH directions with a one-token egg edit (TESTNOV.EGG, experience=novice):
NOVICE = 212 fire cycles all pinned at T=77, zero jams, zero shutdowns (the
heat model authentically absent); EXPERT = the full economy (duty cycles, 119
shutdowns + 2 authentic jams under forced fire). Zero Fail in both.
- BTPLAYER: the flag block renamed to its TRUE semantics (simLive @0x25c,
heatModelOn @0x260 -- the FUN_004ad7d4 master switch, advancedDamageOn pair,
levelFlag26c/270, experienceLevel); the ctor rows were already
binary-accurate (nov 0000 / std 1011 / vet 1111 / exp 1101); accessors +
[exp] sentinel.
- HeatSink::HeatModelActive() + ProjectileWeapon::LiveFireEnabled(): owner
mech -> Entity::GetPlayerLink() -> the BTPlayer flags, NULL-permissive.
Gates: both HeatSinkSimulation phases, every weapon fire-heat dump, and
CheckForJam is now the AUTHENTIC form (LiveFireEnabled + heatLoad<=0
early-outs + the minJamChance floor; the interim heat-degraded gate retired).
- THE LOAD-BEARING FIX: Mech ctor SetValidFlag(). Every 1995 entity ctor tail
marks itself valid; ours didn't -- Entity::Dispatch routes messages to an
INVALID entity into the deferred event queue, so the PlayerLink bind (and
every directly-dispatched mech message) silently never landed and the gates
read a NULL player forever.
- THE COOLANT SYSTEM (authentic bodies, byte-verified constants: HeatLoadScale
0.002 -> heatLoad now in [0,1]; equalize eps 1e-4; draw floor/ON
0.0025/0.003): UpdateCoolant (damage-scaled draw -- an undamaged mech leaks
nothing), BalanceCoolant (full clamp chain from ConductHeat), DrawCoolant
base=0 with the RESERVOIR override as THE SOURCE; Reservoir reconstructed
(capacity overlays thermalCapacity, CoolantSimulation + the full
InjectCoolant flush distribution, BT_FORCE_FLUSH dev hook); Condenser
RefrigerationSimulation (massScale = (1-damage)*refrigerationFactor >= 1 --
the heat pump that chills the bank; valveState inits 1; digit-suffix number
fix). Deferred: AggregateHeatSink family, cockpit-button handlers
(MoveValve/ToggleCooling/InjectCoolant), TrackSeekVoltage charge model.
Research driven by a 4-agent workflow dossier over the BT411 RE (verbatim
bodies for every function above + the egg experience plumbing).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Weapons dump firing heat into their own sinks, sinks conduct through the
Condenser bank into the central HeatSink, temperatures drive the degradation/
failure alarms, and every powered subsystem tracks its generator through the
electrical state machine. Verified: the authentic fire-discipline game -- a PPC
spikes 77->709K per shot, relaxes to 441K across its 5s reload, and sustained
fire climbs 441->674->838->960K, brushing the authored 1000K degradation
threshold. All calibration values match the BT411 audit exactly (central bank
1.39e6, PPC sink 174000, thresholds 77/1000/2000).
- HEAT: HeatSinkSimulation (@004ad924 absorb->T->load->conduct->alarm),
ConductHeat/ComputeHeatFlow (@004ad8ac/@004ad9ec two-body equilibrium
relaxation; flow==0 exactly at uniform T), UpdateHeatLoad (15-sample filter);
heat-state enum + accessors; installed by the HeatSink ctor.
- WIRE-VERIFIED: HeatSink resource gains linkedSinkIndex -- THE missing
ancestry int that shifted every descendant block +1 (voltageSourceIndex had
been reading the linked-sink index: "power sources" appeared to be
Condensers). Conduction topology wired from it at construction:
weapons->Condensers1-6->central; Generators->Condensers; Reservoir->central.
- MECHWEAP resource: authentic pip tail (pipPosition int + pipColor 3 floats +
pipExtendedRange int) per the BT411 verified overlay; with linkedSinkIndex
this closes the whole +3 alignment mystery (ProjectileWeapon pad deleted).
True bhk1 reads: PPC recharge 5.0s (not 1.0), discharge 0.99s, range 900.
- POWERSUB: ctor resolves voltageSourceIndex -> GeneratorA-D (wire-verified),
taps via Generator::TapVoltageSource (-1 when full); PoweredSubsystemSimulation
(@004b0bd0) electrical FSM (Starting/NoVoltage/Shorted/GeneratorOff/Ready);
GeneratorSimulation partial (start/short-recovery timers).
- Weapons: power step at sim head; Loading recharge gated on electrical Ready
(authentic @4bbdf5); FireWeapon dumps firing heat. HEAT UNITS: the chain is
1e7-native with TWO authoring conventions -- energy weapons store small
(PPC=11, x1e7 from the energy algebra), ballistics store native (SRM6=5.06e7,
dumped raw; double-scaling it was the bring-up runaway-temperature bug).
- SENSOR: authentic gating (@004b1c4c) -- power step, electrical-Ready gate,
heat-state switch (Degradation x0.5 / Failure 0). Verified Ready + 100%.
Deferred: coolant depletion/venting, the novice HeatModelOff gate, the charge
model (TrackSeekVoltage/voltage sag/I2R), weapon gate 1 + jam roll, the central
sink's forward-linked drain. Zero Fail across fire + neutral runs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HeatSink : public HeatableSubsystem (confirmed hierarchy) -- a thermal mass with
a coolant loop. Added to HEAT.HPP/.CPP: the class + its SubsystemConnection slot
helper + HeatSink__SubsystemResource. Ctor chains HeatableSubsystem and seeds the
thermal state from the resource (startingTemperature/degradation/failure/
thermalConductance/thermalMass), heatAlarm(3) = AlarmIndicator, heatFilter =
AverageOf<Scalar>(15). Statics, dtor, ResetToInitialState, TestClass/TestInstance
real; the per-frame thermal sim (HeatSinkSimulation/DrawCoolant) staged.
Also reconciled HeatableSubsystem to the 4.10 fields: its resource struct now
carries startingTemperature/degradationTemperature/failureTemperature/
thermalConductance/thermalMass (was the BT411-mislabeled thermalMass/degrade/fail/
coolingLoop set), and degradationTemperature/failureTemperature are base members
(HeatSink reads them). Verified slice now: MechSubsystem -> HeatableSubsystem ->
HeatSink. Compile-verified (BT: 25 ok); tree links clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HeatableSubsystem (HEAT.CPP) reconstructed + compile-verified (BT: 25 ok):
statics, ctor (chains MechSubsystem resource form + ResetToInitialState ->
currentTemperature=300, heatLoad=0), dtor, TestClass/TestInstance,
CreateStreamedSubsystem staged. Verified slice now: Subsystem -> MechSubsystem
-> HeatableSubsystem.
Also mapped the roster hierarchy from BT411, which is DEEPER than the staged
headers (recorded in MECHSUB.NOTES.md):
MechSubsystem -> HeatableSubsystem -> HeatSink -> PoweredSubsystem -> {Sensor,
Generator, ...}
Findings for the next steps: (1) HeatSink is MISSING entirely from the staged
tree and must be added between HeatableSubsystem and PoweredSubsystem;
(2) staged POWERSUB.HPP wrongly derives PoweredSubsystem from HeatableSubsystem
(real base = HeatSink) -- fix it; (3) PoweredSubsystem has inter-subsystem wiring
(resolves its voltage-source generator from the owner mech's subsystem roster),
imposing a segment-walk ordering constraint for the phase-4 ctor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>