Files
BT411/game
Joe DiPrimaandClaude Opus 5 c32d02b3cc the heat schematic was lit at every spawn: a dropped x87 tail, and the last #47 derivation
Playtest night 5: "heat damage @ launch is lit up like a Christmas tree",
confirmed by two players at ANY spawn -- so not the respawn-reset bug it was
first filed as.  Two stacked causes, both fixed here.

1. THE RATIO WAS NEVER RECONSTRUCTED.  HeatConnection::Transfer @004c3720 sets
   the colour index a ColorMapper pushes into the palette slot.  The port wrote

       *currentColorIndex = HeatRound(heat->currentTemperature);

   straight into an index whose range is 0..99.  A stone-cold mech sits at ~77
   and the generators idle near 260, so every one of the 24 cmHeat mappers in
   GAUGE/L4GAUGE.CFG (GeneratorA-D, Condenser1-6, HUD, Avionics, Gyroscope,
   Torso, GAUSS, the lasers, SRM6, Myomers) saturated at the hot end from the
   first frame and stayed there.

   The real computation was INVISIBLE in the decomp.  Ghidra renders the tail as
   `uVar1 = FUN_004dcd94();` -- an arg-less __ftol, exactly the carve artifact
   reconstruction-gotchas §19 documents: the x87 expression that left the value
   in ST0 is dropped from the export.  The previous author reconstructed the
   only thing visible and flagged the scaling as unreconciled in a comment.
   Raw disasm @0x4c379a-0x4c37c1 recovers it:

       fld [num] ; fdiv [den]        ; ratio
       fcomp 1.0f ; jbe -> ratio=1   ; clamp
       fmul 99.0f ; call __ftol      ; -> 0..99

   i.e. "how close to failure am I", not a raw temperature.  At spawn that is
   77/2000 -> 4, and the schematic reads cold.

   The two operands come from one of two branches, chosen by a class flag at
   +0x14 that the port did not model at all (ctor disasm @0x4c3682-0x4c36c2):
     HeatableSubsystem (0x50e3ec) -> own temp@0x114 / own failureTemperature@0x11C
     HeatWatcher       (0x50e604) -> WATCHED subsystem's temp@0x114
                                     / the WATCHER's failureTemperature@0x124
     neither                      -> source NULLed, Transfer writes 100
   Note the watcher asymmetry: temperature from the watched subsystem, reference
   from the watcher itself.  Bridged as BTHeatWatcherSample so the gauge TU need
   not include the heat family's headers.

2. THE WATCHER BRANCH COULD NEVER BE SELECTED.  With (1) fixed, HUD, Gyroscope
   and Torso still pinned at 99 reading temp=1.6369e-35 failAt=0 -- uninitialised
   bytes.  HeatWatcher's C++ base had been re-based to MechSubsystem, but its
   DERIVATION chain still said HeatableSubsystem, so every watcher answered "yes,
   I am heat-bearing", took branch one, and read its own watchedLink@0x114 as a
   temperature.  This is the last surviving instance of the #47 bug -- in the
   file that fix's own comment cites as already correct.  The two families are
   disjoint in the binary and the branch test depends on it.

VERIFIED LIVE (solo, F6 to the Heat schematic, BT_HEATGAUGE_LOG=1): mode mask
reached 0x510421 (ModeSecondaryHeat) and the gauge tracks per subsystem rather
than saturating -- GeneratorA 259.5->13, Condenser3 202.0->10, SRM6 182.8->9,
lasers 90-97->4-5, and AmmoBinSRM6_1 (a watcher) resolves its link and reads
182.7->9 through the branch that previously read 1.6369e-35.  Nothing pinned at
99.  Operator confirms the panel is blue, not red.

BT_HEATGAUGE_LOG kept as a permanent env-gated diagnostic: bind-time
classification plus per-sample temp/failAt/ratio/index.  A fresh spawn reading
99 is this regression returning.

Not covered: ColorMapperCritical (cmCrit / ModeSecondaryCritical) already
computes a proper damageLevel*100 ratio and was read, not measured -- if the
Critical page specifically still misbehaves that is a separate lead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 11:28:11 -05:00
..