#118: the eject slot graphic lands -- BitMapInverseWipeScalar reconstructed
The operator's field report ("eject slot flashing but no graphic in it") was
the tracked-NULL bring-up stub in BallisticWeaponCluster. Reconstructed the
missing gauge class from the binary:
- BitMapInverseWipeScalar @004c61c8 (vtable 0x518a14; base @004c5e84,
Execute @004c5fb8, BecameActive @004c5fa4): a bitmap COLUMN SWEEP whose
level tracks a live Scalar -- two spans with INVERTED colour pairs,
[0..level] fg colorA / bg colorB, remainder the inverse.
- Wired per the binary call site (part_014.c:2312): bteejtm.pcc at (0xF,0)
on the weapon's eng port, colours 0/0xFF, watching the weapon's
PercentOfEject (@0x3F8, the round-eject 0..1 progress) via a new
complete-type bridge BTWeaponPercentOfEjectPtr (databinding rule).
At idle (level 0) the EJECT graphic now renders in the slot; during a
round-eject cycle it sweeps with the countdown. BTEEJTM.PCC ships in
content/GAUGE (the loose gauge bitmap set). Smoke-tested: constructs clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
611b1a8bbd
commit
751b1159b5
@@ -2020,11 +2020,22 @@ BallisticWeaponCluster::BallisticWeaponCluster(
|
||||
destroyedLamp = new TwoState(ChildRate(), eng_mode, renderer_in, owner_ID,
|
||||
engPort, 0xc2, 0x85, "edestryd.pcc", 0, 0xff, (int *)&failedState, "TwoState"); // the destroyed X: bright when FAILED
|
||||
|
||||
// ejectWipe (BitMapInverseWipeScalar @004c61c8, eject-timer wipe reading
|
||||
// subsys+0x3f8). BRING-UP: the BitMapInverseWipeScalar class is not yet
|
||||
// declared/reconstructed in btl4gaug (only the non-Scalar BitMapInverseWipe);
|
||||
// tracked NULL until it lands.
|
||||
ejectWipe = NULL; // @0x44
|
||||
// ejectWipe (BitMapInverseWipeScalar @004c61c8): the round-EJECT progress
|
||||
// sweep -- bteejtm.pcc revealed column-by-column as the weapon's
|
||||
// PercentOfEject (@0x3F8) runs 0..1. Binary call (part_014.c:2312):
|
||||
// FUN_004c61c8(., rate, eng_mode, renderer, ownerID, engPort, 0xF, 0,
|
||||
// "bteejtm.pcc", 0, 0xFF, weapon+0x3f8, name). Reconstructed 2026-08-03
|
||||
// (#118 field report: the eject slot flashed EMPTY -- this was the
|
||||
// tracked-NULL stub).
|
||||
{
|
||||
extern Scalar *BTWeaponPercentOfEjectPtr(void *weapon);
|
||||
Scalar *ejectPct = BTWeaponPercentOfEjectPtr(subsystem_in);
|
||||
ejectWipe = (ejectPct != NULL)
|
||||
? new BitMapInverseWipeScalar(ChildRate(), eng_mode, renderer_in,
|
||||
owner_ID, engPort, 0xF, 0, "bteejtm.pcc", 0, 0xFF,
|
||||
ejectPct, "BitMapInverseWipeScalar")
|
||||
: NULL; // @0x44
|
||||
}
|
||||
}
|
||||
|
||||
BallisticWeaponCluster::~BallisticWeaponCluster()
|
||||
|
||||
@@ -2152,6 +2152,101 @@ void BitMapInverseWipe::Execute()
|
||||
}
|
||||
|
||||
|
||||
//###########################################################################
|
||||
// BitMapInverseWipeScalar @004c61c8 ctor / @004c5fb8 Execute
|
||||
//###########################################################################
|
||||
//
|
||||
// The weapon-eng round-EJECT progress sweep (bteejtm.pcc <- PercentOfEject).
|
||||
// Base ctor @004c5e84 interns the image and captures colours + the bitmap
|
||||
// geometry; the @004c61c8 tail adds the Scalar value-watcher (FUN_00474855
|
||||
// into this+0xA8) -- our GaugeConnectionDirectOf<Scalar> is that watcher.
|
||||
//
|
||||
BitMapInverseWipeScalar::BitMapInverseWipeScalar(
|
||||
GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in,
|
||||
int owner_ID, int graphics_port_number, int x, int y, const char *image,
|
||||
int color_a, int color_b, Scalar *value_pointer,
|
||||
const char *identification_string
|
||||
):
|
||||
GraphicGauge(rate, mode_mask, renderer_in, owner_ID, graphics_port_number,
|
||||
identification_string)
|
||||
{
|
||||
localView.SetOrigin(x, y);
|
||||
imageName = new char[strlen(image) + 1];
|
||||
strcpy(imageName, image);
|
||||
colorA = color_a;
|
||||
colorB = color_b;
|
||||
value = 0.0f;
|
||||
previousLevel = -1;
|
||||
L4Warehouse *warehouse = (L4Warehouse *)renderer_in->warehousePointer;
|
||||
BitMap *bmp = warehouse->bitMapBin.Get(imageName);
|
||||
if (bmp == NULL)
|
||||
{
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = bmp->Data.Size.x;
|
||||
height = bmp->Data.Size.y;
|
||||
}
|
||||
AddConnection(new GaugeConnectionDirectOf<Scalar>(0, &value, value_pointer));
|
||||
}
|
||||
|
||||
BitMapInverseWipeScalar::~BitMapInverseWipeScalar()
|
||||
{
|
||||
L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer;
|
||||
warehouse->bitMapBin.Release(imageName);
|
||||
delete[] imageName;
|
||||
imageName = NULL;
|
||||
}
|
||||
|
||||
Logical BitMapInverseWipeScalar::TestInstance() const { return GraphicGauge::TestInstance(); }
|
||||
|
||||
void BitMapInverseWipeScalar::BecameActive() // @004c5fa4
|
||||
{
|
||||
previousLevel = -1;
|
||||
}
|
||||
|
||||
//
|
||||
// @004c5fb8 -- Execute. level = round(value x width) clamped to [0, width]
|
||||
// (the x87 form Ghidra drops -- fild width / fmul value, gotcha #19), and on
|
||||
// a change draw the two column spans with INVERTED colour pairs: [0..level]
|
||||
// fg colorA over bg colorB, [level+1..width] fg colorB over bg colorA.
|
||||
//
|
||||
void BitMapInverseWipeScalar::Execute()
|
||||
{
|
||||
Scalar scaled = value * (Scalar)width;
|
||||
int level = (int)(scaled + (scaled < 0.0f ? -0.5f : 0.5f)); // FUN_004dcd94 round
|
||||
if (level < 0) level = 0;
|
||||
if (level > width) level = width;
|
||||
|
||||
if (level != previousLevel)
|
||||
{
|
||||
previousLevel = level;
|
||||
L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer;
|
||||
BitMap *bmp = warehouse->bitMapBin.Get(imageName);
|
||||
if (bmp != NULL)
|
||||
{
|
||||
int split = level;
|
||||
if (split != 0)
|
||||
{
|
||||
localView.MoveToAbsolute(0, 0);
|
||||
localView.SetColor(colorA);
|
||||
localView.DrawBitMap(colorB, bmp, 0, 0, split, height);
|
||||
++split; // the binary's iVar2++
|
||||
}
|
||||
if (split < width)
|
||||
{
|
||||
localView.MoveToAbsolute(split, 0);
|
||||
localView.SetColor(colorB);
|
||||
localView.DrawBitMap(colorA, bmp, split, 0, width, height);
|
||||
}
|
||||
warehouse->bitMapBin.Release(imageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
// HeadingPointer @004c554c Make / @004c562c ctor
|
||||
|
||||
@@ -541,6 +541,39 @@
|
||||
Scalar value; // @0xB4 this[0x2D] (connection)
|
||||
};
|
||||
|
||||
//#######################################################################
|
||||
// BitMapInverseWipeScalar @004c61c8 (vtable 0x518a14; base ctor @004c5e84,
|
||||
// Execute @004c5fb8, BecameActive @004c5fa4). A bitmap COLUMN SWEEP whose
|
||||
// level tracks a live Scalar 0..1 (the ctor's trailing Scalar* rides a
|
||||
// value-watcher into this+0xA8): columns [0..level] draw fg colorA over
|
||||
// bg colorB, the remainder draws the INVERSE -- the weapon-engineering
|
||||
// panel's round-EJECT progress graphic (bteejtm.pcc, watching the
|
||||
// weapon's PercentOfEject @0x3F8). Reconstructed 2026-08-03 (#118: the
|
||||
// operator saw the eject slot flash with NO graphic -- this was the
|
||||
// tracked-NULL bring-up stub in BallisticWeaponCluster).
|
||||
//#######################################################################
|
||||
class BitMapInverseWipeScalar :
|
||||
public GraphicGauge
|
||||
{
|
||||
public:
|
||||
BitMapInverseWipeScalar( // @004c61c8
|
||||
GaugeRate, ModeMask, L4GaugeRenderer *, int owner_ID,
|
||||
int graphics_port_number, int x, int y, const char *image,
|
||||
int color_a, int color_b, Scalar *value_pointer, const char *);
|
||||
~BitMapInverseWipeScalar();
|
||||
Logical TestInstance() const;
|
||||
void BecameActive(); // @004c5fa4 (previousLevel = -1)
|
||||
void Execute(); // @004c5fb8 (two-span inverse sweep)
|
||||
protected:
|
||||
char *imageName; // @0x90
|
||||
int colorA; // @0x94
|
||||
int colorB; // @0x98
|
||||
int width; // @0x9C bitmap width (columns)
|
||||
int height; // @0xA0 bitmap height
|
||||
int previousLevel; // @0xA4
|
||||
Scalar value; // @0xA8 (connection: the 0..1 progress)
|
||||
};
|
||||
|
||||
//#######################################################################
|
||||
// SegmentArc gauges -- needle / dial over a 270-degree arc. Derived from
|
||||
// the MUNGA L4 arc primitives (FUN_004745e0 / FUN_00473f44).
|
||||
|
||||
@@ -268,6 +268,17 @@ void *
|
||||
? (void *)((ProjectileWeapon *)weapon)->ammoBinLink.Resolve() : 0;
|
||||
}
|
||||
|
||||
// Eject-wipe gauge bridge (#118): the BallisticWeaponCluster's
|
||||
// BitMapInverseWipeScalar watches the weapon's PercentOfEject (@0x3F8, the
|
||||
// binary passes param_8+0x3f8 straight into the @004c61c8 ctor). Complete-
|
||||
// type TU per the databinding rule -- never raw-offset a compiled object.
|
||||
Scalar *
|
||||
BTWeaponPercentOfEjectPtr(void *weapon)
|
||||
{
|
||||
return (weapon != 0)
|
||||
? &((ProjectileWeapon *)weapon)->percentOfEject : 0;
|
||||
}
|
||||
|
||||
|
||||
//#############################################################################
|
||||
// Construction / Destruction
|
||||
|
||||
@@ -360,6 +360,7 @@ class NotationFile;
|
||||
AmmoBinConnection
|
||||
ammoBinLink; // @0x43C embedded 0xC connection to the AmmoBin subsystem
|
||||
friend void *BTWeaponAmmoBin(void *weapon); // panel ammo-bin bridge (AFC100-counter fix)
|
||||
friend Scalar *BTWeaponPercentOfEjectPtr(void *weapon); // eject-wipe gauge bridge (#118)
|
||||
// (ctor @004bcbb0, dtor @004bcbcf, link vtable
|
||||
// @00512424; resolved via the resource AmmoBin
|
||||
// index at +0x1C0) -- LAST field, object ends 0x448
|
||||
|
||||
Reference in New Issue
Block a user