From d006b40927acb110989e0736af5301b675e6ce79 Mon Sep 17 00:00:00 2001 From: arcattack Date: Mon, 6 Jul 2026 17:14:54 -0500 Subject: [PATCH] gauges: reconstruct cmCrit -- subsystem-critical tint; ColorMapper family complete [widget recon 5] Reconstructs ColorMapperCritical -- tints a schematic colour by a subsystem's operational state (the "critical" secondary display mode): - CriticalConnection (@004c3598/@004c3610): src==0 -> 0; src->simulationState==1 (DestroyedState) -> 100; else the subsystem's own damage-zone damageLevel*100. - ColorMapperCritical::Make/ctor (@004c3ddc/@004c3e40): FindSubsystem by name + wire the CriticalConnection. Resolved the subsystem damageZone@0xE0 shadow: MechSubsystem::damageZone is declared ReconDamageZone*, but the assignment (mechsub.cpp: damageZone = (ReconDamageZone*)new DamageZone(...)) shows the pointer IS a real engine DamageZone -- so cast it back and read damageLevel. Added public accessors GetSimulationState()/GetDamageZoneProxy() to MechSubsystem (those fields are protected; mirrors how HeatConnection reads the public currentTemperature). Completes the ColorMapper family (cmHeat, cmArmor, colorMapperMultiArmor, cmCrit). Verified: parses, builds, all subsystems resolve (0 warnings), no /FORCE unresolved, combat un-regressed (TARGET DESTROYED, 0 crashes), stable. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/GAUGE_COMPOSITE.md | 16 +++- game/reconstructed/btl4gaug.cpp | 142 +++++++++++++++++++++++++++++--- game/reconstructed/btl4gaug.hpp | 3 + game/reconstructed/btl4grnd.cpp | 1 + game/reconstructed/mechsub.hpp | 9 ++ 5 files changed, 157 insertions(+), 14 deletions(-) diff --git a/docs/GAUGE_COMPOSITE.md b/docs/GAUGE_COMPOSITE.md index 7934093..77707bc 100644 --- a/docs/GAUGE_COMPOSITE.md +++ b/docs/GAUGE_COMPOSITE.md @@ -327,9 +327,19 @@ widget — the table is cumulative across all registered gauges. **LESSON (add t corruption that surfaces in an INNOCENT later alloc, right after registering a gauge, is the interpreterTable overflow — the Insert bounds-`Verify` is dead at DEBUG_LEVEL 0.** -**Next increments (priority order from the map):** `cmCrit` (ColorMapperCritical -- needs the subsystem -`damageZone@0xE0` shadow resolved; CriticalConnection = state==1?100:Round(subsys->damageZone->damageLevel*100)), -then the +**✅ Increment 5 — `cmCrit` (ColorMapperCritical) DONE — the ColorMapper family is COMPLETE.** Tints a +schematic colour by a subsystem's operational state (the "critical" secondary display mode). Reconstructed +`CriticalConnection` (@004c3598/@004c3610: `src==0 → 0`; `src->simulationState==1` [DestroyedState] `→ 100`; +else the subsystem's own damage-zone `damageLevel × 100`) + `ColorMapperCritical::Make`/ctor (@004c3ddc/ +@004c3e40 — `FindSubsystem` by name + wire the CriticalConnection). **Resolved the subsystem `damageZone@0xE0` +shadow:** the recon declared `MechSubsystem::damageZone` as a `ReconDamageZone*`, but the assignment +(`mechsub.cpp: damageZone = (ReconDamageZone*)new DamageZone(...)`) shows the pointer IS a real engine +`DamageZone` — so cast it back and read `damageLevel`. Added two public accessors to `MechSubsystem` +(`GetSimulationState()`, `GetDamageZoneProxy()`) since those fields are protected (mirrors how HeatConnection +reads the public `currentTemperature`). Verified: parses, builds, all subsystems resolve (0 warnings), no +`/FORCE` unresolved, combat un-regressed (TARGET DESTROYED, 0 crashes), stable. + +**Next increments (priority order from the map):** the `ArmorZoneConnection`/`MultiArmorConnection` classes reconstructed); then the attribute-table wave (`vertBar`/ `segmentArcRatio` speed/`GeneratorCluster` — need `AttributePointers[]` on Mech/HeatableSubsystem, a separate pass); the XL items (`map`/`vehicleSubSystems`/`PlayerStatus`) last. The POD path needs the FULL 23-entry diff --git a/game/reconstructed/btl4gaug.cpp b/game/reconstructed/btl4gaug.cpp index 806278d..c95c58e 100644 --- a/game/reconstructed/btl4gaug.cpp +++ b/game/reconstructed/btl4gaug.cpp @@ -271,6 +271,51 @@ void } +// +// CriticalConnection (@004c3598 ctor / @004c3610 Transfer) -- drives a +// ColorMapper's colour index from ONE subsystem's operational state. Used by +// ColorMapperCritical. Transfer: no subsystem -> 0 (blank); subsystem DESTROYED +// (simulationState == 1) -> 100 (full critical tint); else the subsystem's own +// damage-zone damageLevel (0..1) as a 0..100 percentage (@0xE0 -> @0x158 * 100). +// +class CriticalConnection : public GaugeConnection +{ +public: + CriticalConnection(int *destination, Subsystem *source) + : GaugeConnection(0), // FUN_00444124(this,0) + subsystem(source), + currentColorIndex(destination) + {} + +protected: + void Update(); // override + + Subsystem *subsystem; // @0x10 source + int *currentColorIndex; // @0x14 destination +}; + +void + CriticalConnection::Update() +{ + if (subsystem == NULL) + { + *currentColorIndex = 0; + return; + } + MechSubsystem *sub = (MechSubsystem *)subsystem; + if (sub->GetSimulationState() == 1) // DestroyedState -> full critical tint + { + *currentColorIndex = 100; + return; + } + // else: the subsystem's own damage zone (the proxy IS a real DamageZone). The + // binary reads @0xe0 unconditionally; guard NULL (not every recon subsystem has + // its zone wired) -> 0, matching an intact/undamaged reading. + DamageZone *dz = (DamageZone *)sub->GetDamageZoneProxy(); + *currentColorIndex = (dz != NULL) ? HeatRound(dz->damageLevel * 100.0f) : 0; +} + + //########################################################################### //########################################################################### // File-private GaugeConnection subclasses @@ -685,20 +730,95 @@ ColorMapperMultiArmor::~ColorMapperMultiArmor() // ColorMapperCritical @004c3ddc Make / @004c3e40 ctor //########################################################################### // -// @004c3ddc -- Make: allocate (0x6c) and construct with the four resource -// strings (DAT_00514b50..) and name "ColorMapperCritical". +// Tints one schematic colour by a subsystem's operational state (the "critical" +// secondary display mode). CFG shape (L4GAUGE.CFG:143): +// cmCrit( rate, mode, colourSlot, paletteA, paletteB, subsystemName ) +// e.g. cmCrit(H, ModeSecondaryCritical, 32, adpal.pcc, adpal2.pcc, GeneratorA); // -// @004c3e40 -- ctor: ColorMapper base (vtable PTR_FUN_00518d44). Resolves the -// named subsystem via Entity::FindSubObject (FUN_0041f98c); warns -// "ColorMapperCritical warning: subsystem ... does not exist" if absent. -// AddConnection(new CriticalConnection(¤tColorIndex, subsystem)) -// (connection ctor FUN_004c3598, 0x18 bytes). + +MethodDescription + ColorMapperCritical::methodDescription = + { + "cmCrit", + ColorMapperCritical::Make, + { + { ParameterDescription::typeRate, NULL }, // rate ID + { ParameterDescription::typeModeMask, NULL }, // mode mask + { ParameterDescription::typeColor, NULL }, // hardware colour slot + { ParameterDescription::typeString, NULL }, // palette name A + { ParameterDescription::typeString, NULL }, // palette name B + { ParameterDescription::typeString, NULL }, // subsystem name + PARAMETER_DESCRIPTION_END + } + }; + // -// CriticalConnection::Transfer @004c3610: -// no subsystem -> *destination = 0; -// subsystem +0x40 == 1 -> *destination = 100; (fully operational) -// else -> *destination = Round(); +// @004c3ddc -- Make. // +Logical + ColorMapperCritical::Make( + int display_port_index, + Vector2DOf /*position*/, + Entity *entity, + GaugeRenderer *gauge_renderer + ) +{ + ParameterDescription *p = methodDescription.parameterList; + + ColorMapperCritical *gauge = (ColorMapperCritical *)operator new(0x6c); // FUN_00402298(0x6c) + if (gauge != NULL) + { + new (gauge) ColorMapperCritical( // FUN_004c3e40 + p[0].data.rate, p[1].data.modeMask, + (L4GaugeRenderer *)gauge_renderer, + display_port_index, // graphics port number + p[2].data.color, // colour slot + entity, + p[3].data.string, // palette A + p[4].data.string, // palette B + p[5].data.string, // subsystem name + "ColorMapperCritical"); + } + return True; +} + +// +// @004c3e40 -- ctor: ColorMapper base + a CriticalConnection on the named subsystem. +// +ColorMapperCritical::ColorMapperCritical( + GaugeRate rate, + ModeMask mode_mask, + L4GaugeRenderer *renderer, + int graphics_port_number, + int color_index, + Entity *entity, + const char *palette_a, + const char *palette_b, + const char *subsystem_name, + const char *identification_string +): + ColorMapper( // FUN_004c37dc + rate, mode_mask, renderer, graphics_port_number, + color_index, palette_a, palette_b, identification_string) +{ + Subsystem *subsystem = entity->FindSubsystem(subsystem_name); // FUN_0041f98c + if (subsystem == NULL) + { + DebugStream << "ColorMapperCritical warning: subsystem " + << subsystem_name << " does not exist\n"; + } + + CriticalConnection *connection = + (CriticalConnection *)operator new(0x18); // FUN_004c3598 (0x18 bytes) + if (connection != NULL) + new (connection) CriticalConnection(¤tColorIndex, subsystem); + AddConnection(connection); +} + +ColorMapperCritical::~ColorMapperCritical() +{ + // base-dtor chain (~ColorMapper -> ~Gauge) releases the connection + palettes. +} //########################################################################### // ColorMapperHeat @004c3f08 Make / @004c3f6c ctor *** ANCHOR *** diff --git a/game/reconstructed/btl4gaug.hpp b/game/reconstructed/btl4gaug.hpp index 9cd5ab9..f176a2c 100644 --- a/game/reconstructed/btl4gaug.hpp +++ b/game/reconstructed/btl4gaug.hpp @@ -184,6 +184,9 @@ public ColorMapper { public: + // Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "cmCrit". + static MethodDescription methodDescription; + static Logical Make(int, Vector2DOf, Entity *, GaugeRenderer *); // @004c3ddc ColorMapperCritical( // @004c3e40 GaugeRate, ModeMask, L4GaugeRenderer *, int, int, diff --git a/game/reconstructed/btl4grnd.cpp b/game/reconstructed/btl4grnd.cpp index f4446bb..2d3b064 100644 --- a/game/reconstructed/btl4grnd.cpp +++ b/game/reconstructed/btl4grnd.cpp @@ -133,6 +133,7 @@ MethodDescription &HeadingPointer::methodDescription, // "headingPointer" -- compass needle + heading &ColorMapperArmor::methodDescription, // "cmArmor" -- per-zone armor-damage tint &ColorMapperMultiArmor::methodDescription, // "colorMapperMultiArmor" -- worst-of-8-zones tint + &ColorMapperCritical::methodDescription, // "cmCrit" -- subsystem-critical-state tint &BTL4ChainToPrevious }; diff --git a/game/reconstructed/mechsub.hpp b/game/reconstructed/mechsub.hpp index 0e5615f..0233841 100644 --- a/game/reconstructed/mechsub.hpp +++ b/game/reconstructed/mechsub.hpp @@ -230,6 +230,15 @@ class Damage; Logical IsDamaged(); // @0x4ac9c8 (bus-state query) + // Critical-state readouts for ColorMapperCritical (CriticalConnection): + // the raw simulation state (DefaultState=0 / Destroyed=1 / Exploding=2) and + // the subsystem's own damage zone (a real DamageZone behind the proxy type; + // set in the ctor via `new DamageZone(...)`, mechsub.cpp). + int + GetSimulationState() const { return simulationState; } + ReconDamageZone * + GetDamageZoneProxy() const { return damageZone; } + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Internal helpers //