sec-surface phantoms: #48 wrap lit idx-254 art in live armor colors

Playtester report: (1) the CONTROL MODE stack showed filled boxes around the
inactive MID/ADV entries, (2) a phantom block between the HEADING dial and the
ARMOR rosette.  Root-caused [T1]:

  The #48 translation-table cycle-fill (6bb03ae, 2026-07-25) mapped
  out-of-range art indices in-plane as (index mod 2^bits) -- so art index 254
  landed on plane slot 62 = the LIVE colorMapperMultiArmor right-armor damage
  slot.  And idx-254 art EXISTS: every SMODE.PCC frame fills the INACTIVE
  mode-box interiors with 254, and BTSEC1.PCX carries a stray 52x13 idx-254
  bar at port (199-250,101-113) between the heading dial and the rosette (a
  scratch duplicate of the rosette quadrant bars).  Both lit up in the
  current right-armor color (adpal ramp green/orange/red) on EVERY render
  path.  On the shipped machine those regions rendered BLACK (the garbage
  entry's low plane bits were 0), which is why the 2026-07-19 smode audit --
  run before the cycle-fill landed -- verified CORRECT.

FIX (BuildSecondaryTranslation): map [2^bits..255] to translationTable[0]
  (plane BACKGROUND) -- the authentic on-screen result, same no-leak
  guarantee.  Verified on both desktop paths: MID/ADV back to authored
  borders+text (idx 5/9), no interior fills; the phantom bar gone; the BAS
  badge and the live armor rosette (in-range slots 60-63) untouched.

KB: gauges-hud #48 REFINED addendum; GAUGE_COMPOSITE audit row 33 corrected
  (binding is ControlsMapper/ControlMode, not DisplayMode) + re-verification
  note.

(The companion glass-token palette-generation fix lives on glass-panel-perf --
it depends on the dirty-skip code there.  The two branches touch disjoint
hunks and merge in either order.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-10 10:51:44 -05:00
co-authored by Claude Opus 4.8
parent 773595b347
commit b59e2ac040
3 changed files with 34 additions and 47 deletions
+16 -40
View File
@@ -414,14 +414,6 @@ Verified live: bay fire → lamp 0xD (the LRM's select button) flashes 0x37 + en
on detonation/purge. Diagnostics: `BT_LAMP_LOG` → `[techstat]`/`[galarm]`/`[lamp]`. Details +
the four load-bearing fixes en route: [[open-questions]] + [[decomp-reference]] §GaugeAlarm.
**These conditions are ROUTINE and SELF-CLEARING [T2, field-verified 2026-08-08].** A `SET` is an
operating state, not a fault: every laser volley trips Overheating (cond 3) and clears it on
cooldown (33× on one LLaser in a single match), and BadPower (cond 6) flickers whenever
simultaneous draw browns the bus. Across a full match every subsystem's SET/CLEARED counts are
balanced — nothing latches. So a lamp flashing after a respawn is the mech *operating*, not a
failed reset; diagnose from the SET/CLEARED balance, never from a lone SET. Full census + the
#137 post-mortem it settled: [[decomp-reference]] §TechStatus.
## ConfigMapGauge (the weapon panel's trigger-config joystick) — LIVE via LinkToEntity (2026-07-21)
The per-weapon btjoy.pcc joystick image + 4 cm_* state lamps (off/other/only/both) showing,
for each mappable fire button (Pinky/ThumbLow/Trigger/ThumbHigh), whether THIS panel's weapon
@@ -557,38 +549,6 @@ and every instrument is now live [T2]:**
transcription color bug caught by a period reference screenshot, 2026-07-09; same for the
bottom bowtie carets @4569-4570); pegs at 1200 with no target; the DISPLAYED range slides at
**500 m/s** toward the true pick range (HudSimulation :5652 [T1]).
**The HudSimulation tuning constants, read off .rdata 2026-08-08 [T1]** (`section_dump.txt`
rows ` 4b7ec0 8be55dc3 0000403f 0000803f 0000c842` / ` 4b7ed0 00000000`) —
`_DAT_004b7ec4` = **0.75f**, `_DAT_004b7ec8` = **1.0f**, `_DAT_004b7ecc` = **100.0f**,
`_DAT_004b7ed0` = **0.0f**, `_DAT_004b7f90` = **0.0f**. ec4/ec8 are the fire-control **LOCK**
limits (own HUD host zone < 0.75 damage, targeted zone < 1.0 — so a shot-up cockpit drops to
"target held, no lock", and a dead zone can't be re-locked); ed0 is the shared **zero** in the
range-slide `Abs()` idiom (the 500 is an immediate `0x43fa0000`, NOT a global). `hud.cpp` had
carried all five as 0.0f/500.0f stand-ins under guessed names ("SegmentTempLimit … heat
threshold for HUD page visibility" was neither heat nor page visibility) — corrected in place,
with the live lock/slide implementation staying in mech4.cpp's targeting step, which had both
thresholds right all along.
**THE CARET CAN DIE FOR THE WHOLE SESSION — #147, fixed 2026-08-08 [T2 code, T3 field link]:**
`sShownRange` (mech4's targeting step) is a **function-level static** — one cell per process,
shared by every mech, carried across drops. **NaN is absorbing** in `step = trueRange
sShownRange; sShownRange += step`, and neither the producer's clamps nor
`BTReticleRenderable::Draw`'s (`range < minRange` / `range > maxRange`) catch it — both
comparisons are **false for NaN**. A poisoned value therefore reaches `AddPoint`/`ConcatMatrix`,
and the caret + its bar become **degenerate geometry that stops rendering while the static tick
marks keep drawing** — the exact reported symptom ("no range finder on this drop", ticks there,
caret gone), sticky until relaunch. Fixed four ways: re-seed on mech change (not on respawn —
that reuses the entity, and the binary doesn't reset the readout either), a producer NaN trap, a
NaN-safe consumer clamp falling back to the 1200 peg, and — the reason no log could settle it —
**`BT_RANGE_LOG` now prints the caret's actual input** (`[range] caret input shown=`). It never
did before: `BT_RANGE_LOG` instrumented the PICK (#4) and `[target]`'s `range=` is a *separate*
locally-recomputed Sqrt in the weapon-range check, so grepping field logs for NaN found nothing
because the poisoned variable was never printed. Causal link to the field report is INFERENCE.
**GAP — the 100 m RANGE BIAS is NOT reconstructed [T1 read, unimplemented]:** HudSimulation
subtracts `_DAT_004b7ecc` (100.0f) from `RangeToTarget@0x1EC` **every frame while the timed
flag @0x22C is set**, accumulating @0x21C by `time_slice` until it reaches @0x1D8, then clearing
both. The port's targeting step (mech4.cpp) does the slide but never the bias, so whatever
in-game state sets @0x22C currently produces no range offset. Trigger for @0x22C not yet
identified — see [[open-questions]].
**VERDICT (Gitea #4, 2026-07-20): the "range slides in/out crazily while walking" report is
AUTHENTIC behavior, not a bug [T2 measured].** Per-frame `BT_RANGE_LOG` traces (mech4.cpp, with
an independent Möller-Trumbore cross-check `BTGroundRayHitExact` in btvisgnd.cpp) on scripted
@@ -823,6 +783,22 @@ pattern across entries [2^bits..255] (high-index art degrades to its
index-mod-2^bits colour IN-PLANE, can never leak). The 1995 binary ships the
SAME 64-entry fill and relied on 6-bit art discipline -- garbage is not a
preservable behaviour, so the cycle-fill is a guarded PORT deviation.
**REFINED 2026-08-10 [T1] -- the cycle-fill itself made phantoms.** The
in-plane wrap mapped art index 254 onto plane slot 62 = the LIVE
`colorMapperMultiArmor` right-armor damage slot, and idx-254 art EXISTS:
every SMODE.PCC frame fills the INACTIVE control-mode box interiors with 254
(active border/text idx 9 yellow, inactive borders idx 5 orange-red), and
BTSEC1.PCX carries a stray 52x13 idx-254 bar at port (199-250, 101-113) --
between the heading dial and the armor rosette, geometrically a scratch
duplicate of the rosette quadrant bars. Both lit up in the current
right-armor colour (adpal ramp green/orange/red) on EVERY render path --
playtester-reported as "red boxes around MID/ADV" + "a block between Armor
and Heading". On the shipped machine those regions rendered BLACK (the
garbage entry's low plane bits were 0 -> in-plane index 0), so the fill now
maps [2^bits..255] to `translationTable[0]` (plane BACKGROUND) -- authentic
on-screen result, same no-leak guarantee. NB the 2026-07-19 audit verified
smode BEFORE the 07-25 cycle-fill landed, which is why the row said CORRECT.
Post-fix: **0 leaks over 60s** on the same probe; sim3 3-pod regression clean.
**The tripwires are DEFAULT-ON in every build** (BT_PLANE_AUDIT=0 opts out): the
plane-leak trap, plus an out-of-bounds draw-start trap in `buildDestPointer`