gauge-complete P4e: SectorDisplay reconstructed + registered -> radar SECTOR X/Z read-out LIVE
The "sectorDisplay" cockpit primitive (Secondary overlay, the radar SECTOR X/Z coordinate read-out) was PROSE-ONLY in btl4gau3 (placeholder Make, no ctor/ methodDescription/registration) -> the config line was parse-SKIPPED and never built. Reconstructed byte-verified from the disassembly (ctor @4c9e10, Execute @4ca07c, methodDescription PE-parse): SectorDisplay : GraphicGauge, sizeof 0xC4. Its Execute reads the linked mech's world position and shows two 100-unit sector numerics: numericA = Round(-localOrigin.z * 0.01) + 500 numericB = Round( localOrigin.x * 0.01) + 500 (rounding = round-to-nearest == FUN_004dcd94, corrected from the reviewer's wrong "truncate" claim; 0.01 const PE-verified; -Z/+X axis + fchs confirmed from asm). Overridden slots: LinkToEntity(9) caches the subject, BecameActive(3, non-inactivating), Execute(16) -> satisfies the container-Execute rule. Layout overflow-locked (static_assert sizeof<=0xC4). Make/ctor/dtor mirror the registered PilotList sibling; the config image name is copied (nameCopy) since Execute reads it per-frame. Registered in BTL4MethodDescription[]. VERIFIED LIVE (BT_SECTOR_LOG): Make port=1 pos=(125,579) image=helv15.pcc gridCached=1; Execute -Z=960.4 X=361.6 -> sectorA=510 sectorB=504 (Round(9.6)+500=510, Round(3.6)+500=504 -- authentic 100-unit sectors from live mech position). Gauge composite renders full, no crash. The skip list is now exactly the two remaining widgets (prepEngr x12, messageBoard). Also: a permanent BT_GAUGE_SKIP_LOG diagnostic (GAUGREND.cpp, gated) that logs each unregistered gauge primitive the dev-parse skips -- the tool that pinned this down (earlier "not built" runs were killed before the lazy gauge-renderer init). 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
d54e0009d7
commit
4a4ec6855c
+155
-48
@@ -59,18 +59,8 @@
|
||||
# include <app.hpp>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Unrecovered .data resource pools used by the two Make() factories below. The
|
||||
// concrete bytes (rate/mode/colours/image names) were not recovered from the
|
||||
// optimised image; declared here as typed placeholders so the construction
|
||||
// paths compile. (BEST-EFFORT.) DebugStream is provided by the shared recon
|
||||
// headers.
|
||||
//
|
||||
static int DAT_0051a2bc = 0; // SectorDisplay GaugeRate
|
||||
static int DAT_0051a300 = 0; // SectorDisplay ModeMask
|
||||
static char DAT_0051a344[] = ""; // SectorDisplay grid image name
|
||||
// (PlayerStatus's DAT_ placeholder pool removed -- its rewired Make reads
|
||||
// methodDescription.parameterList[] like the other registered widgets.)
|
||||
// (SectorDisplay's + PlayerStatus's DAT_ placeholder pools removed -- their rewired
|
||||
// Make()s read methodDescription.parameterList[] like the other registered widgets.)
|
||||
|
||||
|
||||
//###########################################################################
|
||||
@@ -92,61 +82,178 @@ static char DAT_0051a344[] = ""; // SectorDisplay grid image name
|
||||
// SectorDisplay @004c9d44 Make / @004c9e10 ctor
|
||||
//###########################################################################
|
||||
|
||||
// @0x51a2b0 -- registered so the interpreter builds the CFG line
|
||||
// sectorDisplay( K, ModeAlwaysActive, helv15.pcc, 0, 3 ) (L4GAUGE.CFG:5146; the
|
||||
// port index + offset=(125,579) arrive via Make's args, not config tokens).
|
||||
MethodDescription
|
||||
SectorDisplay::methodDescription =
|
||||
{
|
||||
"sectorDisplay",
|
||||
SectorDisplay::Make,
|
||||
{
|
||||
{ ParameterDescription::typeRate, NULL }, // p[0] rate [row0 type=1]
|
||||
{ ParameterDescription::typeModeMask, NULL }, // p[1] modeMask [row1 type=2]
|
||||
{ ParameterDescription::typeString, NULL }, // p[2] image [row2 type=9]
|
||||
{ ParameterDescription::typeColor, NULL }, // p[3] color [row3 type=4]
|
||||
{ ParameterDescription::typeColor, NULL }, // p[4] okColor [row4 type=4]
|
||||
PARAMETER_DESCRIPTION_END
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// @004c9d44 -- Make. Allocate (0xc4) and construct "SectorDisplay" from the
|
||||
// resource strings (DAT_0051a2bc..0051a3cc). Then verify the grid image exists
|
||||
// (BitMapCache); if missing, warn "SectorDisplay: Missing image <name>".
|
||||
// @004c9d44 -- Make. Alloc 0xc4 + construct; then peek the grid image and warn
|
||||
// "SectorDisplay: Missing image <name>" if absent (binary-faithful: the base ctor
|
||||
// self-registers the gauge regardless of the return value).
|
||||
//
|
||||
Logical
|
||||
SectorDisplay::Make(
|
||||
int /*display_port_index*/,
|
||||
Vector2DOf<int> /*position*/,
|
||||
Entity *entity,
|
||||
int display_port_index,
|
||||
Vector2DOf<int> position,
|
||||
Entity * /*entity -- unused*/,
|
||||
GaugeRenderer *gauge_renderer
|
||||
)
|
||||
{
|
||||
SectorDisplay *gauge = (SectorDisplay *)operator new(0xc4);
|
||||
ParameterDescription *p = methodDescription.parameterList;
|
||||
|
||||
SectorDisplay *gauge = (SectorDisplay *)operator new(0xc4); // FUN_00402298(0xc4)
|
||||
if (gauge != NULL)
|
||||
{
|
||||
//
|
||||
// Construct with the recovered grid-image name; the remaining geometry /
|
||||
// colour / font-image arguments were not recovered, so are typed
|
||||
// placeholders.
|
||||
//
|
||||
new (gauge) SectorDisplay(
|
||||
(GaugeRate)DAT_0051a2bc, // rate
|
||||
(ModeMask)DAT_0051a300, // mode mask
|
||||
p[0].data.rate, p[1].data.modeMask,
|
||||
(L4GaugeRenderer *)gauge_renderer,
|
||||
0, // graphics port number
|
||||
0, 0, // x, y
|
||||
(char *)&DAT_0051a344, // grid image
|
||||
(char *)0, // font image
|
||||
0, // color
|
||||
display_port_index, // graphics_port_number (FIX: was 0)
|
||||
position.x, position.y, // SetOrigin (FIX: was 0,0)
|
||||
p[2].data.string, // grid/font image (helv15.pcc)
|
||||
p[3].data.color, // numericColor
|
||||
p[4].data.color, // gridColor / numeric okColor
|
||||
"SectorDisplay");
|
||||
}
|
||||
|
||||
BitMap *grid = gauge_renderer->warehousePointer->bitMapBin.GetIfAlreadyExists(
|
||||
(char *)&DAT_0051a344); // FUN_00442aec (peek, no AddRef)
|
||||
p[2].data.string); // FUN_00442aec (peek, no AddRef)
|
||||
if (grid == NULL)
|
||||
{
|
||||
DebugStream << "SectorDisplay: Missing image "
|
||||
<< (char *)&DAT_0051a344 << "\n"; // FUN_004dbb24 x3
|
||||
}
|
||||
DebugStream << "SectorDisplay: Missing image " << p[2].data.string << "\n";
|
||||
if (getenv("BT_SECTOR_LOG"))
|
||||
DEBUG_STREAM << "[sector] Make port=" << display_port_index << " pos=("
|
||||
<< position.x << "," << position.y << ") image=" << p[2].data.string
|
||||
<< " gridCached=" << (int)(grid != NULL) << "\n" << std::flush;
|
||||
return (grid != NULL);
|
||||
}
|
||||
|
||||
//
|
||||
// @004c9e10 -- ctor (vtable PTR_FUN_0051beec). GraphicGauge base, interns the
|
||||
// grid image; computes cellWidth = imageWidth/14, gridLeft = cellWidth*12,
|
||||
// gridRight = cellWidth*12 + cellWidth - 1, gridHeight = imageHeight; sets the
|
||||
// port origin; builds two NumericDisplays (this[0x2F] at origin, this[0x30] at
|
||||
// +cellWidth*4). baseLine = dirty = 500.
|
||||
// @004c9f94 -- dtor: release grid, delete both numerics, GraphicGauge::~GraphicGauge.
|
||||
// @004ca038 -- BecameActive: dirty=1; reset both numerics.
|
||||
// @004ca068 -- SetEnable: this[0x24] = enable.
|
||||
// @004ca07c -- Execute: when enabled, DrawAt the two numerics at the rounded
|
||||
// cursor position (+offsets), and on the first pass blit the grid image.
|
||||
// @004c9e10 -- ctor (vtable PTR_FUN_0051beec). GraphicGauge base (owner 0); copy
|
||||
// the grid-image name; from the cached bitmap compute cellWidth=imageW/14,
|
||||
// gridLeft=cellWidth*12, gridRight=cellWidth*13-1, gridHeight=imageH; SetOrigin;
|
||||
// build two 3-digit NumericDisplays (@0 and @cellWidth*4).
|
||||
//
|
||||
SectorDisplay::SectorDisplay(
|
||||
GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in,
|
||||
int graphics_port_number, int x, int y,
|
||||
const char *image, int color, int ok_color, const char *identification_string)
|
||||
: GraphicGauge(rate, mode_mask, renderer_in, 0, // FUN_00444818 (owner 0)
|
||||
graphics_port_number, identification_string)
|
||||
{
|
||||
// nameCopy(image) -- Execute reads gridImage per-frame, so it must outlive the
|
||||
// (transient) config parameterList string; store a durable copy (FUN_004700ac).
|
||||
gridImage = NULL;
|
||||
if (image != NULL) {
|
||||
gridImage = new char[strlen(image) + 1];
|
||||
strcpy(gridImage, image);
|
||||
}
|
||||
numericColor = color; // @0xA8
|
||||
gridColor = ok_color; // @0xAC
|
||||
subject = NULL; // @0x90
|
||||
sectorBaseB = 500; // @0xB4 (0x1f4)
|
||||
sectorBaseA = 500; // @0xB0
|
||||
dirty = 1; // @0xB8 (binary leaves uninit; BecameActive sets it)
|
||||
|
||||
L4Warehouse *wh = (L4Warehouse *)renderer_in->warehousePointer; // renderer+0x4c
|
||||
BitMap *img = wh->bitMapBin.GetIfAlreadyExists(gridImage); // FUN_00442aec (peek)
|
||||
if (img == NULL) {
|
||||
cellWidth = gridLeft = gridRight = gridHeight = 0;
|
||||
} else {
|
||||
cellWidth = img->Data.Size.x / 14; // img+0xc width
|
||||
gridLeft = cellWidth * 12;
|
||||
gridRight = cellWidth * 12 + cellWidth - 1;
|
||||
gridHeight = img->Data.Size.y; // img+0x10 height
|
||||
}
|
||||
localView.SetOrigin(x, y); // this+0x48 vtbl+0x10
|
||||
|
||||
// NumericDisplay(warehouse, x, y, image, fieldWidth=3, format=unsignedFormat(0), color, okColor)
|
||||
numericA = new NumericDisplay(wh, 0, 0, image, 3,
|
||||
NumericDisplay::unsignedFormat, color, ok_color);
|
||||
numericB = new NumericDisplay(wh, cellWidth * 4, 0, image, 3,
|
||||
NumericDisplay::unsignedFormat, color, ok_color);
|
||||
}
|
||||
|
||||
//
|
||||
// @004c9f94 -- dtor. Free the copied name + both numerics; the base
|
||||
// GraphicGauge::~GraphicGauge runs implicitly (do NOT call it).
|
||||
//
|
||||
SectorDisplay::~SectorDisplay()
|
||||
{
|
||||
delete[] gridImage; gridImage = NULL;
|
||||
delete numericA; numericA = NULL; // FUN_0047018c
|
||||
delete numericB; numericB = NULL;
|
||||
}
|
||||
|
||||
// @004ca020 -- TestInstance: out-of-line forward to the base (so it isn't /FORCE-stubbed).
|
||||
Logical
|
||||
SectorDisplay::TestInstance() const { return GraphicGauge::TestInstance(); }
|
||||
|
||||
// @004ca068 -- LinkToEntity (slot 9): cache the renderer's linked entity as the subject.
|
||||
void
|
||||
SectorDisplay::LinkToEntity(Entity *entity) { subject = entity; }
|
||||
|
||||
// @004ca038 -- BecameActive (slot 3): mark dirty + reset both numerics. NON-inactivating
|
||||
// (does NOT chain the base) -> satisfies the container-Execute rule.
|
||||
void
|
||||
SectorDisplay::BecameActive()
|
||||
{
|
||||
dirty = 1;
|
||||
numericB->ForceUpdate(); // FUN_004703f4 (B then A, binary order)
|
||||
numericA->ForceUpdate();
|
||||
}
|
||||
|
||||
// @004ca07c -- Execute (slot 16): draw the two sector numerics from the linked mech's
|
||||
// world X/Z (Round via the codebase +0.5 idiom == FUN_004dcd94), then on the first
|
||||
// active frame blit the grid-cell background.
|
||||
void
|
||||
SectorDisplay::Execute()
|
||||
{
|
||||
Entity *s = subject; // this+0x90
|
||||
// PORT accommodation (marked): the LinkToEntity broadcast may be unwired on
|
||||
// WinTesla -> fall back to the viewpoint mech (same as btl4rdr ResolveOperatorEntity).
|
||||
// The binary gates purely on `subject != 0` (@0x4ca08e).
|
||||
if (s == NULL && application != NULL)
|
||||
s = (Entity *)application->GetViewpointEntity();
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
const Point3D &pos = s->localOrigin.linearPosition; // s+0x100 (x@+0x100, z@+0x108)
|
||||
Scalar za = -pos.z * 0.01f; // @0x4ca194 = 0.01 (100-unit sectors)
|
||||
Scalar xb = pos.x * 0.01f;
|
||||
int vA = (int)(za + (za < 0.0f ? -0.5f : 0.5f)) + sectorBaseA; // Round(-Z*0.01)+500
|
||||
int vB = (int)(xb + (xb < 0.0f ? -0.5f : 0.5f)) + sectorBaseB; // Round( X*0.01)+500
|
||||
numericA->Draw(&localView, (Scalar)vA); // FUN_00470430
|
||||
numericB->Draw(&localView, (Scalar)vB);
|
||||
|
||||
if (getenv("BT_SECTOR_LOG")) {
|
||||
static int s_n = 0;
|
||||
if ((s_n++ % 120) == 0)
|
||||
DEBUG_STREAM << "[sector] Execute -Z=" << (-pos.z) << " X=" << pos.x
|
||||
<< " -> sectorA=" << vA << " sectorB=" << vB << "\n" << std::flush;
|
||||
}
|
||||
|
||||
if (dirty) { // this+0xB8
|
||||
dirty = 0;
|
||||
BitMap *grid = ((L4Warehouse *)renderer->warehousePointer)
|
||||
->bitMapBin.GetIfAlreadyExists(gridImage); // FUN_00442aec
|
||||
if (grid != NULL) {
|
||||
localView.MoveToAbsolute(cellWidth * 3, 0); // vtbl+0x24
|
||||
localView.SetColor(gridColor); // vtbl+0x18 (this+0xAC)
|
||||
localView.DrawBitMap(0, grid, gridLeft, 0, gridRight, gridHeight); // vtbl+0x54
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//###########################################################################
|
||||
|
||||
Reference in New Issue
Block a user