Field reports were all "intermittent": "the display buttons aren't always
flashing or lighting up on leaking components" (Oracle), "I'm getting leaks but
no indicators" (Lynx), "I am getting indicators sometimes" (Sauron). It is not
intermittent -- it is per-condenser, and three of the six were wrong.
condenserNumber is 1-BASED (verified live: 'Condenser6' reports 6), which the
lamp table's own comment stated. The guard was `n >= 0 && n < 6` -- a 0-based
bound -- and the table it indexed was missing 0x2C:
condenser 1..3 -> 0x2F 0x2E 0x2D correct
condenser 4 -> 0x2B WRONG (loop 5's button)
condenser 5 -> 0x2A WRONG (loop 6's button)
condenser 6 -> rejected NOTHING flashes
So a leak in loop 6 annunciated nowhere, loops 4-5 lit a neighbour's button, and
loops 1-3 were fine -- which from the cockpit reads exactly as "sometimes".
Condenser N drives cooling-loop N, whose six lamps are the same ones the
coolingLoop1..6 codes already resolve through, so the fix routes condensers
through that verified map (BTFixedLampOf(N-1)) and retires the duplicate,
partly-wrong table rather than patching it.
⚠ PROVENANCE: the retired table cited @0051d058 as byte-verified. That address
holds gauge-type NAME STRINGS, not lamp ids, and a byte search for the six loop
ids as consecutive int32 finds nothing -- so neither the old table nor the new
mapping is byte-verified. The correction rests on three checkable things: the
1-based numbering (live), the guard contradicting its own documented indexing,
and six condensers mapping onto the six cooling-loop lamps of the verified fixed
map. [T2 -- behaviour verified, not byte-grounded.]
Also adds the missing diagnostic on the silent path: an alarm item that matched
its condition but resolved no lamp now names the subsystem and why, instead of
returning quietly. That is what found this, and it immediately surfaced a
SECOND gap for someone to pick up: a destroyed HeatSink (condition 0) resolves
no lamp either, because it is neither Condenser nor Generator nor a
PoweredSubsystem with an aux screen.
Verified (scratchpad/night8/leaklamp.sh, BT_LAMP_LOG):
before [galarm] condition 2 ... sub 'Condenser6' -> NO LAMP RESOLVED
after [galarm] condition 2 ... -> lamp 0x2a FLASH
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
394 lines
17 KiB
C++
394 lines
17 KiB
C++
//===========================================================================//
|
|
// File: btl4galm.cpp //
|
|
// Project: BattleTech Brick: Gauge Renderer Manager //
|
|
// Contents: BTL4GaugeAlarmManager -- the BattleTech override of the MUNGA //
|
|
// GaugeAlarmManager. It supplies the two per-item stream virtuals //
|
|
// that the base class declares pure ("not overridden!"): it knows how //
|
|
// to compile a named cockpit-gauge alarm definition into a memory //
|
|
// stream (CreateGaugeAlarmStreamItem) and how to instantiate a live //
|
|
// GaugeAlarm for a given subsystem/condition from that stream //
|
|
// (ReadGaugeAlarmStreamItem). //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 02/22/96 CPB Initial coding. //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1996, Virtual World Entertainment, Inc. All rights reserved //
|
|
// PROPRIETARY AND CONFIDENTIAL //
|
|
//===========================================================================//
|
|
//
|
|
// RECONSTRUCTED -- IMPORTANT PROVENANCE NOTE
|
|
// ------------------------------------------
|
|
// ⚠ 2026-07-25 (#47): THE CENTRAL CLAIM BELOW IS WRONG. The BTL4 override
|
|
// bodies DO exist in the shipped image, at @004cc148..@004cc2fc -- immediately
|
|
// BELOW the @004cc40c boundary this note drew:
|
|
// @004cc2fc ReadGaugeAlarmStreamItem: stream-reads {trigger level, lamp
|
|
// code}; when the subsystem's alarm level matches, maps the lamp
|
|
// (<0x80 fixed via @004cc148; >=0x80 SUBSYSTEM-RELATIVE eng-page
|
|
// button via @004cc1a0; Condenser @004cc264 / power-family
|
|
// @004cc27c specials), FindLamp @00444c80 on the renderer's lamp
|
|
// map (+0x1c0c8), Lamp::SetAlertState(@00444e64, the FLASH
|
|
// counter), and records the lamp on the GaugeAlarm's lampList.
|
|
// @004cc294 the Create-side sibling.
|
|
// This is the #47 ENG-BUTTON ATTENTION FLASH (jam / bay fire); the authored
|
|
// data survives as the type-31 GaugeAlarmStream in BTL4.RES (exactly 1:
|
|
// ModelList 'mechalrm' 83 -> stream 331). The bodies below ARE now the real
|
|
// re-transcriptions from those addresses (2026-07-25, same day). The
|
|
// original (wrong) note is preserved for the record:
|
|
//
|
|
// btl4galm.obj links after btl4grnd.obj and before btl4vid.obj (BTL4.MAK
|
|
// BTL4_OBJS order). In the shipped optimised image (BTL4OPT.EXE) there is **no
|
|
// distinct, separately-emitted BTL4GaugeAlarmManager override body**:
|
|
// * the only GaugeAlarmManager code in the binary is the MUNGA base class in
|
|
// gaugalrm.cpp (recovered/all/part_006.c, @00448928..@00448f48), including
|
|
// the two "not overridden!" trap virtuals
|
|
// CreateGaugeAlarmStreamItem @00448ab8 (asserts GAUGALRM.CPP:0x71)
|
|
// ReadGaugeAlarmStreamItem @00448ad4 (asserts GAUGALRM.CPP:0x7e)
|
|
// and the base driver helpers that CALL those virtuals
|
|
// GaugeAlarmManager::CreateGaugeAlarmStream @00448af0
|
|
// GaugeAlarmManager::BuildAlarmsForModel @00448d00
|
|
// * no subclass vtable (one whose Create/Read slots point into BattleTech
|
|
// code) appears in vtables.tsv, and no caller constructs a subclass
|
|
// (the base ctors @00448ebd/@00448edc/@00448efb have no external callers).
|
|
// The code region the linker reserved for btl4galm.obj (@004cc40c..@004cdac0)
|
|
// actually holds a 3-D HUD-model builder (PNAME1..8.bgf), i.e. the compiler
|
|
// folded BTL4GaugeAlarmManager's tiny overrides away / it was never instantiated
|
|
// in this build configuration.
|
|
//
|
|
// This file is therefore reconstructed to MATCH THE SURVIVING BTL4GALM.HPP and
|
|
// the base driver's calling contract (the argument order, the GaugeAlarm object
|
|
// it expects, and the "undefined alarm type ... resource not built" diagnostic
|
|
// the base prints when the virtual returns False). Bodies below are BEST-EFFORT
|
|
// (low confidence): they describe what the override must do for the base driver
|
|
// to function, not bytes lifted from this image.
|
|
//
|
|
// Base contract (recovered, high confidence):
|
|
// GaugeAlarmManager::CreateGaugeAlarmStream @00448af0
|
|
// - opens the "gaugeAlarm" sub-resource of a model record (FUN_00404720,
|
|
// "gaugeAlarm" @004eecc9), counts its entries, makes a MemoryStream
|
|
// (0x18), and for each entry calls the virtual
|
|
// this->CreateGaugeAlarmStreamItem(stream, entry_name, entry_data)
|
|
// (entry_name @entry+8, entry_data @entry[1]); if it returns 0 it prints
|
|
// "Model file '<model>' has undefined alarm type '<name>' -
|
|
// resource not built." (strings @004eecd4/@004eece1/@004eecfe)
|
|
// and aborts the build. On success it bakes the stream (FUN_00406f3c).
|
|
// GaugeAlarmManager::BuildAlarmsForModel @00448d00
|
|
// - for each baked alarm record it makes a GaugeAlarm (FUN_004489e8,
|
|
// 0x28 bytes; vtable PTR_FUN_004eed7c; ctor stores entity@+0xc,
|
|
// subsystem@+0x10, condition@+0x14), adds it to the manager's alarm list
|
|
// (this+4, list-insert vtbl+4), then calls the virtual
|
|
// this->ReadGaugeAlarmStreamItem(alarm, entity, subsystem, condition,
|
|
// stream)
|
|
// once per record to configure it.
|
|
//
|
|
// Engine-internal helper map (from the base + the BattleTech alarm bricks):
|
|
// FUN_004489e8 GaugeAlarm ctor FUN_00448a28 GaugeAlarm dtor
|
|
// FUN_00448ebd GaugeAlarmManager base ctor (chains a ChainOf<GaugeAlarm*>)
|
|
// FUN_0040385c Verify(msg,file,line)
|
|
// FUN_004dbb24 DebugStream << (char*) FUN_004db78c DebugStream << (int)
|
|
// DAT_00524e20 DebugStream (warning channel)
|
|
// MemoryStream write/read = (*stream + 0x20)(stream,&v,4) / (*stream+0x1c)(...)
|
|
//
|
|
|
|
#include <bt.hpp>
|
|
#pragma hdrstop
|
|
|
|
#if !defined(BTL4GALM_HPP)
|
|
# include <btl4galm.hpp>
|
|
#endif
|
|
#if !defined(APP_HPP)
|
|
# include <app.hpp>
|
|
#endif
|
|
|
|
|
|
//
|
|
// THE REAL OVERRIDE BODIES (Gitea #47, 2026-07-25) -- re-transcribed from the
|
|
// binary at @004cc294 (Create) / @004cc2fc (Read) + the mapping helpers
|
|
// @004cc108/@004cc148/@004cc1a0/@004cc264/@004cc27c and their data tables
|
|
// @0051cf1c..@0051d084. The previous bodies here were admitted fabrications
|
|
// ("threshold/range/state/flash" types that exist nowhere in the image); the
|
|
// provenance note at the top of this file records why.
|
|
//
|
|
// The STREAM FORMAT (verified against the shipped resource -- BTL4.RES carries
|
|
// ModelList 'mechalrm' id=83 -> GaugeAlarmStream id=331, 11 items):
|
|
// { int32 condition; int32 lampCode; } per item
|
|
// where condition = the MechSubsystem TechStatusType (the @0x50de74 name table:
|
|
// Destroyed 0, Damaged 1, CoolantLeaking 2, Overheating 3, AmmoBurning 4,
|
|
// Jammed 5, BadPower 6) and lampCode = the @0051cf1c vocabulary below.
|
|
//
|
|
// The shipped 'mechalrm' authoring (decoded 2026-07-25):
|
|
// Destroyed -> gotoEngineering + engCooling + engBusMode
|
|
// CoolantLeaking -> gotoEngineering + engCooling
|
|
// AmmoBurning -> gotoEngineering + engEject
|
|
// Jammed -> gotoEngineering + engEject
|
|
// BadPower -> gotoEngineering + engBusMode
|
|
// ("on a jam or bay fire the display eng button for the system flashes" --
|
|
// Cyd's pod testimony, verbatim in the data.)
|
|
//
|
|
|
|
//
|
|
// @0051cf1c -- the lamp-code name vocabulary ({char*, code} pairs, 0-terminated).
|
|
//
|
|
struct BTLampNameEntry { const char *name; int code; };
|
|
static const BTLampNameEntry kBTLampNames[] =
|
|
{
|
|
{ "coolingLoop1", 0x00 },
|
|
{ "coolingLoop2", 0x01 },
|
|
{ "coolingLoop3", 0x02 },
|
|
{ "coolingLoop4", 0x03 },
|
|
{ "coolingLoop5", 0x04 },
|
|
{ "coolingLoop6", 0x05 },
|
|
{ "gotoEngineering", 0x80 },
|
|
{ "engGeneratorA", 0x81 },
|
|
{ "engGeneratorB", 0x82 },
|
|
{ "engGeneratorC", 0x83 },
|
|
{ "engGeneratorD", 0x84 },
|
|
{ "engEject", 0x85 },
|
|
{ "engBusMode", 0x86 },
|
|
{ "engCooling", 0x87 },
|
|
{ "engBackToQuad", 0x88 },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
//
|
|
// The lamp-resolution tables, byte-verified from the image:
|
|
// @0051cf9c quad-page mode masks [12] (aux screens 1..12)
|
|
// @0051cfcc eng-page mode masks [12]
|
|
// @0051cffc quad-select button lamp ids [12] (code 0x80 gotoEngineering)
|
|
// @0051d02c eng-page bank-top lamp ids [12] (codes 0x81.. descend from it)
|
|
// @0051d058 per-condenser lamps (index = condenserNumber @0x1D4, 1-based)
|
|
// @0051d070 per-placement lamps (index = auxScreenPlacement @0x1E0)
|
|
//
|
|
static const int kBTQuadModeMask[12] = { 0x1,0x1,0x1,0x1, 0x20,0x20,0x20,0x20, 0x400,0x400,0x400,0x400 };
|
|
static const int kBTEngModeMask[12] = { 0x2,0x4,0x8,0x10, 0x40,0x80,0x100,0x200, 0x800,0x1000,0x2000,0x4000 };
|
|
static const int kBTQuadLamp[12] = { 0xF,0xD,0xB,0x9, 0x27,0x25,0x23,0x21, 0x7,0x5,0x3,0x1 };
|
|
static const int kBTEngBankTop[12] = { 0xF,0xF,0xF,0xF, 0x27,0x27,0x27,0x27, 0x7,0x7,0x7,0x7 };
|
|
// (the per-condenser lamp table that used to live here is retired: condenser N
|
|
// resolves through BTFixedLampOf(N-1), the same six cooling-loop lamps. #98)
|
|
static const int kBTPlacementLamp[5] = { 0x29, 0x1A,0x1B,0x1C,0x1D }; // [auxScreenPlacement]
|
|
|
|
//
|
|
// @004cc148 -- the fixed (<0x80) map: coolingLoop1..6 -> the heat-display bank.
|
|
//
|
|
static int BTFixedLampOf(int code)
|
|
{
|
|
static const int kFixed[6] = { 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A };
|
|
return (code >= 0 && code < 6) ? kFixed[code] : -1;
|
|
}
|
|
|
|
//
|
|
// @004cc108 -- map an authored lamp NAME to its code.
|
|
//
|
|
static Logical BTLampCodeOf(const char *data, int *out_code)
|
|
{
|
|
for (const BTLampNameEntry *e = kBTLampNames; e->name != 0; ++e)
|
|
{
|
|
if (strcmp(data, e->name) == 0) // FUN_004d4b58
|
|
{
|
|
*out_code = e->code;
|
|
return True;
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static Logical BTLampLog()
|
|
{
|
|
static int s_on = -1;
|
|
if (s_on < 0) s_on = (getenv("BT_LAMP_LOG") != 0) ? 1 : 0;
|
|
return s_on ? True : False;
|
|
}
|
|
|
|
//
|
|
// BTL4GaugeAlarmManager::CreateGaugeAlarmStreamItem -- @004cc294 [T1]
|
|
//
|
|
// iVar1 = FUN_004ac194(name, &condition); // MechSubsystem::LookupStatusType
|
|
// if (!iVar1) return 0;
|
|
// iVar1 = FUN_004cc108(data, &code); // the @0051cf1c vocabulary
|
|
// if (!iVar1) return 0;
|
|
// stream->WriteBytes(&condition, 4); // (*stream+0x20)
|
|
// stream->WriteBytes(&code, 4);
|
|
// return 1;
|
|
//
|
|
// (Bake-side only -- the shipped BTL4.RES already carries the baked stream; this
|
|
// matters when re-baking resources from notation.)
|
|
//
|
|
Logical
|
|
BTL4GaugeAlarmManager::CreateGaugeAlarmStreamItem(
|
|
MemoryStream *mem_stream,
|
|
const char *alarm_name,
|
|
const char *alarm_data
|
|
)
|
|
{
|
|
int condition, code;
|
|
|
|
if (!MechSubsystem::LookupStatusType(alarm_name, &condition)) // FUN_004ac194
|
|
{
|
|
return False; // -> "has undefined alarm type ... resource not built"
|
|
}
|
|
if (!BTLampCodeOf(alarm_data, &code)) // FUN_004cc108
|
|
{
|
|
return False;
|
|
}
|
|
mem_stream->WriteBytes(&condition, sizeof(condition));
|
|
mem_stream->WriteBytes(&code, sizeof(code));
|
|
return True;
|
|
}
|
|
|
|
//
|
|
// BTL4GaugeAlarmManager::ReadGaugeAlarmStreamItem -- @004cc2fc [T1]
|
|
//
|
|
// Read one {condition, lampCode} item; when the item's condition equals the
|
|
// condition being ACTIVATED, resolve the lamp:
|
|
// * Condenser-derived (0x50e4fc) + condition CoolantLeaking(2):
|
|
// lamp = @0051d058[condenserNumber@0x1D4] (its own loop button)
|
|
// * Generator-derived (0x50fb60) + condition CoolantLeaking(2):
|
|
// lamp = @0051d070[auxScreenPlacement@0x1E0] (its radar-rail button)
|
|
// * else code < 0x80: the fixed heat-bank map (@004cc148)
|
|
// * else code >= 0x80: PoweredSubsystem-derived only (0x50f4bc -- the same
|
|
// gate BTGetSubsystemAuxScreen applies) + auxScreen@0x1DC > 0:
|
|
// 0x80 gotoEngineering: lamp = @0051cffc[aux], mode = @0051cf9c[aux]
|
|
// 0x81..: lamp = @0051d02c[aux] - (code - 0x81), mode = @0051cfcc[aux]
|
|
// then FindLamp(lamp, mode) on the renderer lamp manager (@00444c80), start it
|
|
// flashing (Lamp::SetAlertState(True) @00444e64 -- a COUNTER, alarms stack), and
|
|
// record it on the GaugeAlarm lampList so ~GaugeAlarm un-flashes on Deactivate.
|
|
// A lamp code that resolves to no lamp is silently skipped (iVar2 = -1 path).
|
|
//
|
|
// (0x50f4bc = PoweredSubsystem::ClassDerivations, NOT Generator -- pinned via
|
|
// @004b1208 = PoweredSubsystem::TestInstance in the powersub method cluster;
|
|
// 0x50fb60 = the Generator one, adjacent to its message table @0x50fb90.)
|
|
//
|
|
void
|
|
BTL4GaugeAlarmManager::ReadGaugeAlarmStreamItem(
|
|
GaugeAlarm *alarm,
|
|
Entity * /*the_entity*/,
|
|
Subsystem *the_subsystem,
|
|
Enumeration the_condition,
|
|
MemoryStream *mem_stream
|
|
)
|
|
{
|
|
int item_condition = -1, lamp_code = -1;
|
|
mem_stream->ReadBytes(&item_condition, sizeof(item_condition)); // (*stream+0x1c)
|
|
mem_stream->ReadBytes(&lamp_code, sizeof(lamp_code));
|
|
|
|
if ((int)the_condition != item_condition)
|
|
{
|
|
return;
|
|
}
|
|
|
|
extern int BTSubsystemIsCondenser(::Subsystem *sub); // heat.cpp (0x50e4fc)
|
|
extern int BTCondenserNumber(::Subsystem *sub); // heat.cpp (+0x1D4)
|
|
extern int BTSubsystemIsGenerator(::Subsystem *sub); // powersub.cpp (0x50fb60)
|
|
extern bool BTGetSubsystemAuxScreen(::Subsystem *sub, int *screen, int *placement, char *label64); // powersub.cpp (0x50f4bc gate + 0x1DC/0x1E0)
|
|
|
|
int lamp_id = -1;
|
|
int mode = -1; // local_10: stays -1 for the fixed/family paths
|
|
|
|
if (BTSubsystemIsCondenser(the_subsystem))
|
|
{
|
|
if (the_condition == 2) // CoolantLeaking
|
|
{
|
|
//
|
|
// #98 -- "I'm getting leaks but no indicators" / "the display
|
|
// buttons aren't ALWAYS flashing on leaking components".
|
|
//
|
|
// condenserNumber is 1-BASED (verified live: 'Condenser6' reports 6),
|
|
// which the old table's own comment said -- but the guard was
|
|
// `n >= 0 && n < 6`, a 0-based bound. Condenser 6 was therefore
|
|
// REJECTED outright and could never annunciate, and the table it
|
|
// indexed was missing 0x2C, so condensers 4 and 5 flashed the NEXT
|
|
// loop's button instead of their own. Three of six correct, one
|
|
// silent, two lying -- exactly the "sometimes" players reported.
|
|
//
|
|
// Condenser N drives cooling-loop N, whose six lamps are the same
|
|
// ones the coolingLoop1..6 codes resolve to, so use that verified
|
|
// map rather than a second, partly-wrong copy of it.
|
|
//
|
|
int n = BTCondenserNumber(the_subsystem);
|
|
if (n >= 1 && n <= 6)
|
|
lamp_id = BTFixedLampOf(n - 1); // loop N -> its own button
|
|
}
|
|
}
|
|
else if (BTSubsystemIsGenerator(the_subsystem))
|
|
{
|
|
if (the_condition == 2) // CoolantLeaking
|
|
{
|
|
int screen = 0, placement = -1;
|
|
BTGetSubsystemAuxScreen(the_subsystem, &screen, &placement, 0);
|
|
if (placement >= 0 && placement < 5)
|
|
lamp_id = kBTPlacementLamp[placement];
|
|
}
|
|
}
|
|
else if (lamp_code < 0x80)
|
|
{
|
|
lamp_id = BTFixedLampOf(lamp_code);
|
|
}
|
|
else
|
|
{
|
|
int screen = 0, placement = -1;
|
|
if (BTGetSubsystemAuxScreen(the_subsystem, &screen, &placement, 0) // PoweredSubsystem gate
|
|
&& screen > 0)
|
|
{
|
|
int aux = screen - 1; // +0x1DC - 1
|
|
if (aux < 12)
|
|
{
|
|
if (lamp_code == 0x80) // gotoEngineering
|
|
{
|
|
lamp_id = kBTQuadLamp[aux];
|
|
mode = kBTQuadModeMask[aux];
|
|
}
|
|
else // engGeneratorA..engBackToQuad
|
|
{
|
|
lamp_id = kBTEngBankTop[aux] - (lamp_code - 0x81);
|
|
mode = kBTEngModeMask[aux];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lamp_id < 0)
|
|
{
|
|
// DIAGNOSTIC (#98): this is the SILENT path -- the item matched the
|
|
// condition but no lamp could be resolved for this subsystem, so nothing
|
|
// flashes and nothing was logged. Players see "leaking but no
|
|
// indicator". Name the subsystem and why it fell through, so the
|
|
// mapping gap is visible in a field log instead of being invisible.
|
|
if (BTLampLog())
|
|
DEBUG_STREAM << "[galarm] condition " << (int)the_condition
|
|
<< " code 0x" << std::hex << lamp_code << std::dec
|
|
<< " sub '" << (the_subsystem && the_subsystem->GetName()
|
|
? the_subsystem->GetName() : "?")
|
|
<< "' -> NO LAMP RESOLVED (condenser=" << BTSubsystemIsCondenser(the_subsystem)
|
|
<< " condenserNumber=" << (BTSubsystemIsCondenser(the_subsystem)
|
|
? BTCondenserNumber(the_subsystem) : -1)
|
|
<< " generator=" << BTSubsystemIsGenerator(the_subsystem)
|
|
<< ")" << std::endl << std::flush;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// @004cc2fc tail: FindLamp on the gauge renderer lamp manager, flash it,
|
|
// and record it on the alarm. (DAT_004efc94+0x4c = the gauge renderer.)
|
|
//
|
|
GaugeRenderer *renderer = (GaugeRenderer *)application->GetGaugeRenderer();
|
|
if (renderer == 0)
|
|
{
|
|
return;
|
|
}
|
|
Lamp *lamp = (renderer->GetLampManager() != 0)
|
|
? renderer->GetLampManager()->FindLamp((LampID)lamp_id, (ModeMask)mode)
|
|
: 0;
|
|
|
|
if (BTLampLog())
|
|
DEBUG_STREAM << "[galarm] condition " << (int)the_condition
|
|
<< " code 0x" << std::hex << lamp_code
|
|
<< " -> lamp 0x" << lamp_id << " mode 0x" << mode << std::dec
|
|
<< (lamp ? " FLASH" : " (no lamp registered -- skipped)") << "\n" << std::flush;
|
|
|
|
if (lamp != 0)
|
|
{
|
|
lamp->SetAlertState(True); // FUN_00444e64 -- the flash counter
|
|
alarm->lampList.Add(lamp); // un-flashed by ~GaugeAlarm on Deactivate
|
|
}
|
|
}
|