gauges: reconstruct + register the first BT gauge widget (cmHeat) [widget recon 1]
Begins the BT gauge WIDGET reconstruction -- making the cockpit instruments show
live data instead of just frame art. The BT gauge method table
(BTL4MethodDescription[]) was a chain-only stub with zero registered
methodDescriptions, so every BT-specific gauge keyword was parse-skipped.
Registers the first widget, cmHeat (ColorMapperHeat -- tints the heat/critical
schematic palette by a named subsystem's live temperature):
- Added ColorMapperHeat::methodDescription (keyword "cmHeat", param list
R,Md,C,St,St,St matching the binary .data) and registered it in
BTL4MethodDescription[] (chain link stays last).
- Rewired ColorMapperHeat::Make to read methodDescription.parameterList[]
(the interpreter restores each instance's parsed params there) instead of the
earlier unrecovered DAT_* placeholders -- so it uses the real CFG rate/mode/
colour-slot/palette/subsystem-name, and the runtime port arg as the graphics
port.
Registering it exposed the /FORCE trap: two REAL functions in the chain were
undefined (silently stubbed -> would AV once the gauge builds). Reconstructed
both from the decomp:
- ColorMapper::BecameActive (@004c395c): invalidate the cached colour index +
last-written RGB so the next Execute re-pushes the hardware palette.
- ColorMapperHeat::~ColorMapperHeat: empty -- the base chain (~ColorMapper ->
~Gauge) releases the HeatConnection + palettes.
Verified: register->parse->Make->ctor->FindSubsystem("GeneratorA") (resolves,
no "does not exist")->HeatConnection->Execute->palette-push runs end-to-end
under BT_DEV_GAUGES; no parse desync; combat un-regressed (TARGET DESTROYED,
0 crashes). Establishes the registration recipe + the /FORCE-check discipline
for every remaining widget. Details + priority order: docs/GAUGE_COMPOSITE.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b526e92cad
commit
547f25e043
@@ -220,3 +220,47 @@ yet reconstructed (parse-skipped). The separate window is now the **live viewer*
|
||||
monitors. NOT a CFG rename (the CFG uses BT names pervasively + `MUNGA_L4` is shared with RP's aux names).
|
||||
- **Polish:** overlay-plane compositing over `sec`; a pod-accurate RGB-packing toggle; texture atlas
|
||||
(one lock/upload); device-reset recreate hardening for the additional swap chain.
|
||||
|
||||
## Widget reconstruction — the real fix (in progress; mapped by `bt-gauge-widget-recon-map`)
|
||||
|
||||
**Why the surfaces show only frames:** the BT gauge method table `BTL4MethodDescription[]` (btl4grnd.cpp)
|
||||
was a chain-only STUB, and **no BT gauge class registered a `methodDescription`**, so every BT-specific gauge
|
||||
keyword (`cmHeat`/`headingPointer`/`map`/`vertBar`/`GeneratorCluster`/…) failed table lookup and was
|
||||
parse-skipped. The binary's real table @0x51c910 has 23 gauge entries; 19 keywords are actually invoked by
|
||||
this CFG. The reconstructed classes exist in `btl4gaug/gau2/gau3.cpp` but most have prose-only (undefined)
|
||||
ctors/Execute/connection-feeds — only a few chains are fully real code.
|
||||
|
||||
**The registration recipe (per widget, mirrors the engine — `NumericDisplayScalar`, L4GAUGE.cpp:542):**
|
||||
1. Add `static MethodDescription methodDescription;` to the class in its `.hpp`.
|
||||
2. Define `<Class>::methodDescription = { "cfgKeyword", <Class>::Make, { {type,NULL}…, PARAMETER_DESCRIPTION_END } }`
|
||||
in the class's `.cpp`. **The param-type list MUST match the binary's `.data` exactly** — the parser reads
|
||||
one token per entry to the `typeEmpty` terminator (GAUGREND.cpp:1458); a wrong count desyncs the parse.
|
||||
3. Rewire `Make` to read `methodDescription.parameterList[]` (the interpreter restores each instance's parsed
|
||||
params there before calling Make) instead of placeholder statics; the runtime port arg (`display_port_index`)
|
||||
is the graphics port, NOT a param slot.
|
||||
4. Register `&<Class>::methodDescription` in `BTL4MethodDescription[]` (btl4grnd.cpp), **chain link LAST**.
|
||||
|
||||
**⚠ THE /FORCE TRAP (the load-bearing gotcha): register a widget ONLY when its whole Make→ctor→feed→every-
|
||||
vtable-slot chain is REAL code.** A registered class instantiates its vtable; any prose-only/undefined virtual
|
||||
(dtor, `BecameActive`, a connection ctor) is `/FORCE`-stubbed to the image base and AVs the moment it's called.
|
||||
The build "succeeds" (exit 0) and lies — **grep the link log for `unresolved external` on the class's symbols**
|
||||
(filter out the known dead `mech3.obj` `DefaultData`/`CreateStreamedSubsystem` offline-factory externals).
|
||||
|
||||
**✅ Increment 1 — `cmHeat` (ColorMapperHeat) DONE (commit pending).** The first registered BT gauge widget.
|
||||
Pure wiring proved the whole pipeline: added the `methodDescription` (`R,Md,C,St,St,St`, keyword `cmHeat`),
|
||||
rewired `Make` to `parameterList[]`, registered it. Registering it EXPOSED two `/FORCE`-stubbed real functions
|
||||
in the chain (the map had missed them) — reconstructed both from the decomp: `ColorMapper::BecameActive`
|
||||
(@004c395c — invalidates the cached colour index + last RGB so the next Execute re-pushes the palette) and
|
||||
`ColorMapperHeat::~ColorMapperHeat` (empty — base chain releases the HeatConnection + palettes). Verified:
|
||||
register→parse→Make→ctor→`FindSubsystem("GeneratorA")` (resolves — no "does not exist")→HeatConnection→
|
||||
Execute→palette-push runs end-to-end; combat un-regressed (TARGET DESTROYED, 0 crashes); the sec/heat
|
||||
schematic zones are now tinted by the REAL subsystem `currentTemperature` (stable tint since temp is stable —
|
||||
the visible-animation win is `headingPointer` next). **Method established for every remaining widget.**
|
||||
|
||||
**Next increments (priority order from the map):** `headingPointer` (rotating compass + numeric heading —
|
||||
reconstruct from prose `btl4gaug.cpp:644`, reads owner quaternion directly, no attribute table/connection
|
||||
needed — the visible money-shot); then `cmArmor`/`colorMapperMultiArmor` (armor schematic, needs 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
|
||||
table with real ctors (the dev tolerant-skip is the on-ramp, not the finish).
|
||||
|
||||
@@ -304,6 +304,20 @@ ColorMapper::~ColorMapper()
|
||||
// base ~Gauge -- FUN_00444360
|
||||
}
|
||||
|
||||
//
|
||||
// @004c395c -- ColorMapper::BecameActive. On (re)activation, force the next
|
||||
// Execute (@004c3980) to re-push the hardware palette: invalidate the cached
|
||||
// colour index + the last-written RGB so the "unchanged" fast-out can't skip the
|
||||
// write (the surface may have been cleared/redrawn while the gauge was inactive).
|
||||
//
|
||||
void
|
||||
ColorMapper::BecameActive()
|
||||
{
|
||||
currentColorIndex = -1; // this[0x15] @0x54
|
||||
previousColorIndex = -1; // this[0x16] @0x58
|
||||
previousRed = previousGreen = previousBlue = 0xFF; // this[0x19] @0x64-0x66
|
||||
}
|
||||
|
||||
//
|
||||
// @004c3980 -- the palette push. Clamp the driving value to a [0,255] colour
|
||||
// index, choose the active palette (flashing alternates the two when
|
||||
@@ -423,41 +437,57 @@ void
|
||||
// DAT_00515020 port, DAT_00515064/0a8/0ec palette/slot/subsystem-name) and the
|
||||
// class name "ColorMapperHeat" (0x5184c4).
|
||||
//
|
||||
|
||||
//
|
||||
// Unrecovered .data resource pool used by ColorMapperHeat::Make (the gauge's
|
||||
// configured rate/mode/port/colour/palette names). The concrete bytes were not
|
||||
// recovered from the optimised image; declared here as typed placeholders so the
|
||||
// construction path compiles. (BEST-EFFORT -- real values live in l4gauge.cfg.)
|
||||
// The gauge's configured parameters come LIVE from the config interpreter (the
|
||||
// CFG "cmHeat" primitive) via methodDescription.parameterList -- the interpreter
|
||||
// restores each instance's parsed params into this scratch list before calling
|
||||
// Make (the engine pattern, cf. NumericDisplayScalar::Make L4GAUGE.cpp:569).
|
||||
// The earlier DAT_* placeholders (unrecovered .data) are gone.
|
||||
// CFG shape (verified L4GAUGE.CFG:182):
|
||||
// cmHeat( rate, modeMask, colourSlot, paletteA, paletteB, subsystemName )
|
||||
// e.g. cmHeat(H, ModeSecondaryHeat, 32, heatpal.pcc, heatpal2.pcc, GeneratorA);
|
||||
//
|
||||
static int DAT_00514f98 = 0; // GaugeRate
|
||||
static int DAT_00514fdc = 0; // ModeMask
|
||||
static int DAT_00515020 = 0; // graphics port number
|
||||
static int DAT_00515064 = 0; // colour-slot resource (&-taken)
|
||||
static char DAT_005150a8[] = ""; // palette name A
|
||||
static char DAT_005150ec[] = ""; // palette name B
|
||||
MethodDescription
|
||||
ColorMapperHeat::methodDescription =
|
||||
{
|
||||
"cmHeat",
|
||||
ColorMapperHeat::Make,
|
||||
{
|
||||
{ ParameterDescription::typeRate, NULL }, // refresh-rate ID
|
||||
{ ParameterDescription::typeModeMask, NULL }, // display mode mask
|
||||
{ ParameterDescription::typeColor, NULL }, // hardware colour slot
|
||||
{ ParameterDescription::typeString, NULL }, // palette name A
|
||||
{ ParameterDescription::typeString, NULL }, // palette name B
|
||||
{ ParameterDescription::typeString, NULL }, // heat subsystem name
|
||||
PARAMETER_DESCRIPTION_END
|
||||
}
|
||||
};
|
||||
|
||||
Logical
|
||||
ColorMapperHeat::Make(
|
||||
int /*display_port_index*/,
|
||||
int display_port_index,
|
||||
Vector2DOf<int> /*position*/,
|
||||
Entity *entity,
|
||||
GaugeRenderer *gauge_renderer
|
||||
)
|
||||
{
|
||||
ParameterDescription *p = methodDescription.parameterList;
|
||||
|
||||
ColorMapperHeat *gauge =
|
||||
(ColorMapperHeat *)operator new(0x6c); // FUN_00402298(0x6c)
|
||||
if (gauge != NULL)
|
||||
{
|
||||
new (gauge) ColorMapperHeat( // FUN_004c3f6c
|
||||
(GaugeRate)DAT_00514f98,
|
||||
(ModeMask) DAT_00514fdc,
|
||||
p[0].data.rate, // rate ID
|
||||
p[1].data.modeMask, // mode mask
|
||||
(L4GaugeRenderer *)gauge_renderer,
|
||||
DAT_00515020, // graphics port number
|
||||
(int)&DAT_00515064, // colour slot resource
|
||||
display_port_index, // graphics port number (the runtime port)
|
||||
p[2].data.color, // colour slot
|
||||
entity,
|
||||
(char *)&DAT_005150a8, // palette name A
|
||||
(char *)&DAT_005150ec, // palette name B
|
||||
(char *)"ColorMapperHeat", // subsystem name resource / id
|
||||
p[3].data.string, // palette name A
|
||||
p[4].data.string, // palette name B
|
||||
p[5].data.string, // heat subsystem name (e.g. "GeneratorA")
|
||||
"ColorMapperHeat");
|
||||
}
|
||||
return True;
|
||||
@@ -524,6 +554,17 @@ ColorMapperHeat::ColorMapperHeat(
|
||||
AddConnection(connection); // (*this+0x34)
|
||||
}
|
||||
|
||||
//
|
||||
// ColorMapperHeat destructor. No extra work: the HeatConnection added above is
|
||||
// released by the base Gauge teardown (RemoveAllConnections), and the two
|
||||
// palettes by ~ColorMapper -- so the base-dtor chain (~ColorMapper -> ~Gauge)
|
||||
// runs implicitly at the closing brace. (The binary's deleting-dtor thunks
|
||||
// @004c66ff/6725/674b/6771 forward to that same base chain.)
|
||||
//
|
||||
ColorMapperHeat::~ColorMapperHeat()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
|
||||
@@ -202,6 +202,10 @@
|
||||
Entity *, const char *, const char *,
|
||||
const char *heat_subsystem_name, const char *);
|
||||
~ColorMapperHeat();
|
||||
|
||||
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) so the config
|
||||
// interpreter builds this gauge from the CFG "cmHeat" primitive.
|
||||
static MethodDescription methodDescription;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -120,6 +120,16 @@ extern
|
||||
MethodDescription
|
||||
*BTL4MethodDescription[] =
|
||||
{
|
||||
//
|
||||
// BT gauge WIDGET reconstruction -- register each BT-specific gauge
|
||||
// class's methodDescription so the config interpreter can build it
|
||||
// (the CFG keyword -> class Make). Add ONE at a time, only when its
|
||||
// whole Make->ctor->feed chain is REAL code (a prose-only/undefined
|
||||
// link would be /FORCE-stubbed and AV at runtime; see CLAUDE.md §10).
|
||||
// The chain-to-previous link MUST stay LAST (routes unmatched keywords
|
||||
// to the engine base table L4MethodDescription).
|
||||
//
|
||||
&ColorMapperHeat::methodDescription, // "cmHeat" -- heat-driven palette tint
|
||||
&BTL4ChainToPrevious
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user