Files
BT411/context/subsystems.md
T
Joe DiPrimaandClaude Opus 5 df1b4651b3 #137 CALIBRATION SETTLED: the myomer heat math is faithful -- constants read byte-exact
Read the integrator's constants from .rdata rather than inferring them.  Row
` 4b8ee0  5dc30000 0000003f 00000000 0000803f` gives:

    _DAT_004b8ee4 = 0.5f   the kinetic half  (work = mass * |v|^2 * 0.5)
    _DAT_004b8ee8 = 0.0f   the Abs() idiom zero
    _DAT_004b8eec = 1.0f   the (1 - efficiency) complements + gear clamp floor

All three are exactly what the port computes, and the logged terms reproduce
from the authored tuning to the digit:

    work        = 75000 * 54^2 * 0.5                       = 109.3e6
    complement  = 1 - VelocityEfficiency(0.995)            = 0.005
    termKinetic = 109.3e6 * 0.005 * (dt*28 = 0.728)        = 398e3  (logged 399003)

THE ONE DEVIATION IS DELIBERATE AND IS THE FAITHFUL CHOICE.  The binary applies
NO time_slice to the kinetic term (`fVar5 * fVar1`) while climb and accel both
carry param_2 -- a per-frame energy add at the pod's FIXED ~28 Hz.  The port's
`work * (time_slice * 28)` is identical at 28 Hz (dt*28 = 1.0) and holds the same
heat-per-SECOND at any frame rate.  Transcribing it literally would add the full
term once per frame, so at Oracle's measured 170 fps it would inject ~6x the heat
the pod ever did.  Preserving behaviour beats preserving the artifact of a fixed
timestep.  BT_MYO_HZ still brackets the reference rate.

SO #137 IS NOT A CALIBRATION DEFECT.  Heat is QUADRATIC in speed, so v~50 on open
ground after a respawn is ~9x the input of v~10-18 in a fight -- which is why the
overshoot correlates with respawns without being caused by them.  It is also
self-limiting: effectiveness reaches 0, the mech stops, speed falls, it cools.
That is the authentic governor.  Whether the cliff is too punishing for players
is a DESIGN call for the operator, not a fidelity bug.

Recorded in context/subsystems.md so the next reader does not re-litigate the
dt-normalisation as a bug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SgmXGNMXavXiafKXf9MDC
2026-08-09 12:22:15 -05:00

28 KiB
Raw Blame History

id, title, status, source_sections, related_topics, key_terms, open_questions
id title status source_sections related_topics key_terms open_questions
subsystems Subsystems — the factory + the reconstruction waves established PROGRESS_LOG.md §10d; docs/SUBSYS_PLAN.md; docs/VEHICLE_SUBSYSTEMS.md; docs/RESOURCE_AUDIT.md
decomp-reference
reconstruction-gotchas
combat-damage
gauges-hud
subsystem
factory
ClassID
subsystem-roster
PoweredSubsystem
bridge
Myomers coupling (gates on owning-player @mech+0x190); factory capability-roster loops 2-4 still dead (SubProxy stub)

Subsystems

The mech's components (heat/power/weapons/sensor/gyro/torso/myomers/…), built by the Mech-ctor factory. Full per-family plan: docs/SUBSYS_PLAN.md; the engineering-cluster panels: docs/VEHICLE_SUBSYSTEMS.md; the layout audit: docs/RESOURCE_AUDIT.md. ClassID map: decomp-reference §2.

The factory

The Mech ctor switches on the resource ClassID to build each subsystem into the subsystem-roster (subsystemArray@0x128). The case <Name>ClassID LABELS are systematically MISLABELED — the real class is the // FUN_004xxxxx ctor-address comment reconciled via CLASSMAP.md, not the case name (e.g. 0xBBE case="Sensor" is actually AggregateHeatSink). Each real class is wired via a Create<Class>Subsystem(Mech*,int,void*) bridge in the class's own .cpp (do NOT #include the real subsystem headers into mech.cpp — the local stubs collide). Keep the alloc SIZE + special-cache. [T2]

