diff --git a/context/gauges-hud.md b/context/gauges-hud.md index 4bfd10d..d3c5756 100644 --- a/context/gauges-hud.md +++ b/context/gauges-hud.md @@ -826,3 +826,34 @@ classes (PlayerStatus/pilotList/...) with the config interpreter, which let more of the authored page furniture parse + draw than before -- the leaking high-index pixmaps rode in with that. [T3 for this correlation detail; the leak itself and its fix are T2 live-verified.] + + +## Coolant-leak LAMP ROUTING (#172, night-16 hunt 2026-08-13) [T1 resolver + T2 field] +The leak lamp chain is INTACT -- every staged leak all night (35 events, 4 machines, incl. the +"fatal" case) asserted its lamp the SAME FRAME as the [techstat] cond-2 SET. The field reports +were three different presentation facts: +- **Generator leak -> the radar-rail generator button ONLY** (kBTGeneratorLamp @0051d070; both + authored item codes 0x80+0x87 collapse to that one lamp, mode 0xffffffff = the ALWAYS-ACTIVE + sentinel, never mode-gated). NO MFD carries any generator-leak indicator -- binary-authentic + (@004cc2fc). In glass surround that rail cell renders at the minimumStrip floor (~6px, + L4RIOBANK.cpp) -- Elengil's fatal lamp FLASHED for 28.4s, effectively invisible. FIX (glass- + only deviation, #154-class): the ALERT BLOOM -- an alternating lamp paints a grown halo + (+8px) under the surfaces in BTDrawCockpitPanels; only the outward part shows. Receipt: + [lamppx] alert ON/OFF with cell size, per edge. +- **Condenser (loop) leak -> the per-loop button** (@0051d058, 1-based '2F 2E 2D 2B 2A 29'; + the 0x2c gap is AUTHENTIC -- 0x2c belongs to the DISTINCT coolingLoop set of the code<0x80 + path). Also always-active. No gate, immediate flash. +- **Weapon leak (AFC50 family) -> PAGE-SCOPED lamps only** (quad-select + its eng-page button): + the mode gate (@00444e64 == LAMP.cpp) skips the notify while the page is hidden and the + page-entry force-notify delivers it when the player pages there -- "flash appeared at the + second voice line" = the player paging to engineering after voice loop #1. AUTHENTIC; + documented for testers, not fixed. Coolant-cooled guns leak in PAIRS (weapon bit + serving + condenser bit) -- one page-gated lamp, one always-active loop lamp. +- **The VOICE is a LOOPING sequence** (0E4ACE50, tempo 160): Start on the coolantActive 0->1 + edge, self-repeats, Stop on 1->0 (the #136 model REFINED: edge-started, level-looped, + edge-stopped; hysteresis flap = restart). Voice repetitions carry NO edge information -- + the field testers used the voice as a clock and mis-dated the lamp. Receipt: [seqloop] + START/STOP, always-on for looped sequences. +- Authored no-lamp facts (from the mechalrm item census, [galm-item]): conditions 1 (Damaged) + and 3 (Overheating) have NO lamp items for ANY subsystem; cond 0 (Destroyed) resolves lamps + only for weapon-slot subsystems. A heat alarm never flashes a cockpit button -- authored. diff --git a/context/open-questions.md b/context/open-questions.md index 2bcc7e3..bffe156 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -1235,5 +1235,13 @@ consistent with internals-only, see #103). Logs: `scratchpad/night16/` + Oracle re-descend re-entrancy (zone 18 x7 re-rolls, own ticket); (iii) incident-(b) "no damage on target": shooter log computed FULL damage (11.76) -- wire/victim-side loss, needs paired matchlogs (fleet-wide BT_DMG_LOG + matchlog next night would make this attributable). +- **#172 RESOLVED (2026-08-13, 4-agent hunt): the lamp chain was NEVER broken** -- all 35 + staged leak events flashed same-frame. Three presentation facts: generator lamp = the ~6px + radar-rail strip in glass (fatal case: flashed 28.4s unseen -> ALERT BLOOM deviation); + weapon-leak lamps are page-gated AUTHENTICALLY (the "delayed" flash = the player paging to + engineering); the leak voice LOOPS off one edge (testers' clock was wrong). See + [[gauges-hud]] SSCoolant-leak LAMP ROUTING. Receipts armed: [seqloop], [lamppx]. Residual: + Lynx's loop-6 report unstaged (his machine, no log); Elengil's AFC50 pre-rotation log still + wanted for the pair-leak confirmation. - Nanook lost the coolant MFD glass window, recovered only by config edit -> #76 comment (in-game layout-reset recovery path). diff --git a/engine/MUNGA/AUDSEQ.cpp b/engine/MUNGA/AUDSEQ.cpp index 9df6360..98755ad 100644 --- a/engine/MUNGA/AUDSEQ.cpp +++ b/engine/MUNGA/AUDSEQ.cpp @@ -401,6 +401,17 @@ void << " tempo=" << (int)tempo << " divPerBeat=" << (int)divisionsPerBeat << std::endl; } } + // #172 field receipt, ALWAYS-ON for LOOPED sequences (the alarm family -- + // leak warning, lock beeper; a handful per mission): night-16 proved the + // voice loop is the testers' CLOCK ("it said coolant leak 5-6 times") yet + // no field receipt tied voice to leak edges -- the fatal-case timeline had + // to be reconstructed from matchlog wallclocks. One line per Start/Stop + // EDGE makes voice-vs-lamp timing readable straight from any field log. + // (Looped only: one-shot effects fire constantly and would spam.) + if (isLooped && !isRunning) + DEBUG_STREAM << "[seqloop] START seq=" << (void *)this + << " tempo=" << (int)tempo << "\n" << std::flush; + Check(this); // @@ -438,6 +449,10 @@ void if (!isRunning) return; + if (isLooped) // #172 receipt (see StartSequence) + DEBUG_STREAM << "[seqloop] STOP seq=" << (void *)this + << "\n" << std::flush; + // // Find next on or off event // diff --git a/engine/MUNGA_L4/L4VB16.cpp b/engine/MUNGA_L4/L4VB16.cpp index 1692f91..d9c1aaa 100644 --- a/engine/MUNGA_L4/L4VB16.cpp +++ b/engine/MUNGA_L4/L4VB16.cpp @@ -1180,17 +1180,67 @@ void BTDrawCockpitPanels(LPDIRECT3DDEVICE9 device) CkFill(device, 0, L.viewY, L.viewX, L.viewH, band); // left CkFill(device, L.viewX + L.viewW, L.viewY, L.canvasW - (L.viewX + L.viewW), L.viewH, band); // right - // 2) Button lamp faces (UNDER the surfaces -- only the protruding edge shows). + // 2a) #172 ALERT BLOOM (glass-only deviation, #154-class, user-approved + // family): an ALERTING lamp in a surround strip bank is a ~6px sliver + // (minimumStrip) -- Elengil's fatal GeneratorB leak FLASHED lamp 0x1b + // for 28.4s and nobody saw it (night-16, ticket #172; the pod's + // physical backlit buttons don't have this problem). Draw a GROWN + // halo rect FIRST, under everything: neighbors repaint their own + // faces and the surfaces cover the inward part, so only the outward + // bloom survives -- which respects the "no on-top overlay" rule + // (twice field-broken, see the note below step 3). Fires ONLY while + // the lamp genuinely alternates (flash bits set AND the two levels + // differ), so steady lamps and same-level pulses are untouched. unsigned long tick = GetTickCount(); + const int kAlertBloom = 8; for (int i = 0; i < L.buttonCount; i++) { const BTCockpitBtn &b = L.buttons[i]; - int shade = BTLampBrightnessOf(CkLampState(b.address), tick); + int state = CkLampState(b.address); + if ((state & 0x3) != 0 + && (((state >> 2) & 0x3) != ((state >> 4) & 0x3))) + { + int shade = BTLampBrightnessOf(state, tick); + D3DCOLOR fill, border; + CkLampColors(b.colorClass, shade, b.inert, &fill, &border); + CkFill(device, b.x - kAlertBloom, b.y - kAlertBloom, + b.w + 2 * kAlertBloom, b.h + 2 * kAlertBloom, fill); + } + } + + // 2b) Button lamp faces (UNDER the surfaces -- only the protruding edge shows). + for (int i = 0; i < L.buttonCount; i++) + { + const BTCockpitBtn &b = L.buttons[i]; + int state = CkLampState(b.address); + int shade = BTLampBrightnessOf(state, tick); if (gCkPressed == b.address || gCkLatched[b.address & 0x7F]) shade = 3; D3DCOLOR fill, border; CkLampColors(b.colorClass, shade, b.inert, &fill, &border); CkFill(device, b.x, b.y, b.w, b.h, border); CkFill(device, b.x + 1, b.y + 1, b.w - 2, b.h - 2, fill); + + // #172 paint receipt: one line per ALERT edge per lamp -- the night-16 + // forensics could prove the lamp STATE but had zero visibility into + // pixels ("the seam-to-pixels observability hole"). Cheap: fires only + // when a lamp's alternating-status flips. + { + static unsigned char s_wasAlert[128]; + int alerting = ((state & 0x3) != 0 + && (((state >> 2) & 0x3) != ((state >> 4) & 0x3))) ? 1 : 0; + int slot = b.address & 0x7F; + if (alerting != s_wasAlert[slot]) + { + s_wasAlert[slot] = (unsigned char)alerting; + DEBUG_STREAM << "[lamppx] 0x" << std::hex << b.address + << (alerting ? " alert ON" : " alert OFF") + << " cell=" << std::dec << b.w << "x" << b.h + << "+bloom" << (alerting ? kAlertBloom : 0) + << " at " << b.x << "," << b.y + << " state=0x" << std::hex << state << std::dec + << "\n" << std::flush; + } + } } // 3) Gauge surfaces on top (green-tinted mono / palette sec). Reuses the