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

171 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).