Class hierarchy (two branches, shared only at MechSubsystem)

  • Heat leaf: PoweredSubsystem : HeatSink : HeatableSubsystem : MechSubsystem. Emitter/PPC/ Sensor/Myomers/ProjectileWeapon/MissileLauncher/GaussRifle chain here. Uses 0xC SubsystemConnection
    • 0x54 GaugeAlarm54 (alarm level at +0x14). [T1]
  • Watcher branch: Torso/Gyroscope/Searchlight/ThermalSight/HUD : PowerWatcher : HeatWatcher : MechSubsystem; AmmoBin : HeatWatcher. Uses a 0xC WatchedConnection + 0x54 WatcherGaugeAlarm. [T1] Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER in one build, each static_assert-locked (the P7 alarm-unification: HeatSink 0x1D0 → PoweredSubsystem 0x31C → MechWeapon 0x3F0 → Emitter 0x478). [T2]

Reconstruction waves (state: ALL 20 factory cases wired to real classes — 0xBD3 = the real SubsystemMessageManager since task #7, 2026-07-11; the mapper lives in roster slot 0)

  • WAVE 1 — HeatableSubsystem re-based onto MechSubsystem (de-shadow cascade).
  • WAVE 2 — heat family (Condenser/AggregateHeatSink/Reservoir) + HUD + MechTech (un-swap).
  • WAVE 3 — power bus (Generator/PoweredSubsystem) + Emitter/PPC fire-path (end-to-end fire; heat conducts to the central sink via the linked-sink roster).
  • WAVE 4 — standalone readouts: Sensor/Searchlight/ThermalSight/AmmoBin (de-shim, gate fixes).
    • Searchlight VISUALS complete 2026-08-05 — cockpit fog swap + external spot.bgf beam cone, both MP-replicated; mountSegment@0x1DC identified (= resource segmentIndex, the cone's mount joint — was "commandedOn, role unidentified"). Full story: rendering §SEARCHLIGHT.
    • Searchlight + ThermalSight buttons WIRED 2026-07-25 (#61) — both classes' handler sets were default-constructed blackholes (systemic cause #1, docs/INPUT_PATH_AUDIT.md). Each now chains PowerWatcher::GetMessageHandlers() with its own id-3 ToggleLamp. Verified live: pad 0x14requestedOn 0→1lightState 0→1; pad 0x12requestedOn 0→1thermalActive 0→1.
    • RETRACTED: the "ORIGINAL 1995 latent bug" (old task #63) NEVER EXISTED. It was an artifact of a swapped table attribution: the claim compared Searchlight's Performance (@004b841c, reads requestedOn@0x1E0) against ThermalSight's ToggleLamp (@004b860c, toggles 0x1DC) and concluded "no 0x1DC→0x1E0 bridge, so the lamp can never light". @004b860c is not Searchlight's. Searchlight's own handler is @004b838c (table @0x51117C) and — like every sibling — toggles the field its OWN Performance reads, requestedOn@0x1E0. Each class is internally self-consistent. [T1 throughout. Attribution: un-pooled "ToggleLamp" strings adjacent to their own class names, section_dump.txt:69661-69711. Body: @004b838c is a Ghidra EXPORT GAP (#60) but was recovered by raw disasm of content/BTL4OPT.EXE (scratchpad/dis838c.py) — it toggles 0x1E0, raises updateModel (+0x18) unconditionally, and has NO novice gate (ThermalSight-only).] Consequence: the searchlight→fog swap was NOT inert in the arcade, and making it work in the port is FAITHFUL, not a designer-intent deviation. See open-questions + rendering fog, both corrected.
    • ⚠ Still open on Searchlight: commandedOn@0x1DC (ctor-seeded from subsystem resource +0x28, @004b84dc) has no identified role — it is neither the toggle target nor read by @004b841c, and it measured 21 live (not a boolean), which hints our SubsystemResource +0x28 ≠ the binary's.
    • ⚠ ThermalSight's published attributes are mis-named: we publish "LightState"→thermalActive@0x1D8, but the binary's table @0x511268 has "LightOn"(id 3)→0x1D8 and "LightState"(id 4)→0x1E0 (the alarm). A gauge binding ThermalSight/LightOn currently misses. Not yet changed (gauge-binding change needs its own verification pass).
  • WAVE 5 — Torso (aim twist/elevation; joint-I/O reconstructed; the BLH record disables torso → faithfully no visible twist).
  • WAVE 6 — MYOMER SYSTEM COMPLETE (2026-07-31). The "mover cutover" concept is DEAD — it was built on a misattribution. @004b9550/@004b95b8 (the "ConnectToMover/DisconnectFromMover" bodies) are MechWeapon::ConfigureMappables/ChooseButton — their +0x31C is the weapon TriggerState and **(mech+0x128) is the CONTROLS MAPPER's button roster (proven by the MECHWEAP.CPP assert string + the trigger-edge detector @004b9608 adjacent). ⚠ SAME-DAY CORRECTION (the seek audit): the binary DOES have a dynamic myomer→speed feed — it lives in the un-exported master-perf gap (@0x4a9cf2-0x4a9da4, raw capstone; the same gap that hid the #93 crash block). Per frame: MAX speedEffect@0x31C over the myomer chain (+0x7AC) → mech+0x79Cmapper->speedDemand@0x128 *= it, and at |drive| ≤ 1e-4 (@0x4ab16c) the mapper's turnDemand@0x12C is ZEROED — dead/overheated myomers freeze speed AND turn. The morning's "no feed" verdict was export-gap blindness (text sweeps cannot see un-exported functions; the decisive tool was a raw-image capstone scan for FPU reads of +0x31C). Field truth agrees: Oracle (pod veteran) — seek-4 humanoids ran 182 kph (the manual's printed Super Charged figure), overheat = "freezing up". The port restored the demand scale byte-grounded (mechmppr.cpp): MAX over the chain, UNCLAMPED (gear 4 rides ~1.43× demand into the RegisterMaxOutput cap = SUPERCHARGE), turn-freeze included; damage slows (speedEffect carries 1damage — gitea #75's original premise was RIGHT). The rest of the myomer system, all live in the port:
    1. Heat — the drive-heat integrator (#85, below).
    2. Vital death — myomers are the ONLY vital=1 subsystem in the authored records (BTL4.RES res+0x48 scan): destroying them KILLS the mech (the #80 vital-crit path). This is also the collision ram-death mechanism (myomers are a 0.35-weight rattle target).
    3. The assembly capRegisterMaxOutput @004b8ef0: mech+0x7A0 = max(cap, AvailableOutput(topGear)) = base × ~1.4284, the gait rise-clamp both channels bound against (part_012:12103/12334). Port: each myomer registers per tick (idempotent max(); the 0x358 layout lock leaves no latch room) — state-identical to once-at-assembly. The old per-frame reverseSpeedMax2 = reverseStrideLength heal (which would have clobbered it) is garbage-guarded only now. Mech+0x34C (base) = port reverseStrideLength, Mech+0x7A0 (cap) = port reverseSpeedMax2 — both documented misnomers.
    4. The ENG-page power curve — SeekVoltageResponse, now at the correct absolute scale (OwnerBaseSpeed reads the real base instead of the 1.0 stub).
    5. Electrical sourcing — the dial's REAL stake is TORSO TWIST. A gear's voltage must be <= the generator's measured output ((1 generator damage) × rated, thermal breaker on FailureHeat; there is NO load model — demand never pulls the bus down), or the myomers leave Ready. The Torso is a PowerWatcher on the myomers: TorsoSimulation ZEROES the twist rate while the watched myomers are not Ready (halves it at DegradationHeat) — so an over-high gear on a damaged generator freezes the pilot's AIM until they downshift. On a healthy mech, gears 1-3 are literally identical (heat ratio floors at 1, no speed coupling); gear 4 = double heat + a tighter dropout threshold for nothing but the taller graph — a trap. (A speedDemand *= speedEffect multiplier was removed in the morning commit and RESTORED, byte-grounded and improved, the same day — see the correction above. Verified live: healthy drive=1.0, collision-rattled myomers drive 0.89→0.74 with demand scaling in lockstep.) MYOMER DRIVE HEAT LIVE (#85, 2026-07-31) [T2]. The five Mech motion operands of the drive-heat integrator @004b8d18 were return 0.0f cross-family shims, so the (fully reconstructed) integrator accumulated ratio² · damageGain · 0 every tick — myomers ice cold at any seek (Oracle's night-7 panel-confirmed report). Operands re-grounded from the raw disasm + the decomp and mapped to named members via the complete-TU bridge BTMechMyomerMotionSample (mech4.cpp): +0x1C4 vec = localVelocity.linearMotion; +0x82C vec = localAcceleration.linearMotion (the authentic 15-ring smoothed AccelerationLastFrame); +0x20C = moverMass (the collision divert's cell — the old "motion gain" label was wrong); *(+0x250) = the environment gravity pointer (FUN_00421e2c does vy -= **(+0x250)/tick). Physics: heat += ratio²·(1+dmg)·[0.005·½mv² + 0.2·m·g·|vy|·dt + 0.2·m|v||a|·dt] — kinetic work
    • climb power + acceleration power. The binary fabses v.y (the port's old draft didn't). Authored tuning extracted from BTL4.RES (all 18 mech variants share one record): VelocityEfficiency=0.995, AccelerationEfficiency=0.8, seek gears 0.3/0.5/0.7/0.9999 rec idx 2; myomer thermal profile startT=77 / degradation=1000 / failure=2000 / conductance=1.9e5 / thermalMass=2.5e5. Live-verified (solo arena, BT_MYO_LOG): standstill ≈ 0 generation, hard circling drove T 77→1225 (past degradation) and the coolant loop pulled it back to ~520 equilibrium on slowing. ⚠ Two carried caveats: (1) the kinetic work term has no dt in the binary (per-tick accumulate, a 1995 fixed-frame assumption) → heat/s scales mildly with frame rate; kept verbatim, field-calibrate against pod veterans. (2) the climb term is wired but inert: Mover::localEnvironment is never populated in the port (EnvironmentZone res type 23 unwired), so g reads 0 — see open-questions. ⚠ The registered Performance is the WRAPPER @004b8b9c, not the inner integrator @004b8d18 [T1, fixed 2026-07-28]. The ctor @004b8fec stores [0x511620] into activePerformance, and that pointer resolves to 0x4b8b9c, whose first instruction is call 0x4b0bd0 = PoweredSubsystem::PoweredSubsystemSimulation; @004b8d18 is the drive-heat integrator it then runs. The port had registered the INNER function, so Myomers never advanced its own electrical state machine — and since that machine is the only thing that both enters NoVoltage (source == 0) and walks it back (NoVoltage → Starting → Ready), a Myomers that lost power in the death/reset window stayed there forever. The Torso is a PowerWatcher that MIRRORS its watched subsystem, so torso.cpp:570 held effectiveTwistRate at 0 = Gitea #70, "torso twist stops working after a death/respawn." Measured: 2 dead windows per respawn before, 0 after; pristine missions never showed it. Order is load-bearing — the base call precedes the heat-model gate, and gating first also denied the electrical machine to every non-expert pilot (OwnerAdvancedDamage() is the +0x260 flag, off below veteran). General rule this establishes: when a subsystem's Performance address in a PTR_LAB_* slot does not match the function you reconstructed, check whether the real target is a wrapper that chains the base sim — the whole family does (Generator/PoweredSubsystem lead with HeatSink::HeatSinkSimulation; the Torso's own perf @004b5cf0 leads with UpdateWatch). The full wrapper is now reconstructed [T1/T2, 2026-07-28] — five blocks in binary order: @004b8bab chain PoweredSubsystemSimulation; @004b8bb9 the un-powered self-repair (see below); @004b8c1e republish outputVoltage@0x344 from the resolved source when the alarm is Ready; @004b8c5a republish speedEffect@0x31C = AvailableOutput(clamped V) / OwnerBaseSpeed() — the Mech base speed CANCELS, so speedEffect is a 0..1 fraction of full drive carrying gear ratio, thermal curve and zone damage; @004b8ceb run the inner integrator only when outputVoltage > 0. Verified live: healthy outV=10000 speed=1, un-powered outV=0 speed=0, and 96/96 torso samples at elec=4 across two death/respawn cycles. CORRECTED 2026-07-29 (#80): @004b8bb9 — the un-powered self-repair — is LIVE, in 1995 and now in the port. The 2026-07-28 "dead code in the original too" verdict here was wrong about the original: the frozen-at-0.6 experiment was measuring a PORT bug (the subsystem ctor wrote the zone's armour/scales through the ReconDamageZone proxy at struct offsets +4/+8 instead of the engine members at +0x140/+0x144, so the real damageScale[] stayed zero). The binary ctor (@0x4ac7bb) initializes them from the resource keys WeaponDamagePoints + the five per-type ...DamagePoints; the port now does the same through the engine's named members, and the CSS parses the keys. An un-powered, not-yet-destroyed myomer heals at 0.011 × damageScale[Explosive] per tick. The retracted "cannot damage a subsystem's own zone" rule is superseded — see combat-damage §CORRECTED for the full mechanism (this also revived the crit sink, #80). Also un-stubbed here: Myomers::DamageStructureLevel() returned a hardcoded 0.0f, which pinned AvailableOutput's (1 - damage) factor at 1 — a shot-up myomer drove exactly as well as a fresh one. Now routed to the base bridge GetSubsystemDamageLevel(); measured dmg=0.6 → speed=0.4.
  • WAVE 7 — projectile/missile weapons (byte-exact; flying projectiles are a PORT reconstruction — the 2007 Entity is 0x1BC vs the binary's 0x300, so raw base-offset reads fail).
  • TASK #56 (2026-07-11) — Gyroscope LIVE byte-exact (ctor @004b3778, sizeof 0x3D0 locked; integrators + FUN_004b2980 damage fan-out; joint dispatch from the Mech performance tail @0x4aaf74/83; BT_GYRO_LOG/BT_GYRO_TRACE) — see cockpit-view.
  • DONE (task #7, afefaee) — SubsystemMessageManager 0xBD3 (a damage/explosion consolidation hub cached to Mech[0x10d]=0x434; the factory builds the REAL class and mech.hpp names the slot messageManager — the old controlsMapper mislabel is swept, the real mapper is roster slot 0. NOT the valve/heat-model gate — that's the owning BTPlayer @mech+0x190 carrying the EXPERIENCE-level flags, see experience-levels). [T2]

The watcher electrical chain (task #57, 2026-07-13 — the torso power gate)

The Watcher branch is POWERED indirectly: a watcher WATCHES another roster subsystem and mirrors its electrical state. The full chain, byte-verified [T1]:

  • Data: the model entry WatchedSubsystem=<name> → segment index +2 at resource+0xE4 (HeatWatcher::CreateStreamedSubsystem @004aec54); the ctor @004aeb40 stores it at watcher+0x128 (watchedSubsystem).
  • Bind (factory post-roster loop 1): vtable slot 14 (+0x38) — @004aee2c (HeatWatcher) / @004b1a40 (PowerWatcher/Torso override, byte-identical); Ghidra missed both function starts (recovered from raw bytes; the vtables.tsv rows have GAPS where the exporter skipped slots — dump the exe bytes at vtable+slot*4 when a slot looks missing). Master-gated ((owner->simulationFlags & 0xC)==0 && (flags & 0x100)); binds watchedLink(+0x114).Add( owner->roster[+0x128][watchedSubsystem]). Port: BTWatcherWatchedIndex/BTWatcherBindTarget bridges (heatfamily_reslice.cpp) called from the mech.cpp factory loop.
  • Tick: @004b181c = the REAL PowerWatcher Performance (PTR @0050f5fc → 004b181c) = UpdateWatch(): heat mirror (FUN_004aeac4) + watchdogAlarm.SetLevel(watched->electrical level @+0x278) + brownout downgrade to 1 when gen->outputVoltage(+0x1DC) <= minVoltage(+0x180) × gen->ratedVoltage(+0x1D8). The Torso sims (@004b5cf0/@004b65f8) call it first-line. (@004b1804 is slot-10 ResetToInitialState, NOT the Simulation — old recon mislabel, fixed.)
  • MinVoltageScale = 0.01 — a 10-byte x87 literal at 0x4b1924 (0a d7 a3 70 3d 0a d7 a3 f8 3f); the port had 1.0f, making minVoltage 100× too big → the brownout latched every watchdog at 1.
  • PowerWatcher::GetClassDerivations chains HeatWatcher (real base) — the old HeatableSubsystem stand-in broke IsDerivedFrom(HeatWatcher) for Torso/Searchlight/ThermalSight and silently skipped them in the connect pass.
  • Effect: Torso::ElectricalStateLevel()==Ready(4) un-gates effectiveTwistRate — the MadCat torso twists at its authored 50°/s (±140° limits, roster 17 watching 15 → generator @10000V); the BLH is authentically fixed (horizontalEnabled=0, limits ±0.01°). [T2 live-verified]
  • STILL DEAD: factory loops 2-4 (heatable/weapon/damageable capability rosters) go through the SubProxy stub whose IsDerivedFrom returns 0 — they add NOTHING. See open-questions.

Coolant LEAKS (Gitea #88 fix, 2026-07-31) — the #64 shadow was the single break [T2]

HeatSink::UpdateCoolant (@004adbf8) prices the damage-driven leak: coolantDraw = ownZone->damageLevel × heatLoad, floor 0.0025, and the coolantActive@0x138 hysteresis (ON >0.003) IS the ReportLeak attribute the 19 authored leak watchers (3-note warning) ride. The read is the qualified ENGINE member Subsystem::damageZone@0xE0 — which the port's MechSubsystem ctor never assigned (it filled only its re-declared shadow, gitea #64 / gotcha #1), so zoneDamage pinned 0 and a leak was structurally impossible regardless of damage. Fixed by ALIASING the engine base member to the same zone in both MechSubsystem ctors (mechsub.cpp) — every shadow reader is untouched (same object), the engine base Subsystem::TakeDamage null-AV is disarmed, and the #64 full de-shadow sweep remains the long-term cleanup. Verified live: collision rattle and weapon crits ([critroll]) both drive [cool] leak lines (draw ≈ dmg×heatLoad, level draining, hysteresis arming). Authored damage routing (BTL4.RES): collision rattle targets HeatSinkBank 0.3 / Gyro 0.35 / Torso 0.25 / Myomers 0.35 — Condenser + Reservoir are authored 0 for collisions; weapon crits select via the ZONE's crit-entry list. The central pull-through is ALSO live (same day): HeatSink::DrawCoolant was a return 0 stub; the real base @0x4add00 (disassembled) RECURSES UP the sink linkage — return resolve(linkedSinks)->DrawCoolant(requested × coolantFlowScale@0x15C) — terminating at the Reservoir's supply (@0x4af3b0, already faithfully ported as Reservoir::DrawCoolant: clamp to [0, coolantLevel], drain, return granted). Binary terminal hop = the bank's slot-14 override @0x4ae8b0 resolving its reservoir connection @0x1D8 (the port's helper member — NOT vestigial); the port terminates equivalently via the Reservoir-ctor Attach into the bank's linkedSinks + the Reservoir override (the extra bank hop scales by its ctor-default flowScale 1.0 — a no-op). Verified live ([resdraw]): a destroyed myomer's leak pulled the central Reservoir tank 6.0 → 5.58 over ~30 s — Reservoir/CoolantMass, the cockpit coolant BAR, now visibly drops as a damaged mech bleeds coolant, and DeathReset refills it on respawn.

The coolant FLUSH (Gitea #7, 2026-07-19) — Reservoir InjectCoolant end-to-end [T2 live-verified]

The manual-p24 coolant button (coolant MFD top-right; punch or HOLD): message id 4 "InjectCoolant" on the Reservoir (handler table @0x50e680, one entry → @4aee70 — same per-receiver id space as the Condenser's MoveValve @0x50E52C). Handler @4aee70: novice-lockout gate (FUN_004ac9c8), press (data≥1) → reservoirAlarm.SetLevel(1) gated on coolantLevel@0x12C > 0 (+300 decimal = 0x12C = coolantLevel — the old "currentTemperature" reading was a misdecode), release → level 0; ForceUpdate. While level==1 the CoolantSimulation Performance (@4aef78) runs InjectCoolant @4aefa4 each frame: work list = condenser chain @+0x7cc + weapon chain @+0x7bc + heatable chain @+0x7ac + linkedSinks master (port walks the roster with the same membership tests — chains are SubProxy-dead; condensers/ weapons/bank get the binary's duplicate-visit weighting; watcher-branch members are filtered to HeatSink [T2 guarded]); per sink with flowScale@0x15C≠0: move squirtMass@0x224 × flowScale × dt clamped to [-resLevel, 0] (reservoir drains → the coolant vertBar C Reservoir/CoolantMass drops), sink level clamped [0, capacity], and pendingHeat@0x1C8 += -(sinkE×|Δ|/sinkLvl resE×|Δ|/resLvl) capped at sinkMass@0x154 × res->startingTemperature@0x13C, clamped ≤0 (only cools) — the heat relief rides the existing heat model. Two ctor corrections surfaced: (1) the master gate @4af408 (and @4aeb40 HeatWatcher) reads *(param_2+0x28) = the owner MECH's simulationFlags, not the resource's subsystemFlags (the resource read left the Reservoir Performance unregistered → no drain); (2) (float10)*(int*)(link+0x1d0) is a FILD of the bank's integer HeatSinkCount — capacity = 0.05 (_DAT_004af518) × heatSinkCount × streamed CoolantCapacity (BLH: 0.05×6×20 = 6), not a float reinterpret (which gave ~1e-44 → empty tank). Visual: the binary's mode-1 coolant-effect renderable (FUN_00456a68, built for classID 0xBC0 on the "ReservoirState" attr, part_014.c:5439; tick @part_007.c:8780) spawns psfx 19 = FLUSH.PFX ("Coolant flush", BTDPL.INI:1018, bluish smoke) on the state's 0→1 edge — port: BTSpawnFlushCloud (mech4.cpp) on the same alarm edge, attached emitter at torso height. Audio (CoolantDump layers) rides the ReservoirState watchers. Desktop input: 'H' HELD = Flush (CONTROLS.MAP action; press+release dispatch). Diags: BT_FLUSH_LOG ([flush-tx] press/release, [flush] level drain, cloud spawn), BT_FLUSH_TEST=<frame> scripted ~2 s hold. Verified live (FOGDAY): level 6→0 over ~0.6 s held (≈3-4 short punches to empty — matches the manual), bluish cloud screenshot-confirmed (scratchpad/flush_cloud.png). Replicant note: the state receive @4aeef8 sets the alarm from the state stream — the port's reservoir state replication is not wired, so remote clouds are deferred (solo/master path complete).

Valve boost A/B/C (2026-07-23, field "boosted loop still cooked" audit) [T2 measured]

Blackhawk, sustained BT_AUTOFIRE, 150 s soaks, [heat-t] 5 s cadence. Valve shares are zero-sum (valveState/Σ); detent cycle per press = 1 → 5 → 50 → 0 (CLOSED) → 1 (@4ae464, one past max turns the loop OFF). SRM6s (+ GeneratorB + Avionics) are on Condenser2 ([heat-link] dump; the MFD loop readout matches the heatSinkIndex truth). Results, SRM6_1:

  • boost 2 (share 0.91): plateau ~531, then COOLS under nonstop fire — never jams/fails.
  • balanced (0.167): ~1162 @ 70 s and climbing (the jam band; field jams logged 1043-1469).
  • starved (0.018): ~1440 @ 70 s, slope → the 2000 FailureHeat line. The valve mechanic is REAL and powerful; MoveValve reruns the redistribute on every press (not a placebo), veteran role passes the guard. The field double-failure with "loop 2 boosted" is therefore a DETENT question — most likely one press past max (detent 0 = closed, share 0) — now diagnosable: valve presses log always-on ([valve] ... -> detent N, with a CLOSED warning). Solo clock shim verified the same day (60 s egg self-ends; [mission] solo game clock expired).

The four systemic checks (every subsystem)

See reconstruction-gotchas: (1) shadowing (re-declared engine-base fields), (2) the Wword trap, (3) message-handler chaining, (4) entity validity. Plus resource-struct layout (must mirror the class hierarchy, byte-exact — the RESOURCE_AUDIT found 8 bugs) and object-layout (alias/phantom/interior fields). A +0x128-style owner offset is the ROSTER, not the segment table (the heat-link type confusion that caused a heap-corruption via OOB ConductHeat writes). [T2]

Engineering cluster panels (vehicleSubSystems)

Not a widget — a per-subsystem FACTORY (FUN_004cbaf0) building a status CLUSTER onto one of 12 aux MFD positions, dispatching on classID → HeatSinkCluster/MyomerCluster/EnergyWeaponCluster/ BallisticWeaponCluster. Reads PoweredSubsystem::auxScreenNumber/Placement/Label (res +0x104/8/C) via the BTGetSubsystemAuxScreen bridge. See docs/VEHICLE_SUBSYSTEMS.md + gauges-hud. [T2]

Key Relationships

Myomer drive-heat calibration — VERIFIED FAITHFUL (2026-08-09, #137) [T1]

The integrator @004b8d18 accumulates into pendingHeat@0x1C8: gear² × (1 + X) × [ (1velEff)·|vy|·m·g·dt + (1velEff)·work + (1accEff)·|v|·|a|·m·dt ] with work = mass · |v|² · 0.5. Its constants, read byte-exact from .rdata (section_dump.txt row 4b8ee0 5dc30000 0000003f 00000000 0000803f): _DAT_004b8ee4 = 0.5f (the kinetic ½), _DAT_004b8ee8 = 0.0f (the Abs() idiom), _DAT_004b8eec = 1.0f (the 1 efficiency complements and the gear-ratio clamp floor). All three match what the port computes — the formula and its authored inputs (VelocityEfficiency 0.995, AccelerationEfficiency 0.8, thermalMass 2.5e5, myomers linked Condenser5) are reconstructed correctly.

The one deliberate deviation, and why it is the faithful choice. The binary applies no time_slice to the kinetic term (fVar5 * fVar1) while the climb and accel terms both carry param_2 — it is a per-frame energy add at the pod's fixed ~28 Hz. The port uses work × (time_slice × 28), which is identical at 28 Hz (dt·28 = 1.0) but holds the same heat-per-SECOND at any frame rate. A literal transcription would add the full term once per frame, so at 170 fps it would inject ~6× the heat the pod ever did. BT_MYO_HZ overrides the reference rate for bracketing.

Consequence for #137: the heat model is not miscalibrated. Myomers running away past failT=2000 at sustained top speed is the authentic governor — heat is quadratic in speed (work = m·v²·0.5), so v≈50 on open ground after a respawn is ~9× the input of v≈10-18 in a fight. It is self-limiting: effectiveness hits 0, the mech stops, speed falls, it cools. What players read as "respawned with the heat bar maxed" is that acceleration to top speed, not a failed reset (combat-damage · the reset itself is verified: T == startingTemperature at every reset). Whether the cliff is too punishing is a DESIGN call, not a fidelity defect.