Gitea #47 COMPLETE: the ENG-button attention FLASH is live (jam / bay fire) -- MechTech status scan + BTL4GaugeAlarmManager + lamp chain, all from the binary
The pod behaviour Cyd described -- "on a jam or bay fire the display eng button
for the system flashes" -- is authored data + a five-stage chain, now running:
MechTech::TechnicalAssistance (@004ad33c, per frame)
edge-scans every monitored subsystem's GetStatusFlags() 7-bit condition
mask (TechStatusType: Destroyed 0, Damaged 1, CoolantLeaking 2,
Overheating 3, AmmoBurning 4, Jammed 5, BadPower 6)
-> Start/StopEntityAlarmMessage (@00436688 id 7 size 0x20 / @004366b8 id 8
size 0x1C; broadcast @004364e4; port shape: direct
Start/StopEntityAlarmImplementation calls on the gauge renderer)
-> GaugeAlarmManager::Activate(alarmModel) -- alarmModel = MechTech+0x100 =
the 'mechalrm' ModelList (id 83) -> SearchList(83, type 31) -> the baked
GaugeAlarmStream (id 331, 11 items {condition, lampCode}), decoded:
Destroyed -> gotoEngineering + engCooling + engBusMode
CoolantLeaking -> gotoEngineering + engCooling
AmmoBurning -> gotoEngineering + engEject
Jammed -> gotoEngineering + engEject
BadPower -> gotoEngineering + engBusMode
-> BTL4GaugeAlarmManager::ReadGaugeAlarmStreamItem -- THE REAL BODY, from
@004cc2fc + helpers @004cc108/148/1a0/264/27c + tables @0051cf1c..0x51d084
(gotoEngineering 0x80 = the subsystem's QUAD-SELECT bezel button via
lamp[aux]/mode[aux] tables; 0x81+ descend the eng-page bank; Condenser /
Generator specials on CoolantLeaking; <0x80 = the heat-bank fixed map).
btl4galm.cpp's old bodies were admitted fabrications and its provenance
note ("no override body exists in the image") was wrong -- corrected.
-> LampManager::FindLamp (@00444c80) -> Lamp::SetAlertState (@00444e64, a
COUNTER so stacked alarms hold the flash) -> L4Lamp::NotifyOfStateChange
emits RIO flashFast states 0x37/0x13 (== T0 L4LAMP.cpp:234-239) -> the
pod's physical lamps AND the glass panels (PadRIO IS rioPointer there).
FOUR load-bearing defects found and fixed en route -- each independently fatal
to the feature:
1. HeatableSubsystem's Derivation chained Subsystem directly, SKIPPING
MechSubsystem (written before the WAVE-1 re-basing) -- so EVERY subsystem
on both family branches failed IsDerivedFrom(MechSubsystem), which is
exactly MechTech's monitor filter: the status scan watched NOTHING.
2. MechSubsystem::GetStatusFlags was non-virtual (binary: vtable slot 12) --
the scan's MechSubsystem* call bound statically to the base tier and the
weapon bits could never surface.
3. ProjectileWeapon::GetStatusFlags sat in a Ghidra export gap -- raw-disasm
@004bbf88: base | 0x20 (weaponAlarm==5 Jammed) | 0x10 (linked bin
cookOffArmed@0x18C AmmoBurning). The port had "defer to base".
4. Mech::Reset's respawn sweep blanket-cast every roster entry to
MechSubsystem and called RespawnRepair -- wrong for the Subsystem-level
entries (MechTech 0xBDC, SubsystemMessageManager 0xBD3). On MechTech the
recon damageZone slot lands on its subsystemMonitors chain head: NULL
while the scan was broken (fix #1's bug MASKED this one), but the moment
the monitors populated, RespawnRepair virtual-called through a
SubsystemMonitor as if it were a DamageZone -> load-time crash in
StateIndicator::SetState (caught with cdb; call [edx+14h] on code bytes).
The sweep now filters IsDerivedFrom(MechSubsystem) before the cast.
Also: GUID identities pinned -- 0x50f4bc = PoweredSubsystem::ClassDerivations
(via @004b1208 = its TestInstance; btl4gau2's two "Generator" comments were
wrong, swept), 0x50fb60 = Generator's. A shadow-field instance documented for
the deferred de-shadow: MechSubsystem::damageZone re-declares the PUBLIC engine
Subsystem::damageZone (SUBSYSTM.h:159) -- mechtech's naive `sub->damageZone`
read the never-written engine member (0 monitors again); now reads the recon
member via GetDamageZoneProxy(). Logged in open-questions.
Wiring: BTL4GaugeRenderer now constructs + assigns the BTL4GaugeAlarmManager
(the base ctor NULLs it and Activate Check()s it); MechTech's Report*/
StatusMessageSink stubs are real; alarmModel confirmed baked (= 83) at runtime.
VERIFIED LIVE (scratchpad/lampflash.py, BT_LAMP_LOG chain trace):
[techstat] MechTech id 32 monitors 29 subsystems, alarmModel 83
[techstat] LRM15_1 condition 4 SET (alarmModel 83) <- bay fire armed
[galarm] condition 4 code 0x80 -> lamp 0xd mode 0x1 FLASH
[lamp] 0xd <- 0x37 (FLASHING) <- the select button
[galarm] condition 4 code 0x85 -> lamp 0xb mode 0x4 FLASH <- engEject
[techstat] LRM15_1 condition 4 CLEARED <- detonation clears
[techstat] AmmoBinLRM15_1 condition 0 SET <- bin Destroyed
Regressions: baytest PASS (bay fire kills), baypurge PASS (purge extinguishes),
sim3 3-pod brawl PASS with ZERO crashes (6 kills, organic heat-route bay fires,
respawns clean through the new sweep filter).
Env: BT_LAMP_LOG ([techstat]/[galarm]/[lamp]). KB: gauges-hud (the flash
section), decomp-reference (the GaugeAlarm closure + tables + GUIDs),
open-questions (built-note + the de-shadow deferred item), btl4gau2 comment
sweep. checkctx CLEAN.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5ae4410914
commit
6f5a264835
@@ -207,10 +207,17 @@ MechTech::MechTech(
|
||||
for (int seg = entity->GetSubsystemCount() - 1; seg >= 0; --seg)
|
||||
{
|
||||
Subsystem *sub = entity->GetSubsystem(seg);
|
||||
// #47 SHADOW-FIELD TRAP (gotcha #1): the naive `sub->damageZone`
|
||||
// resolves to the PUBLIC engine Subsystem::damageZone
|
||||
// (SUBSYSTM.h:159) -- which the reconstruction NEVER WRITES; the
|
||||
// recon zone lives on the re-declared MechSubsystem member @0xE0.
|
||||
// Testing the engine one rejected every subsystem (0 monitors).
|
||||
// Read the recon member through its accessor. (The re-declaration
|
||||
// itself is logged in open-questions as a deferred de-shadow.)
|
||||
if (
|
||||
sub != 0
|
||||
&& sub->IsDerivedFrom(MechSubsystem::ClassDerivations) // FUN_0041a1a4(**sub[3], 0x50de2c)
|
||||
&& sub->damageZone != 0 // damageable segment (segment bit 0x8)
|
||||
&& ((MechSubsystem *)sub)->GetDamageZoneProxy() != 0 // damageable segment (segment bit 0x8)
|
||||
)
|
||||
{
|
||||
SubsystemMonitor *monitor =
|
||||
@@ -218,6 +225,21 @@ MechTech::MechTech(
|
||||
subsystemMonitors.Add(monitor); // (*this[0x39].vt[1])(...)
|
||||
}
|
||||
}
|
||||
|
||||
// #47 diag: how many subsystems the status scan actually watches, and
|
||||
// which alarm table it will Activate with. A monitor count of ~2 here
|
||||
// betrays the roster-order hazard (the ctor walk sees only the slots
|
||||
// built BEFORE MechTech in the stream).
|
||||
if (getenv("BT_LAMP_LOG"))
|
||||
{
|
||||
int n = 0;
|
||||
ChainIteratorOf<SubsystemMonitor*> it(&subsystemMonitors);
|
||||
for (it.First(); it.GetCurrent() != 0; it.Next()) ++n;
|
||||
DEBUG_STREAM << "[techstat] MechTech id " << subsystem_id
|
||||
<< " monitors " << n << " subsystems (roster count "
|
||||
<< entity->GetSubsystemCount() << "), alarmModel "
|
||||
<< alarmModel << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -462,12 +484,21 @@ Logical
|
||||
//
|
||||
|
||||
//
|
||||
// @0050e248-era global: the status / MFD message sink == *(DAT_004efc94 + 0x38).
|
||||
// @0050e248-era global: the status / MFD message sink == *(DAT_004efc94 + 0x38)
|
||||
// = the RENDERER MANAGER whose renderer chain (+0x10) FUN_004364e4 broadcasts
|
||||
// Start/StopEntityAlarm messages to (ids 7/8, sizes 0x20/0x1C).
|
||||
//
|
||||
// PORT SHAPE (Gitea #47): the port has no renderer-manager broadcast -- the only
|
||||
// alarm consumer is the gauge renderer (Renderer::Start/StopEntityAlarmMessage-
|
||||
// Handler just forwards to Start/StopEntityAlarmImplementation, RENDERER.cpp:574/
|
||||
// 601, both T0) -- so the sink is the gauge renderer and Report* below call the
|
||||
// implementations directly. Faithful semantics, direct delivery (the same shape
|
||||
// the port uses for StartEntityEffect).
|
||||
//
|
||||
void*
|
||||
MechTech::StatusMessageSink()
|
||||
{
|
||||
return 0; // TODO(cross-family): *(theApplication + 0x38) -- HUD status sink
|
||||
return (application != 0) ? (void *)application->GetGaugeRenderer() : 0;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -480,32 +511,49 @@ Time
|
||||
}
|
||||
|
||||
//
|
||||
// FUN_004366b8 (build "status cleared" message) + FUN_004364e4 (dispatch).
|
||||
// FUN_004366b8 = Renderer__StopEntityAlarmMessage ctor (id 8, size 0x1C) +
|
||||
// FUN_004364e4 = the renderer broadcast. Port shape: direct implementation
|
||||
// call on the gauge renderer (see StatusMessageSink). [Gitea #47]
|
||||
//
|
||||
void
|
||||
MechTech::ReportStatusCleared(
|
||||
void * /*sink*/,
|
||||
Mech * /*owner*/,
|
||||
MechSubsystem * /*sub*/,
|
||||
int /*type*/
|
||||
void *sink,
|
||||
Mech *owner,
|
||||
MechSubsystem *sub,
|
||||
int type
|
||||
)
|
||||
{
|
||||
// TODO(cross-family): build + dispatch the cockpit "status cleared" message.
|
||||
if (getenv("BT_LAMP_LOG"))
|
||||
DEBUG_STREAM << "[techstat] " << (sub ? sub->GetName() : "?")
|
||||
<< " condition " << type << " CLEARED\n" << std::flush;
|
||||
if (sink != 0)
|
||||
((GaugeRenderer *)sink)->StopEntityAlarmImplementation(
|
||||
owner, sub, (Enumeration)type);
|
||||
}
|
||||
|
||||
//
|
||||
// FUN_00436688 (build "status set" message, carries alarmModel) + FUN_004364e4.
|
||||
// FUN_00436688 = Renderer__StartEntityAlarmMessage ctor (id 7, size 0x20:
|
||||
// {msg 0xC | EntityID 8 | subsystem | condition | resourceID}) + FUN_004364e4.
|
||||
// resourceID = MechTech's alarmModel (this+0x100) -- the 'mechalrm' ModelList
|
||||
// (id 83) whose list resolves to the type-31 GaugeAlarmStream (id 331) inside
|
||||
// GaugeAlarmManager::Activate's SearchList. [Gitea #47]
|
||||
//
|
||||
void
|
||||
MechTech::ReportStatusSet(
|
||||
void * /*sink*/,
|
||||
Mech * /*owner*/,
|
||||
MechSubsystem * /*sub*/,
|
||||
int /*type*/,
|
||||
ResourceDescription::ResourceID /*alarm_model*/
|
||||
void *sink,
|
||||
Mech *owner,
|
||||
MechSubsystem *sub,
|
||||
int type,
|
||||
ResourceDescription::ResourceID alarm_model
|
||||
)
|
||||
{
|
||||
// TODO(cross-family): build + dispatch the cockpit "status set" message.
|
||||
if (getenv("BT_LAMP_LOG"))
|
||||
DEBUG_STREAM << "[techstat] " << (sub ? sub->GetName() : "?")
|
||||
<< " condition " << type << " SET (alarmModel " << alarm_model
|
||||
<< ")\n" << std::flush;
|
||||
if (sink != 0)
|
||||
((GaugeRenderer *)sink)->StartEntityAlarmImplementation(
|
||||
owner, sub, (Enumeration)type, alarm_model);
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
|
||||
Reference in New Issue
Block a user