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>
171 lines
9.8 KiB
Markdown
171 lines
9.8 KiB
Markdown
# 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·
|
||
(1−exp(−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 ((1−dF)·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).
|