From 047780297160ca6486e2655fb62ae5872aa03c2b Mon Sep 17 00:00:00 2001 From: arcattack Date: Tue, 21 Jul 2026 00:09:48 -0500 Subject: [PATCH] KB: document the 3 per-weapon loop/generator lamp databinding bugs context/gauges-hud.md: new section recording the color-drop + shadow-field + attribute-table-shift fixes for the btploop/btpbus lamps (commit e634709), with the lesson that gauge value feeds must read named members through a complete-type bridge, not AttributePointerOf+ResolveLink. Notes the eng-page GeneratorVoltageConnection as the remaining sibling on the old path. Co-Authored-By: Claude Opus 4.8 (1M context) --- context/gauges-hud.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/context/gauges-hud.md b/context/gauges-hud.md index cc35e49..61f554c 100644 --- a/context/gauges-hud.md +++ b/context/gauges-hud.md @@ -136,6 +136,37 @@ log, `[ctrlmap] installing` ×2) — the tree then rebuilds bound to the NEW mec `Remove(0)` flushes each gauge (`Update(gaugeRate_A)`, GAUGREND.cpp:3465) before deleting it, so gauge CONNECTIONS may read the freed mech once more (plain reads; Execute stays SEH-guarded). +## Per-weapon panel loop/generator lamps — 3 stacked databinding bugs (2026-07-21) [T2 render-verified] +Each `SubsystemCluster` (@004c8140) per-weapon MFD panel draws two image-strip lamps in the +TEMP/STATUS area: **cooling-loop number** (`btploop.pcc`, frames OFF/1..6, `AnimatedSubsystemLamp` +@004c70a4, fed by `CoolingLoopConnection`→`BTCoolingLoopFrame`) and **generator letter** +(`btpbus.pcc`, frames OFF/A..D, `AnimatedSourceLamp` @004c7160, fed by `PowerSourceConnection`). +These are the "4 A / 1 B / 5 D" boxes in the reference. Both rendered COMPLETELY BLANK; three +independent bugs stacked (all fixed, commit e634709): +1. **Color drop** — both lamp ctors dropped the bg/fg color params the binary passes (bg=0xff, + fg=0, same as the sibling temp bar) and hardcoded `0,0`, so `OneOfSeveral::Execute` did + `SetColor(0)`+`DrawBitMapOpaque(0)` = black-on-black. Restored `0xff,0`. +2. **[[shadow-field]] trap (gotcha #2)** — `AnimatedSubsystemLamp`/`AnimatedSourceLamp` each + RE-DECLARED `int selected;` while already inheriting it from `OneOfSeveral` (@0xAC). The + connection's `&selected` bound the derived shadow copy; `OneOfSeveral::Execute` read the base + @0xAC (always 0) → every lamp stuck on frame 0 ("OFF"). Removing the redeclarations (so + `selected` resolves to the inherited member) fixed the loop NUMBER. +3. **Attribute-table shift (gotcha #8 / [[attribute-pointer]])** — the generator lamp resolved its + source via `ResolveLink(AttributePointerOf(subsystem,"InputVoltage"))`, but the `BT_DEV_GAUGES` + audio attribute rows shifted the chained attribute ids so `AttributePointerOf` no longer landed + on `voltageSource@0x1D0` → the link resolved to 0 (OFF) even though the master + `PoweredSubsystem` ctor DID bind `voltageSource` to its Generator (`[busattach]` showed + bound=1). Fix: new `BTPowerSourceFrame(subsystem)` bridge (powersub.cpp) reads the NAMED member + via `PoweredSubsystem::ResolveVoltageSource()` and returns `Generator::generatorNumber` + (@0x1E0), bypassing the attribute table — same pattern as `BTCoolingLoopFrame`. The lamp caller + now passes `subsystem_in` (not the `InputVoltage` attribute slot). **Lesson: gauge value feeds + must read named members through a complete-type bridge, NOT `AttributePointerOf`+`ResolveLink` + — the attribute table's chained ids are unstable under the audio rows.** The eng-page + `GeneratorVoltageConnection` (evolt voltage bar, btl4gau2.cpp:1737) still uses the same + `AttributePointerOf("InputVoltage")` path and is the remaining sibling to convert. +Diagnostics retained (env `BT_LOOP_LOG`): `[loopfeed]` (BTCoolingLoopFrame) + `[busfeed]` +(BTPowerSourceFrame) print each lamp's resolved frame + source. + ## The secondary screen's THREE views (Damage / Critical / Heat) — mode-gated [T0/T1/T2] The `sec` port stacks three mode-gated mech-schematic layers at offset (50,0) over the always-on radar/heading/speed/messageBoard (`Secondary1`): **Damage** (`ModeSecondaryDamage`,