Files
TeslaRel410/restoration/source410/BT/HEAT.NOTES.md
T
CydandClaude Fable 5 e1e5a9a6db BT410 Phase 5.3.12: experience gates + coolant system -- novice/expert modes REAL
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>
2026-07-22 22:15:07 -05:00

9.8 KiB
Raw Blame History

HEAT.CPP / POWERSUB.CPP — the power/heat wave (Phase 5.3.10, 2026-07-21)

The mech's thermal + electrical economy is LIVE: weapons dump firing heat into their own sinks, the 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.

The heat topology (wire-verified on TEST.EGG)

linkedSinkIndex (the previously-missing HeatSink resource int — see below) wires the authored conduction graph at construction:

weapons / equipment  ->  Condenser1..6 (slots 4-9)  ->  central HeatSink (slot 2)
GeneratorA..D (10-13) ->  Condensers
Reservoir (3)         ->  central HeatSink

Authored values match the BT411 calibration audit EXACTLY: central bank mass 1.39e6, PPC's own sink 174000, thresholds 77 start / 1000 degradation / 2000 failure, PPC conductance 86800. (The central sink's own linkedSinkIdx=5 is a FORWARD reference — Condenser2 isn't constructed yet at slot 2's ctor — so its drain is unwired; a post-walk fixup is a small future item. Everything drains INTO it fine.)

Reconstructed

  • HeatSink::HeatSinkSimulation (@004ad924): absorb pendingHeat → T = E/mass → UpdateHeatLoad (15-sample filter) → ConductHeat → alarm thresholds. Installed by the HeatSink ctor (derived classes override and chain it).
  • ConductHeat / ComputeHeatFlow (@004ad8ac/@004ad9ec): the two-body equilibrium relaxation — (T·massScale (E_other+pending+E_own)/denom)·tau· (1exp(dt·conductance·(coolant/capacity)·flowScale/denom)). Verified: flow == 0 exactly at uniform temperature; a fired PPC bleeds ~170K units/step into Condenser4.
  • PoweredSubsystem ctor + PoweredSubsystemSimulation (@004b0f74/@004b0bd0): resolves voltageSourceIndex from the roster (GeneratorA-D — wire-verified), attaches the tap (Generator::TapVoltageSource, 1 when full), and runs the electrical FSM (Starting=0 / NoVoltage=1 / Shorted=2 / GeneratorOff=3 / Ready=4) watching the generator state. Deferred: the AutoConnect replacement-generator hunt (needs the status flags / damage wave).
  • Generator::GeneratorSimulation (PARTIAL): heat step + start/short-recovery timers; the load model (voltage sag, I²R self-heat feeding the charge integration) joins the electrical-charge wave (TrackSeekVoltage).
  • Sensor::SensorSimulation upgraded to the authentic gating (@004b1c4c): power step first, radar = 1 damage, electrical-Ready gate (badVoltage), heat-state switch (Degradation ×0.5, Failure → 0). Verified: voltState=4, radar 100% on the healthy mech.
  • Weapons: Emitter/Projectile sims run the power step at their head; the Loading recharge is gated on electrical Ready (authentic @4bbdf5); FireWeapon dumps the firing heat.

Heat units (1e7-native) — TWO authoring conventions

The heat chain is 1e7-unit-native (BT411 audit). The stream stores heatCostToFire in TWO conventions:

  • Energy weapons: small units (PPC = 11) — the ×1e7 comes from the energy closed form ((1dF)·E ≈ heatCost·1e7 at full charge). Our partial multiplies by 1e7 pending the charge model.
  • Ballistics: native units (SRM6 = 5.06e7) — dumped RAW. (Double-scaling this to 5e14 was the runaway-temperature bug during bring-up.) Both land ~+640K on the weapon's own sink — the consistent design magnitude.

The resource-alignment resolution (wire-verified, closes the +3 mystery)

