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) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-06 17:14:54 -05:00
co-authored by Claude Opus 4.8
parent fb8c1d6ace
commit d006b40927
5 changed files with 157 additions and 14 deletions
+13 -3
View File
@@ -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
+131 -11
View File
@@ -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(&currentColorIndex, 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(<value>);
// @004c3ddc -- Make.
//
Logical
ColorMapperCritical::Make(
int display_port_index,
Vector2DOf<int> /*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(&currentColorIndex, subsystem);
AddConnection(connection);
}
ColorMapperCritical::~ColorMapperCritical()
{
// base-dtor chain (~ColorMapper -> ~Gauge) releases the connection + palettes.
}
//###########################################################################
// ColorMapperHeat @004c3f08 Make / @004c3f6c ctor *** ANCHOR ***
+3
View File
@@ -184,6 +184,9 @@
public ColorMapper
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "cmCrit".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c3ddc
ColorMapperCritical( // @004c3e40
GaugeRate, ModeMask, L4GaugeRenderer *, int, int,
+1
View File
@@ -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
};
+9
View File
@@ -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
//