diff --git a/docs/GAUGE_COMPOSITE.md b/docs/GAUGE_COMPOSITE.md index 2efff28..8c8ebbc 100644 --- a/docs/GAUGE_COMPOSITE.md +++ b/docs/GAUGE_COMPOSITE.md @@ -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 `::methodDescription = { "cfgKeyword", ::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 `&::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). diff --git a/game/reconstructed/btl4gaug.cpp b/game/reconstructed/btl4gaug.cpp index d3fd78c..4ab72c1 100644 --- a/game/reconstructed/btl4gaug.cpp +++ b/game/reconstructed/btl4gaug.cpp @@ -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 /*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() +{ +} + //########################################################################### //########################################################################### diff --git a/game/reconstructed/btl4gaug.hpp b/game/reconstructed/btl4gaug.hpp index 84a359d..86476ad 100644 --- a/game/reconstructed/btl4gaug.hpp +++ b/game/reconstructed/btl4gaug.hpp @@ -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; }; diff --git a/game/reconstructed/btl4grnd.cpp b/game/reconstructed/btl4grnd.cpp index 06b7fd0..ce5b2ed 100644 --- a/game/reconstructed/btl4grnd.cpp +++ b/game/reconstructed/btl4grnd.cpp @@ -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 };