The raw-stream dumps pinned the FULL ancestry alignment:

  • HeatSink__SubsystemResource.linkedSinkIndex — THE missing int that shifted every descendant block +1 (PoweredSubsystem's voltageSourceIndex previously read the linked-sink index — hence "power sources" appearing to be Condensers).
  • The MechWeapon pip tail is pipPosition(int) + pipColor(3 floats) + pipExtendedRange(int) (+2) — matches BT411's verified overlay exactly.
  • +1 +2 = the +3 the interim ProjectileWeapon pad compensated; the pad is gone.
  • True bhk1 reads: PPC recharge 5.0 s (not the misaligned 1.0), discharge 0.99 s, range 900, damage 12, heatCost 11, pipColor (0,0,1); SRM6 recharge 5 s, range 800, salvo damage 35, missileCount 6.

VERIFIED (BT_FORCE_FIRE + BT_POWER_LOG)

PPC: 77° → fires → ~709° → relaxes to 441° across its 5 s reload → sustained fire climbs the residual 441 → 674 → 838 → 960 — brushing the 1000° degradation threshold: the authentic fire-discipline game. SRM salvos spike +641K and cross degradation after three. Condensers absorb and pass to the central bank. Neutral run: sensor Ready/100%, mech holds, zero Fail.

Heat consequences (Phase 5.3.11, 2026-07-22)

The weapon-side consequences are wired and VERIFIED under sustained fire:

  • Ballistic gate 1 (@4bbd36): weapon destroyed-state or its own sink at FailureHeat pins full recoil + the unavailable alarm (the NoAmmo roach-motel re-asserts). (Mech-disabled half joins the damage wave.)
  • CheckForJam (@4bbfcc): p = 0.41·T/failT clamped [minJamChance, 1.0], rolled per granted shot against the MUNGA uniform Random. Interim gate: heat-degraded only (stands in for the deferred LiveFireEnabled novice switch AND the trivially-true heatLoad>0 early-out — the exact spurious- cold-jam trap the BT411 port documented). Verified: SRM6s jam at T≈1200-1320 (24%+ rolls past the 1000° threshold), stay jammed.
  • Emitter hard gate (@4baab9): FailureHeat drops the beam state + charge each frame; SELF-RECOVERING — conduction cools the sink below 2000° and the weapon resumes from Loading. Verified: the PPC settles into an emergent thermal duty cycle (fire at ~1930° → spike 2562° → shutdown → cool → refire); the lasers oscillate around ~2200°.

The experience gates + the coolant wave (Phase 5.3.12, 2026-07-22)

The player-experience gates are LIVE (the workflow dossier recovered every body from the BT411 RE):

  • HeatSink::HeatModelActive() (binary FUN_004ad7d4): owner mech → Entity::GetPlayerLink()BTPlayer::heatModelOn — ON only for veteran / expert. Gates BOTH phases of HeatSinkSimulation (absorb/conduct AND UpdateCoolant) and every weapon fire-heat dump. Standard mode has NO heat consequences — authentic. NULL player link reads ON (dev-permissive).
  • ProjectileWeapon::LiveFireEnabled(): playerLink → simLive — 0 only for novice, whose ballistics never jam. CheckForJam is now the AUTHENTIC form: LiveFireEnabled early-out + the heatLoad <= 0 early-out (with the model off heatLoad stays 0, so standard mode never jams either) + the minJamChance floor (a veteran+ launcher carries its authored ~5% cold jam chance — the interim heat-degraded gate is retired).
  • BTPlayer's flag block renamed to its TRUE semantics (simLive @0x25c, heatModelOn @0x260, advancedDamageOn/2, levelFlag26c/270, experienceLevel) — the ctor rows were already binary-accurate; only the names/types were the old "display toggles" misread.

THE LOAD-BEARING FIX found on the way: Mech ctor SetValidFlag(). Every 1995 entity ctor tail marks itself valid; ours didn't — and 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. The experience gates read a NULL player forever (permissive ON) until this one authentic line was restored.

The coolant system (all bodies authentic from the dossier, constants byte-verified: HeatLoadScale 0.002 → heatLoad now lives in the [0,1] band, HeatEqualizeEpsilon 1e-4, draw floor 0.0025 / ON 0.003, draw gate 1e-4):

  • UpdateCoolant (@004adbf8): coolantDraw = own structural damage × heatLoad (an UNDAMAGED mech leaks nothing — the bars stay full), drain, top-up via the virtual DrawCoolant, and the active-flag hysteresis.
  • BalanceCoolant (@004ada94): full clamp chain, called from ConductHeat.
  • DrawCoolant base = 0; Reservoir overrides it as THE SOURCE (@4af3b0).
  • Reservoir reconstructed (@4af408): capacity overlays thermalCapacity (streamed 20), squirtMass 0.5, CoolantSimulation + InjectCoolant (@4aefa4, the full flush distribution: condensers → weapons → heatables → master with intentional duplicate weighting, squirts only leave the tank, the chill only cools). BT_FORCE_FLUSH=1 dev hook (the cockpit InjectCoolant button joins the message wave). Deferred: the AggregateHeatSink master-bank attach + the 0.05×count capacity rescale.
  • Condenser refrigeration (@4ae4d8): valveState inits 1, massScale = (1 own damage) × refrigerationFactor (streamed 3.0) clamped ≥ 1 each frame — the condenser pumps heat as if 3× hotter, chilling itself below ambient; that chill is what cools the weapons. INTERIM: coolantFlowScale stays 1.0 (authentic streams 0 + the MoveValve/BTRecomputeCondenserValves share assignment — the cockpit-button wave; zeroing it now would strand condenser heat).

VERIFIED headlessly, both directions of the experience lever (TESTNOV.EGG = TEST.EGG with experience=novice):

  • NOVICE: 212 fire cycles ALL at T=77, zero jams, zero shutdowns, coolant static — the heat model authentically absent.
  • EXPERT: playerLink resolves, the full economy runs (PPC duty cycle now sawing ~650-1200° — the refrigeration pump drains harder than before — 119 shutdowns + 2 authentic jams under forced sustained fire). Zero Fail in both.

Still deferred

The electrical charge model (TrackSeekVoltage / voltage sag / I²R), the mech-disabled halves of the weapon gates (damage wave), the AggregateHeatSink family (the central bank's count/radiator loop + reservoir attach + capacity rescale + its forward-linked drain), the MoveValve / ToggleCooling / InjectCoolant cockpit-button message handlers + BTRecomputeCondenserValves, Myomers movement heat, jam recovery via ResetToInitialState (mission reset).