Gauges: fix blank per-weapon loop/generator lamps (4A/1B/5D boxes)

The SubsystemCluster per-weapon MFD panels render two image-strip lamps in
the TEMP/STATUS area: the cooling-loop number (btploop, 1..6) and the
generator letter (btpbus, A..D). Both drew as completely empty boxes.
Three stacked databinding bugs, all now fixed:

1. Color drop (btl4gau2.cpp): AnimatedSubsystemLamp/AnimatedSourceLamp
   ctors dropped the bg/fg color params the binary passes (bg=0xff,fg=0,
   same as the neighboring temp bar) and hardcoded 0,0 -> OneOfSeveral::
   Execute did SetColor(0)+DrawBitMapOpaque(0), i.e. black-on-black.
   Restored 0xff,0 (verified vs @004c70a4 / caller SubsystemCluster
   @004c8140).

2. Shadow-field trap (btl4gau2.hpp, gotcha #2): both lamp classes
   re-declared `int selected;` while already inheriting it from
   OneOfSeveral (@0xAC). The CoolingLoop/PowerSource connection wrote the
   derived shadow copy while OneOfSeveral::Execute read the base @0xAC
   (always 0) -> every lamp stuck on frame 0 ("OFF"). Removed the
   redeclarations so &selected binds the inherited member. Loop numbers
   now render (verified: PPC->4, Myomers->5, ERMLaser->1/6, etc.).

3. Attribute-table shift (powersub.cpp + btl4gau2.cpp, gotcha #8): the
   generator-letter 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
   always resolved to 0 (OFF) even though the master PoweredSubsystem ctor
   DID bind voltageSource to its Generator. New BTPowerSourceFrame bridge
   reads the NAMED member via PoweredSubsystem::ResolveVoltageSource() and
   returns Generator::generatorNumber (@0x1E0), bypassing the attribute
   table -- the same pattern as the BTCoolingLoopFrame fix. Generator
   letters now render (verified: PPC_1->GeneratorA="A", Myomers->
   GeneratorD="D", SRM6->GeneratorB="B").

Result: the "4 A / 1 B / 5 D" boxes render with both the loop number and
generator letter, matching the reference. Kept BT_LOOP_LOG-gated
[loopfeed]/[busfeed] feed diagnostics; both pod + glass builds clean.

Awaiting live playtest verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-21 00:08:58 -05:00
co-authored by Claude Opus 4.8
parent c79b9953f9
commit e634709e5d
4 changed files with 120 additions and 14 deletions
+31 -2
View File
@@ -1140,14 +1140,43 @@ Subsystem *CreateCondenserSubsystem(Mech *owner, int id, void *seg)
//===========================================================================//
int BTCoolingLoopFrame(void *subsystem_v)
{
// [loopfeed] diagnostic (env BT_LOOP_LOG): why does the per-weapon cooling-loop
// lamp read 0 (the blank frame)? Logs the outcome + reason for the first ~80
// samples so every weapon prints once.
static int s_loopLog = -1;
if (s_loopLog < 0) s_loopLog = getenv("BT_LOOP_LOG") ? 1 : 0;
static int s_loopN = 0;
int trace = s_loopLog && (s_loopN++ < 80);
Subsystem *sub = (Subsystem *)subsystem_v;
if (sub == 0 || !sub->IsDerivedFrom(*HeatSink::GetClassDerivations()))
{
if (trace) DEBUG_STREAM << "[loopfeed] " << (sub ? sub->GetName() : "(null)")
<< " -> 0 (not a HeatSink)\n" << std::flush;
return 0;
}
HeatSink *sink = (HeatSink *)sub;
if (sink->coolantAvailable != 1) // the binary's src+0x134 gate
{
if (trace) DEBUG_STREAM << "[loopfeed] " << sub->GetName()
<< " -> 0 (coolantAvailable=" << sink->coolantAvailable << ")\n" << std::flush;
return 0;
}
Subsystem *master = sink->linkedSinks.Resolve(); // attr id 3 "HeatSink" + FUN_00417ab4
if (master == 0 || !master->IsDerivedFrom(*Condenser::GetClassDerivations()))
if (master == 0)
{
if (trace) DEBUG_STREAM << "[loopfeed] " << sub->GetName()
<< " -> 0 (linkedSinks EMPTY -- not linked to a condenser)\n" << std::flush;
return 0;
return ((Condenser *)master)->condenserNumber; // master+0x1d4 (loop/frame number)
}
if (!master->IsDerivedFrom(*Condenser::GetClassDerivations()))
{
if (trace) DEBUG_STREAM << "[loopfeed] " << sub->GetName()
<< " -> 0 (linkedSinks='" << master->GetName() << "' is NOT a Condenser)\n" << std::flush;
return 0;
}
int frame = ((Condenser *)master)->condenserNumber; // master+0x1d4 (loop/frame number)
if (trace) DEBUG_STREAM << "[loopfeed] " << sub->GetName() << " -> frame " << frame
<< " (condenser '" << master->GetName() << "')\n" << std::flush;
return frame;
}