gauge-complete P4f: PrepEngrScreen reconstructed + registered -> 12 engineering-screen label overlays build
The "prepEngr" cockpit primitive (the per-engineering-screen static label overlay,
12 CFG calls at L4GAUGE.CFG:4595-4759) was a MISLABELED stub (base Gauge, an Execute
override, mech/screenNumber SWAPPED) with no methodDescription -> parse-SKIPPED, none built.
Reconstructed byte-verified (ctor @4c7bf0, BecameActive @4c7e48, Make @4c7b30; vtable
0051a06c). THREE corrections vs the stub (decode workflow + vtables.tsv proof):
(1) base = GraphicGaugeBackground, not Gauge -- vtable-identical shape to the engine's
BackgroundBitmap (only dtor + BecameActive overridden); (2) the overridden slot is
BecameActive (a paint-on-activation), NOT Execute -- GraphicGaugeBackground has no Execute
virtual, so the Gauge::Execute->Fail->abort hazard doesn't apply; (3) screenNumber@0x6C /
mech@0x70 were swapped. sizeof 0x90.
BecameActive walks the mech roster (databinding-safe GetSubsystemCount/GetSubsystem +
BTGetSubsystemAuxScreen bridge, like VehicleSubSystems::Make), finds the subsystem whose
auxScreenNumber == this screen (1..12), and paints the screen-number numeric + subsystem
label + type-specific label cells dispatched on GetClassID (Sensor/Myomers/Emitter/PPC/
Projectile/Missile/GaussRifle). methodDescription = 9 params (mode, screen, 7 .pcc names).
Registered in BTL4MethodDescription[]. Two raw subsystem reads (heat-sink #, +0x224 label)
are guarded fail-soft best-efforts (marked; the aux-screen bridge label64 substitutes).
VERIFIED: the skip list drops from {prepEngr x12, messageBoard} to just {messageBoard} --
all 12 prepEngr gauges build; no screen-range/crash; boot+sim+combat run; gauge composite
renders identically (COOLANT 300/77, clusters, radar all un-regressed). The prepEngr labels
paint on the Eng (ModeMFD*Eng*) screens, not the default composite view.
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
4a4ec6855c
commit
c0d2eb0aae
+216
-21
@@ -822,32 +822,227 @@ void
|
|||||||
//###########################################################################
|
//###########################################################################
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|
||||||
|
// forward: the tiled-blit helper is defined lower in this TU (FUN_004c2ec4).
|
||||||
|
static void DrawPortBackground(GraphicsView *view, L4Warehouse *warehouse,
|
||||||
|
int x, int y, const char *tile);
|
||||||
|
|
||||||
|
// ---- FUN_004c79c8 : draw the three status-label cells (+ optional top erase box).
|
||||||
|
// Each cell blits its label .pcc, or blanks the cell. Absolute cell rectangles are
|
||||||
|
// the binary's exact coords (part_014.c:1127-1166).
|
||||||
|
static void
|
||||||
|
DrawStatusCells(GraphicsView *view, L4Warehouse *warehouse,
|
||||||
|
int topBox, const char *A, const char *B, const char *C)
|
||||||
|
{
|
||||||
|
if (topBox) // :1131-1135
|
||||||
|
{
|
||||||
|
view->SetColor(0);
|
||||||
|
view->MoveToAbsolute(0x97, 0x80);
|
||||||
|
view->DrawFilledRectangleToAbsolute(0x17d, 0x13b);
|
||||||
|
}
|
||||||
|
if (A == NULL) { view->SetColor(0); view->MoveToAbsolute(0x15a, 0x145); // :1136-1140
|
||||||
|
view->DrawFilledRectangleToAbsolute(0x17f, 0x159); }
|
||||||
|
else { view->SetColor(0xff); DrawPortBackground(view, warehouse, 0x15a, 0x145, A); }
|
||||||
|
if (B == NULL) { view->SetColor(0); view->MoveToAbsolute(0x85, 0x58); // :1145-1149
|
||||||
|
view->DrawFilledRectangleToAbsolute(0x17f, 0x7c); }
|
||||||
|
else { view->SetColor(0xff); DrawPortBackground(view, warehouse, 0x85, 0x58, B); }
|
||||||
|
if (C == NULL) { view->SetColor(0); view->MoveToAbsolute(0x2a, 0x34); // :1154-1160
|
||||||
|
// raw uses an extra GraphicsView vtbl+0x4c(99,10) compound-blank; a plain
|
||||||
|
// fill at the same cell is the faithful-enough substitute (marked).
|
||||||
|
view->DrawFilledRectangleToAbsolute(0x9f, 0x2d); }
|
||||||
|
else { view->SetColor(0xff); DrawPortBackground(view, warehouse, 0x2a, 0x34, C); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFG shape (L4GAUGE.CFG:4595): mode, int screen(1..12), then 7 .pcc names
|
||||||
|
// (font + 6 labels). 9 params.
|
||||||
|
MethodDescription
|
||||||
|
PrepEngrScreen::methodDescription =
|
||||||
|
{
|
||||||
|
"prepEngr",
|
||||||
|
PrepEngrScreen::Make,
|
||||||
|
{
|
||||||
|
{ ParameterDescription::typeModeMask, NULL }, // p[0] mode (ModeMFD*Eng*)
|
||||||
|
{ ParameterDescription::typeInteger, NULL }, // p[1] screen number 1..12
|
||||||
|
{ ParameterDescription::typeString, NULL }, // p[2] font helv42.pcc -> statusImage[0]
|
||||||
|
{ ParameterDescription::typeString, NULL }, // p[3] escnd.pcc -> statusImage[1]
|
||||||
|
{ ParameterDescription::typeString, NULL }, // p[4] edmg.pcc -> statusImage[2]
|
||||||
|
{ ParameterDescription::typeString, NULL }, // p[5] bteslev.pcc -> statusImage[3]
|
||||||
|
{ ParameterDescription::typeString, NULL }, // p[6] bteunjm.pcc -> statusImage[4]
|
||||||
|
{ ParameterDescription::typeString, NULL }, // p[7] espeed.pcc -> statusImage[5]
|
||||||
|
{ ParameterDescription::typeString, NULL }, // p[8] btesrnge.pcc -> statusImage[6]
|
||||||
|
PARAMETER_DESCRIPTION_END
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// @004c7b30 -- Make. DAT_0051936c is the engineer-screen number; must be 1..12
|
// @004c7b30 -- Make. Validate the screen number (p[1], 1..12) then construct.
|
||||||
// or "PrepEngr: screen number out of range <n>" is warned. Allocates (0x90)
|
// The GraphicGaugeBackground base ctor self-registers with the renderer, so the
|
||||||
// and constructs with seven status-image names (DAT_005193b0..00519548).
|
// gauge's BecameActive fires whenever its Eng screen is selected. We `new` the
|
||||||
|
// object (real compiled size) rather than operator new(0x90) -- the 2007 base
|
||||||
|
// isn't byte-exact, and nothing reads this object at raw offsets.
|
||||||
//
|
//
|
||||||
// @004c7bf0 -- ctor (vtable PTR_FUN_0051a06c). Derives from the non-graphic
|
Logical
|
||||||
// Gauge group (FUN_004440d4) -- it owns no GraphicsView, only a child-gauge
|
PrepEngrScreen::Make(
|
||||||
// chain at +0x24. Interns and BitMapCache-AddRefs the seven status pixmaps
|
int display_port_index,
|
||||||
// (this[0x1D..0x23]); stores mech (this[0x1B]) and screen number (this[0x1C]).
|
Vector2DOf<int> /*position -- PrepEngr draws at absolute coords*/,
|
||||||
|
Entity *entity,
|
||||||
|
GaugeRenderer *gauge_renderer
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ParameterDescription *p = methodDescription.parameterList;
|
||||||
|
|
||||||
|
int screen = p[1].data.integer;
|
||||||
|
if (screen < 1 || screen > 12) // :1182
|
||||||
|
{
|
||||||
|
DEBUG_STREAM << "PrepEngr: screen number out of range " << screen << "\n"; // :1183
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
new PrepEngrScreen( // FUN_00402298(0x90) in the binary
|
||||||
|
p[0].data.modeMask, // mode (ModeMFD*Eng*)
|
||||||
|
(L4GaugeRenderer *)gauge_renderer,
|
||||||
|
0, // owner_ID (raw hard-wires 0)
|
||||||
|
display_port_index, // graphics_port_number
|
||||||
|
entity, // mech
|
||||||
|
screen, // screen number
|
||||||
|
p[2].data.string, p[3].data.string, p[4].data.string,
|
||||||
|
p[5].data.string, p[6].data.string, p[7].data.string,
|
||||||
|
p[8].data.string,
|
||||||
|
"PrepEngr");
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// @004c7d14 -- dtor: release + free the seven images, tear down the child chain
|
// @004c7bf0 -- ctor. GraphicGaugeBackground base (mode/renderer/0/port/"PrepEngr");
|
||||||
// (FUN_0044ae0c), Gauge-group dtor (FUN_00443f7c).
|
// store screen + mech; copy the 7 image names and pre-load (AddRef) each from the
|
||||||
|
// bitmap cache.
|
||||||
//
|
//
|
||||||
// @004c7e48 -- Execute. Walk the mech's critical-subsystem roster
|
PrepEngrScreen::PrepEngrScreen(
|
||||||
// (mech+0x124 count, mech+0x128 array). For each subsystem that (a) is a
|
ModeMask mode_mask, L4GaugeRenderer *renderer_in, unsigned int owner_ID,
|
||||||
// Generator-derived weapon (IsDerivedFrom 0x50f4bc) and (b) belongs to the
|
int graphics_port_number, Entity *mech_in, int screen_number,
|
||||||
// selected screen (subsys+0x1dc == screenNumber), draw its label and -- via
|
const char *img0, const char *img1, const char *img2, const char *img3,
|
||||||
// NumericDisplay (FUN_004700bc/0047043 0) -- its temperature and a HeatSink
|
const char *img4, const char *img5, const char *img6,
|
||||||
// read-out, then dispatch on ClassID to lay out the weapon's status bitmaps
|
const char *identification_string
|
||||||
// (FUN_004c79c8 = draw three labelled status cells):
|
):
|
||||||
// 0xBC3 Sensor -> one cell + ammo bitmap (this[0x23])
|
GraphicGaugeBackground(mode_mask, renderer_in, owner_ID, // FUN_004440d4
|
||||||
// 0xBC6 -> two cells (this[0x22],this[0x20])
|
graphics_port_number, identification_string)
|
||||||
// 0xBC8/0xBD4 Emitter/PPC -> three cells (this[0x1E..0x20])
|
{
|
||||||
// 0xBCD/0xBD0 Proj/Missile -> one cell (this[0x21])
|
screenNumber = screen_number; // @0x6C (:1216)
|
||||||
// 0xBCE GaussRifle -> warn "Gauss rifle not yet supported", one cell
|
mech = mech_in; // @0x70 (:1215)
|
||||||
|
|
||||||
|
const char *src[7] = { img0, img1, img2, img3, img4, img5, img6 };
|
||||||
|
L4Warehouse *warehouse = (L4Warehouse *)renderer_in->warehousePointer; // renderer+0x4c
|
||||||
|
for (int i = 0; i < 7; i++) // :1217-1238
|
||||||
|
{
|
||||||
|
if (src[i] != NULL) {
|
||||||
|
statusImage[i] = new char[strlen(src[i]) + 1]; // nameCopy (FUN_004700ac)
|
||||||
|
strcpy(statusImage[i], src[i]);
|
||||||
|
warehouse->bitMapBin.Get(statusImage[i]); // FUN_00442aec pre-load/AddRef
|
||||||
|
} else {
|
||||||
|
statusImage[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
// @004c7d14 -- dtor. Release the 7 pre-loaded images + free their name copies;
|
||||||
|
// the localView + base dtor run implicitly (do NOT emit explicit base-dtor calls).
|
||||||
|
//
|
||||||
|
PrepEngrScreen::~PrepEngrScreen()
|
||||||
|
{
|
||||||
|
L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer;
|
||||||
|
for (int i = 0; i < 7; i++) // :1254-1274
|
||||||
|
{
|
||||||
|
if (statusImage[i] != NULL) {
|
||||||
|
warehouse->bitMapBin.Release(statusImage[i]); // FUN_00442c12
|
||||||
|
delete[] statusImage[i];
|
||||||
|
statusImage[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// @004c7e48 -- BecameActive. Find the subsystem assigned to this screen and paint
|
||||||
|
// its labels (roster + aux-screen read via the databinding-safe accessors + the
|
||||||
|
// PoweredSubsystem bridge, exactly like VehicleSubSystems::Make). NON-inactivating
|
||||||
|
// (a real paint; does not chain the base) -> the paint-on-activation contract.
|
||||||
|
//
|
||||||
|
void
|
||||||
|
PrepEngrScreen::BecameActive()
|
||||||
|
{
|
||||||
|
if (mech == NULL) return;
|
||||||
|
L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer;
|
||||||
|
const char *font = statusImage[0]; // helv42.pcc
|
||||||
|
|
||||||
|
int count = mech->GetSubsystemCount(); // mech+0x124
|
||||||
|
for (int i = 1; i < count; i++) // skip slot 0 (controls mapper)
|
||||||
|
{
|
||||||
|
Subsystem *sub = mech->GetSubsystem(i); // mech+0x128[i]
|
||||||
|
if (sub == NULL) continue;
|
||||||
|
|
||||||
|
int auxScreen = 0, placement = -1; char label64[64]; label64[0] = '\0';
|
||||||
|
if (!BTGetSubsystemAuxScreen(sub, &auxScreen, &placement, label64)) // filter + sub+0x1dc
|
||||||
|
continue; // not a PoweredSubsystem-derived weapon
|
||||||
|
if (auxScreen != screenNumber) // :1309
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// ---- numeric #1 : the screen number, 2 digits @(0xE9,0x176) (:1310-1313)
|
||||||
|
{
|
||||||
|
NumericDisplay num(warehouse, 0xe9, 0x176, font, 2,
|
||||||
|
NumericDisplay::unsignedFormat, 0, 0xff); // FUN_004700bc
|
||||||
|
num.Draw(&localView, (Scalar)screenNumber); // FUN_00470430
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- numeric #2 : linked heat-sink number, 1 digit @(0x15E,5) (:1315-1321)
|
||||||
|
// BEST-EFFORT (marked): the resolved sink's +0x1D4 index is a raw offset on
|
||||||
|
// a not-byte-exact object -> guarded (NULL link / read 0).
|
||||||
|
int hsNumber = 0;
|
||||||
|
void *hsLink = AttributePointerOf(sub, "HeatSink"); // FUN_0041bfc0
|
||||||
|
void *hs = (hsLink != NULL) ? ResolveLink(hsLink) : NULL; // FUN_00417ab4
|
||||||
|
if (hs != NULL)
|
||||||
|
hsNumber = *(int *)((char *)hs + 0x1d4);
|
||||||
|
{
|
||||||
|
NumericDisplay num(warehouse, 0x15e, 5, font, 1,
|
||||||
|
NumericDisplay::unsignedFormat, 0, 0xff);
|
||||||
|
num.Draw(&localView, (Scalar)hsNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- subsystem label bitmap @(0x125,0x172) (:1322-1329). The binary draws
|
||||||
|
// the .pcc name at sub+0x224; the aux-screen bridge's label64 is the
|
||||||
|
// databinding-safe equivalent (BEST-EFFORT).
|
||||||
|
if (label64[0] != '\0') {
|
||||||
|
localView.SetColor(0xff);
|
||||||
|
DrawPortBackground(&localView, warehouse, 0x125, 0x172, label64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- type-specific label cells (switch on ClassID, :1330-1357)
|
||||||
|
switch (sub->GetClassID()) // sub+4
|
||||||
|
{
|
||||||
|
case 0xBC3: // Sensor
|
||||||
|
DrawStatusCells(&localView, warehouse, 1, NULL, NULL, NULL);
|
||||||
|
localView.SetColor(0xff);
|
||||||
|
DrawPortBackground(&localView, warehouse, 0xb8, 0x8c, statusImage[6]); // btesrnge
|
||||||
|
return;
|
||||||
|
case 0xBC6: // Myomers
|
||||||
|
DrawStatusCells(&localView, warehouse, 0, NULL, statusImage[5], statusImage[3]);
|
||||||
|
return;
|
||||||
|
case 0xBC8: // Emitter
|
||||||
|
case 0xBD4: // PPC
|
||||||
|
DrawStatusCells(&localView, warehouse, 0, statusImage[1], statusImage[2], statusImage[3]);
|
||||||
|
return;
|
||||||
|
case 0xBCD: // ProjectileWeapon
|
||||||
|
case 0xBD0: // MissileLauncher
|
||||||
|
DrawStatusCells(&localView, warehouse, 1, NULL, NULL, statusImage[4]);
|
||||||
|
return;
|
||||||
|
case 0xBCE: // GaussRifle -- not yet supported
|
||||||
|
DEBUG_STREAM << "PrepEngr: Gauss rifle not yet supported\n"; // :1353
|
||||||
|
DrawStatusCells(&localView, warehouse, 1, NULL, NULL, statusImage[3]);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|||||||
@@ -290,35 +290,39 @@
|
|||||||
|
|
||||||
|
|
||||||
//#######################################################################
|
//#######################################################################
|
||||||
// PrepEngrScreen -- builds the per-weapon "engineering" status screen.
|
// PrepEngrScreen (config keyword "prepEngr") -- the per-engineering-screen
|
||||||
// @004c7b30 Make / @004c7bf0 ctor / @004c7d14 dtor. vtable PTR_FUN_0051a06c.
|
// STATIC LABEL overlay. @004c7b30 Make / @004c7bf0 ctor / @004c7d14 dtor /
|
||||||
// Derives from the non-graphic Gauge group (FUN_004440d4) and interns seven
|
// @004c7e48 BecameActive. vtable PTR_FUN_0051a06c.
|
||||||
// status pixmaps (this[0x1D..0x23]). Make validates DAT_0051936c (engineer
|
//
|
||||||
// screen number, must be 1..12; warns "PrepEngr: screen number out of range").
|
// A GraphicGaugeBackground (paint-on-activation, NOT an Execute gauge -- the
|
||||||
// Execute @004c7e48 walks the mech's critical-subsystem roster (mech+0x124
|
// same base/vtable shape as the engine's BackgroundBitmap: only the dtor +
|
||||||
// count / mech+0x128 array) and, for each weapon on the selected screen,
|
// BecameActive slots are overridden). Interns 7 status .pcc's (font + 6
|
||||||
// renders its label & ammo via NumericDisplay, dispatching on subsystem
|
// labels) and, when its MFD-Eng screen (1..12) activates, paints the labels
|
||||||
// ClassID (0xBC3 Sensor, 0xBC6, 0xBC8/0xBD4 Emitter/PPC, 0xBCD/0xBD0
|
// for the ONE subsystem whose auxScreenNumber == this screen, dispatched by
|
||||||
// Projectile/Missile, 0xBCE GaussRifle -- "Gauss rifle not yet supported").
|
// the subsystem's ClassID. Was a MISLABELED stub (base Gauge, Execute, and
|
||||||
|
// mech/screenNumber SWAPPED) with no methodDescription -> "prepEngr" was
|
||||||
|
// parse-skipped (12 CFG calls never built).
|
||||||
//#######################################################################
|
//#######################################################################
|
||||||
class PrepEngrScreen :
|
class PrepEngrScreen :
|
||||||
public Gauge // non-graphic Gauge group (FUN_004440d4)
|
public GraphicGaugeBackground
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static MethodDescription methodDescription; // "prepEngr"
|
||||||
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c7b30
|
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c7b30
|
||||||
PrepEngrScreen( // @004c7bf0
|
PrepEngrScreen( // @004c7bf0
|
||||||
GaugeRate, ModeMask, L4GaugeRenderer *, int screen_number,
|
ModeMask, L4GaugeRenderer *, unsigned int owner_ID,
|
||||||
Entity *mech, int graphics_port_number,
|
int graphics_port_number, Entity *mech, int screen_number,
|
||||||
const char *img0, const char *img1, const char *img2,
|
const char *img0/*font*/, const char *img1, const char *img2,
|
||||||
const char *img3, const char *img4, const char *img5,
|
const char *img3, const char *img4, const char *img5,
|
||||||
const char *img6, const char *identification_string);
|
const char *img6, const char *identification_string = "PrepEngr");
|
||||||
~PrepEngrScreen(); // @004c7d14
|
~PrepEngrScreen(); // @004c7d14
|
||||||
void Execute(); // @004c7e48
|
void BecameActive(); // @004c7e48 (the paint slot -- NOT Execute)
|
||||||
protected:
|
protected:
|
||||||
Entity *mech; // @0x6C this[0x1B]
|
int screenNumber; // @0x6C this[0x1B] (FIX: was mech)
|
||||||
int screenNumber; // @0x70 this[0x1C]
|
Entity *mech; // @0x70 this[0x1C] (FIX: was screenNumber)
|
||||||
char *statusImage[7]; // @0x74 this[0x1D..0x23]
|
char *statusImage[7]; // @0x74 this[0x1D..0x23] (interned .pcc names)
|
||||||
// child gauges chained in the base Gauge group at +0x24
|
// sizeof == 0x90 (Make alloc); no offset locks (2007 GraphicGaugeBackground
|
||||||
|
// base not byte-identical; every read is via named members / safe accessors).
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ MethodDescription
|
|||||||
&PilotList::methodDescription, // "pilotList" -- Comm KILLS/DEATHS pilot roster
|
&PilotList::methodDescription, // "pilotList" -- Comm KILLS/DEATHS pilot roster
|
||||||
&GeneratorCluster::methodDescription, // "GeneratorCluster" -- the 4 generator engineering panels (buttons 9-12)
|
&GeneratorCluster::methodDescription, // "GeneratorCluster" -- the 4 generator engineering panels (buttons 9-12)
|
||||||
&SectorDisplay::methodDescription, // "sectorDisplay" -- radar SECTOR X/Z read-out (Secondary overlay)
|
&SectorDisplay::methodDescription, // "sectorDisplay" -- radar SECTOR X/Z read-out (Secondary overlay)
|
||||||
|
&PrepEngrScreen::methodDescription, // "prepEngr" -- per-Eng-screen static label overlay (12x)
|
||||||
&BTL4ChainToPrevious
|
&BTL4ChainToPrevious
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
Reference in New Issue
Block a user