From 3a9c7a1b91d79bd7713ed194136769c7787df88d Mon Sep 17 00:00:00 2001 From: Cyd Date: Mon, 10 Aug 2026 10:51:44 -0500 Subject: [PATCH] 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 --- context/gauges-hud.md | 56 +++++++++++--------------------------- docs/GAUGE_COMPOSITE.md | 2 +- engine/MUNGA_L4/L4VB16.cpp | 23 ++++++++++++---- 3 files changed, 34 insertions(+), 47 deletions(-) diff --git a/context/gauges-hud.md b/context/gauges-hud.md index 688c8b9..20cfb92 100644 --- a/context/gauges-hud.md +++ b/context/gauges-hud.md @@ -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` diff --git a/docs/GAUGE_COMPOSITE.md b/docs/GAUGE_COMPOSITE.md index db95a0b..fc484dd 100644 --- a/docs/GAUGE_COMPOSITE.md +++ b/docs/GAUGE_COMPOSITE.md @@ -696,7 +696,7 @@ reconfigure/externalConfigure); parse-skip list EMPTY ([gskip]=0), all 50 attr b | 30 | sec: schematic CRITICAL view (cmCrit) | subsystem simulationState/damage | LIVE this audit: N-cycle shows the full subsystem list (GEN A-D, LOOP 1-6, HUD, SENSORS, GYRO, TORSO, weapons) | T2 | CORRECT | | 31 | sec: schematic HEAT view (cmHeat) | subsystem currentTemperature tint | #6 pixel-verified; re-cycled this audit (mask 0x450421→0x490421→0x510421) | T2 | CORRECT | | 32 | sec: view cycling (N / pod 0x15) | CycleDisplayMode → vtbl+0x4C @4d1ae4 | #6 resolution re-verified live ([mode] display notify 0/1/2) | T2 | CORRECT | -| 33 | sec: CONTROL MODE lamp (BAS/MID/ADV) | ControlsMapper/DisplayMode oneOfSeveralPixInt | attr wave incr.5; M-cycle verified #6 | T2 | CORRECT | +| 33 | sec: CONTROL MODE lamp (BAS/MID/ADV) | ControlsMapper/**ControlMode** oneOfSeveralPixInt (row previously mislabeled DisplayMode — that is the sibling sdspmod) | attr wave incr.5; M-cycle verified #6. **2026-08-10:** this row's verification PREDATED the 07-25 #48 cycle-fill, which then lit the inactive box interiors (art idx 254 → live armor slot 62) — playtester-reported, re-fixed by mapping out-of-range indices to plane background (see context/gauges-hud.md §#48 REFINED); both render paths re-verified against the reference | T2 | CORRECT | | 34 | sec: duck / searchlight button lamps | duckState (ctor-zeroed only) / Searchlight LightOn | duckState writer missing (P3 leftover) | T3 | DEFERRED-FEED (duck); CORRECT (light attr published) | | 35 | sec: messageBoard ticker | StatusMessagePool (NULL stub) + kill ticker strip 0 | 7fc4acb; kill ticker live 2026-07-12, other strips unsurveyed | T2/T3 | DEFERRED-FEED (partial) | | 36 | MFD preset paging (J/K/L, pod RIO banks) | SetPresetMode table @0051dbf0 (little-endian re-decode) | #9; re-verified live this audit: cycles visit EXACTLY the populated set (MFD1: 1,2,4; MFD2: 1-4; MFD3: 1,2) | T2 | CORRECT | diff --git a/engine/MUNGA_L4/L4VB16.cpp b/engine/MUNGA_L4/L4VB16.cpp index cd108c4..dbfc303 100644 --- a/engine/MUNGA_L4/L4VB16.cpp +++ b/engine/MUNGA_L4/L4VB16.cpp @@ -7557,17 +7557,28 @@ void // LEFT AS HEAP GARBAGE and any pixmap pixel >= that count wrote the // garbage's high bits into other displays' planes (the #48 stray // blocks; convicted live by BT_PLANE_AUDIT -- the 480x640 radar - // background carries index 217). Cycle the in-plane pattern across - // the remainder: high-index art degrades to its (index mod 2^bits) - // colour IN-PLANE, and can never leak. (The 1995 binary shipped the - // same 64-entry fill and relied on art discipline; garbage is not a - // preservable behaviour, so this is a guarded PORT deviation.) + // background carries index 217). Map the remainder to the plane + // BACKGROUND (translationTable[0]): high-index art renders as the + // port's background colour in-plane, and can never leak. + // + // REFINED 2026-08-10 (the sec-surface phantoms): the first #48 fill + // cycled the remainder IN-PLANE (index mod 2^bits), which mapped art + // index 254 onto plane slot 62 -- a LIVE colorMapperMultiArmor damage + // slot. SMODE.PCC's inactive control-mode box interiors and a stray + // 52x13 idx-254 bar baked into BTSEC1.PCX (between the heading dial + // and the armor rosette) lit up in the current right-armor damage + // colour on every render path. On the shipped machine those regions + // rendered BLACK (the heap garbage's low plane bits were 0 -> + // in-plane index 0), so background IS the authentic on-screen result. + // (The 1995 binary shipped the same 64-entry fill and relied on art + // discipline; garbage is not a preservable behaviour, so this stays a + // guarded PORT deviation.) { int filled = 1; for (int b = bitMask & 0xFF; b != 0; b &= (b - 1)) filled <<= 1; for (int i = filled; i < 256; ++i) - translationTable[i] = translationTable[i & (filled - 1)]; + translationTable[i] = translationTable[0]; } Check_Fpu(); }