Files
BT412/game/reconstructed/btl4gaug.hpp
T
arcattackandClaude Opus 4.8 b6918632fc gauges: reconstruct + register PlayerStatus (mission-review scoreboard)
PlayerStatus (btl4gau3) had only a BUGGY Make defined (it read raw DAT_ pools,
passed (int)entity/(int)gauge_renderer as x/y, and DROPPED the graphics_port_number
param -- the header ctor was one int short); the ctor/Execute/dtor/BecameActive and
the PlayerStatusMappingGroup + CreateMutantPixelmap8 helpers were comment-only
stubs.  Reconstructed the whole family (mapped by the playerstatus-decomp-map
workflow, 6 agents):

* methodDescription (8 params: rate,modeMask,integer player#,string font,4x color)
  + rewired Make reading parameterList[] with the port/x/y bug fixed.
* ctor (@004cb1a8): GraphicGauge base (owner folded to 0), store colours+port,
  SetOrigin, playerIndex=player_number-1, build a score NumericDisplay (fmt 2 =
  signedBlankedZeros).  Fixed the header layout: nameImage is a BitMap* (not a
  Pixmap), and two missing members (dirty@0x2F, previousStatus@0x30).
* dtor / BecameActive / TestInstance.
* PlayerStatusMappingGroup (@004c9bd0): 28 ColorMapperArmor zone tints over the
  mech-outline schematic, one per dz_* damage zone (mech->GetDamageZoneIndex),
  using the EXISTING 10-arg ColorMapperArmor ctor (per the workflow's correction --
  changing it would break cmArmor); added ColorMapper/Armor::SetColor (FUN_004c3c38,
  stores @0x6C).
* Execute (@004cb358): resolve the player (WinTesla-clean via GetMissionPlayer for
  the local player, instead of the binary's raw App+0x24 "Players"-node dictionary
  walk), then draw the score + name box + alive/dead status box; null-guarded.

KEY FINDING: PlayerStatus lives in the config's `cameraInit` block (the MISSION-
REVIEW / spectator camera cockpit), NOT `MechInit` -- so it is NOT part of the mech
cockpit and does not build during normal mech play (which is why the mech test shows
it un-regressed and its Execute never runs).  It is the post-mission scoreboard
(all 8 players' name/score/mech-status), rendered by the BTCameraDirector game
model.  Registered in BTL4MethodDescription[] so `cameraInit` builds it instead of
parse-skipping.

STATE: complete reconstruction; compiles, links (no new /FORCE unresolved), and the
mech cockpit is un-regressed (TARGET DESTROYED, 0 crashes).  NOT runtime-verified --
cameraInit is only built by the mission-review camera model, which a normal
`vehicle=<mech>` egg does not trigger.  BRING-UP STUB (marked): CreateMutantPixelmap8
returns NULL (the mech-outline recolor needs the DynamicMemoryStream read API +
Pixmap pixel-copy mapped) -> the score/name-box/status-box render but not the
recoloured mech schematic.  BT_PS_LOG traces the resolve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:13:32 -05:00

555 lines
22 KiB
C++

//===========================================================================//
// File: btl4gaug.hpp //
// Project: BattleTech Brick: Gauge Renderer Manager //
// Contents: The cockpit instrument gauge library -- the family of small //
// "widget" gauges that the BattleTech HUD is assembled from: //
// * ColorMapper* -- palette-animating tint gauges driven by //
// subsystem state (heat / armour / crit) //
// * Horiz/VertTwoPartBar, VertNormalSlider //
// -- two-colour fill bars (throttle / damage) //
// * OneOfSeveral* -- multi-frame bitmap selectors (state lamp) //
// * HeadingPointer -- rotating numeric heading readout //
// * SegmentArc270/Ratio-- arc / dial style gauges //
// * BitMap wipe gauges -- bitmap "fuel / leak" wipe indicators //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 02/22/96 CPB Initial coding. //
//---------------------------------------------------------------------------//
// Copyright (C) 1996, Virtual World Entertainment, Inc. All rights reserved //
// PROPRIETARY AND CONFIDENTIAL //
//===========================================================================//
//
// RECONSTRUCTED. No BTL4GAUG.HPP survived (the BT_L4 source tree retains only
// BTL4RDR.HPP and BTL4GALM.HPP). This declaration is recovered from the
// shipped binary (BTL4OPT.EXE); the Ghidra pseudo-C is in
// recovered/all/part_013.c (@004c2f94..@004c5e84)
// recovered/all/part_014.c (@004c5e84..@004c6394)
// The module is the .obj that links immediately after btl4rdr.obj and before
// btl4gau2.obj (see BTL4.MAK), so it owns the contiguous code run
// [ @004c2f94 .. @004c6771 ] (btl4gau2.cpp begins at @004c6798).
//
// The sole address with a surviving embedded assert path -- the anchor --
// is ColorMapperHeat's constructor:
// @004c3f6c "d:\tesla\bt\bt_l4\BTL4GAUG.CPP", line 0x68a
//
// Class names are recovered verbatim from the CODE name-string pool
// (section_dump.txt @0x518480..0x51859e):
// ColorMapper ColorMapperArmor ColorMapperMultiArmor ColorMapperCritical
// ColorMapperHeat HorizTwoPartBar VertTwoPartBar VertNormalSlider
// OneOfSeveral OneOfSeveralInt OneOfSeveralPixInt OneOfSeveralStates
// HeadingPointer BitMapInverseWipe BitMapInverseWipeScalar
// SegmentArc270 SegmentArcRatio
//
// Field offsets in comments are the byte offsets observed in the decompiled
// object (e.g. "@0x90" == this[0x24]). The two base gauges are the MUNGA L4
// gauge primitives:
// Gauge ctor FUN_00444308 dtor FUN_00444360 -- non-graphic;
// used by the ColorMapper family (they tint a palette, they
// do not own a GraphicsView).
// GraphicGauge ctor FUN_00444818 dtor FUN_00444870 -- owns a GraphicsView
// embedded at +0x48 (this[0x12]); used by every drawing gauge.
// (Same base as MapDisplay in btl4rdr.cpp.)
//
// Names flagged "(name unconfirmed)" are best-effort: the class exists in the
// binary but its identification string could not be tied to it unambiguously.
//
#if !defined(BTL4GAUG_HPP)
# define BTL4GAUG_HPP
# if !defined(SLOT_HPP)
# include <slot.hpp>
# endif
# if !defined(L4GREND_HPP)
# include <l4grend.hpp>
# endif
# if !defined(L4GAUGE_HPP)
# include <l4gauge.hpp>
# endif
# if !defined(HEAT_HPP)
# include <heat.hpp>
# endif
// NOTE (reconstruction): the original BTL4GAUG.HPP also pulled in
// <powersub.hpp>, but nothing in this gauge-widget family actually depends
// on a power-subsystem type (the gauges connect to subsystems by name /
// attribute index, not by class). The include is dropped to keep this TU
// independent of the (separately-reconstructed) powersub header.
//#######################################################################
//#######################################################################
// ColorMapper family
//
// A ColorMapper is NOT a drawing gauge: every frame it reads a single
// subsystem-derived value (0..255), looks the corresponding R/G/B
// triple out of a named palette resource, and -- if it has changed --
// writes it straight into one hardware palette slot (colorIndex). The
// cockpit art that uses that slot therefore changes colour live. With
// two palette names it alternates between them every frame to flash.
//
// The driving value arrives through a small file-private GaugeConnection
// (see btl4gaug.cpp) wired to currentColorIndex. ColorMapperHeat's
// connection reads HeatableSubsystem::currentTemperature (@0x114).
//#######################################################################
//#######################################################################
class ColorMapper :
public Gauge
{
public:
ColorMapper(
GaugeRate rate,
ModeMask mode_mask,
L4GaugeRenderer *renderer,
int graphics_port_number,
int color_index,
const char *palette_name_a,
const char *palette_name_b,
const char *identification_string
);
~ColorMapper();
Logical TestInstance() const; // inherits Gauge
void BecameActive();
void Execute(); // @004c3980 -- palette push
protected:
char *paletteName[2]; // @0x48,@0x4C this[0x12],[0x13]
Logical twoColorMode; // @0x50 this[0x14] (names differ)
int currentColorIndex; // @0x54 this[0x15] (connection dst)
int previousColorIndex; // @0x58 this[0x16]
int colorSlot; // @0x5C this[0x17] hw palette slot
int paletteToggle; // @0x60 this[0x18] 0/1 flash phase
unsigned char
previousRed, // @0x64 this[0x19].b0
previousGreen, // @0x65
previousBlue; // @0x66
GraphicsPort
*graphicsPort; // @0x68 this[0x1A] (renderer port)
};
//
// ColorMapperArmor -- tints by the damage ratio of one named damage zone.
// Connection: @004c33a4 (reads Subsystem damage-zone array @0x120[index]).
//
class ColorMapperArmor :
public ColorMapper
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "cmArmor".
static MethodDescription methodDescription;
static Logical Make( // @004c3aa4
int display_port_index, Vector2DOf<int> position,
Entity *entity, GaugeRenderer *gauge_renderer);
ColorMapperArmor( // @004c3b98
GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer,
int graphics_port_number, int color_index,
Entity *entity, const char *palette_a, const char *palette_b,
int damage_zone_index, const char *identification_string);
~ColorMapperArmor();
// @004c3c38 -- store the group state into @0x6C (used by PlayerStatusMappingGroup
// to push the mech's disabled/enabled state onto all 28 zone tints).
void SetColor(int state) { unused = state; }
protected:
int unused; // @0x6C this[0x1B] (state slot; SetColor writes it)
};
//
// ColorMapperMultiArmor -- worst-of-N damage zones.
// Connection: @004c346c (copies 8 zone indices, takes the max ratio).
//
class ColorMapperMultiArmor :
public ColorMapper
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "colorMapperMultiArmor".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c3c48
ColorMapperMultiArmor( // @004c3d60
GaugeRate, ModeMask, L4GaugeRenderer *, int, int,
Entity *, const char *, const char *,
const int *zone_indices /*[8]*/, const char *);
~ColorMapperMultiArmor();
};
//
// ColorMapperCritical -- tints by a subsystem's operational state.
// Connection: @004c3598 (subsystem operational flag @0x40 -> 100, else value).
//
class ColorMapperCritical :
public ColorMapper
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "cmCrit".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c3ddc
ColorMapperCritical( // @004c3e40
GaugeRate, ModeMask, L4GaugeRenderer *, int, int,
Entity *, const char *, const char *,
const char *subsystem_name, const char *);
~ColorMapperCritical();
};
//
// ColorMapperHeat -- the cockpit heat tint. ANCHOR @004c3f6c.
// Connection: @004c3664 (reads HeatableSubsystem::currentTemperature@0x114
// as a 0..100 percentage). The named subsystem must derive from a heat
// class (IsDerivedFrom 0x50e3ec or 0x50e604) or the ctor asserts (line 0x68a).
//
class ColorMapperHeat :
public ColorMapper
{
public:
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c3f08
ColorMapperHeat( // @004c3f6c
GaugeRate, ModeMask, L4GaugeRenderer *, int, int,
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;
};
//#######################################################################
//#######################################################################
// Two-part fill bars (: GraphicGauge)
//
// A bar is split into a "filled" colour and a "background" colour at a
// pixel computed from a Scalar input. HorizTwoPartBar grows left->right,
// VertTwoPartBar grows bottom->top. Both blit a tile bitmap behind the
// fill (FUN_004c2ff8 = tiled blit helper).
//#######################################################################
//#######################################################################
class HorizTwoPartBar :
public GraphicGauge
{
public:
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c407c
HorizTwoPartBar( // @004c4170
GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number,
int left, int bottom, int right, int top,
const char *tile_image,
int fill_color, int background_color,
Scalar *value_pointer, Scalar *low_pointer, Scalar *high_pointer,
const char *identification_string);
~HorizTwoPartBar();
Logical TestInstance() const;
void BecameActive(); // @004c4324
void Execute(); // @004c4340
protected:
Scalar value; // @0x90 this[0x24] (connection)
Scalar low; // @0x94 this[0x25]
Scalar high; // @0x98 this[0x26]
char *tileImage; // @0x9C this[0x27]
int fillColor; // @0xA0 this[0x28]
int backgroundColor; // @0xA4 this[0x29]
int width; // @0xA8 this[0x2A] right-left
int height; // @0xAC this[0x2B] (top-bottom)-1
int previousFill; // @0xB0 this[0x2C]
int previousFull; // @0xB4 this[0x2D]
};
class VertTwoPartBar :
public GraphicGauge
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "vertBar".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c462c
VertTwoPartBar( // @004c4724 (ctor arg order = binary param_12..17)
GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number,
int left, int bottom, int right, int top,
const char *tile_image,
int background_color, int fill_color, int extra_color,
Scalar *value_pointer, Scalar *low_pointer, Scalar *high_pointer,
const char *identification_string);
~VertTwoPartBar();
Logical TestInstance() const;
void BecameActive(); // @004c48e0
void Execute(); // @004c48fc
protected:
char *tileImage; // @0x90 this[0x24]
int backgroundColor; // @0x94 this[0x25]
int fillColor; // @0x98 this[0x26]
int extraColor; // @0x9C this[0x27]
int width; // @0xA0 this[0x28] (right-left)-1
int height; // @0xA4 this[0x29] top-bottom
Scalar value; // @0xB0 this[0x2C] (connection)
Scalar low; // @0xB4 this[0x2D]
Scalar high; // @0xB8 this[0x2E]
int previousFill; // @0xA8 this[0x2A]
int previousFull; // @0xAC this[0x2B]
};
//
// VertNormalSlider -- a single-colour rectangle whose height tracks a
// normalised [0,1] value (clamped at @004c4cc0). (name unconfirmed but
// matches the "VertNormalSlider" pool entry and the vtable ordering.)
//
class VertNormalSlider :
public GraphicGauge
{
public:
VertNormalSlider( // @004c4b84
GaugeRate, ModeMask, L4GaugeRenderer *, int,
int left, int bottom, int right, int top,
int fill_color, int background_color, int baseline,
Scalar *value_pointer, const char *identification_string);
~VertNormalSlider();
Logical TestInstance() const;
void BecameActive(); // @004c4cac
void Execute(); // @004c4cc0 -> Draw @004c4d48
protected:
Scalar value; // @0xAC this[0x2B] (connection)
int previousFill; // @0x90 this[0x24]
int fillColor; // @0x94 this[0x25]
int backgroundColor; // @0x98 this[0x26]
int baseline; // @0x9C this[0x27]
int width; // @0xA0 this[0x28]
int height; // @0xA4 this[0x29]
int span; // @0xA8 this[0x2A]
};
//#######################################################################
//#######################################################################
// OneOfSeveral family -- multi-frame bitmap selectors
//
// A strip bitmap holds N frames; the gauge blits whichever frame the
// driving value selects. The base (OneOfSeveral) stores the strip and
// the per-frame size; subclasses add the value source.
//#######################################################################
//#######################################################################
class OneOfSeveral :
public GraphicGauge
{
public:
OneOfSeveral( // @004c4d88
GaugeRate, ModeMask, L4GaugeRenderer *, int,
int x, int y, Logical from_image_strip,
const char *image_name,
int /*reserved*/, int /*reserved*/,
int columns, int rows,
const char *identification_string);
~OneOfSeveral();
Logical TestInstance() const;
void BecameActive(); // @004c4f14
void Execute(); // @004c4f28
protected:
Logical fromImageStrip; // @0x90 this[0x24]
char *imageName; // @0x94 this[0x25]
int backgroundColor; // @0x98 this[0x26]
int foregroundColor; // @0x9C this[0x27] (DrawBitMapOpaque bg arg)
int columns; // @0xA0 this[0x28]
int selected; // @0xAC this[0x2B] (connection)
int previousSelected; // @0xB0 this[0x2C]
int frameWidth; // @0xA4 this[0x29]
int frameHeight; // @0xA8 this[0x2A]
};
//
// OneOfSeveralInt -- frame = a direct int input. @004c5148 Make @004c5068
//
class OneOfSeveralInt :
public OneOfSeveral
{
public:
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *);
OneOfSeveralInt(GaugeRate, ModeMask, L4GaugeRenderer *, int,
int x, int y, const char *image, int columns, int rows,
int *value_pointer, const char *);
~OneOfSeveralInt(); // @004c51d8
};
//
// OneOfSeveralPixInt -- frame from a packed-pixel int strip.
// @004c52d8 Make @004c5204
//
class OneOfSeveralPixInt :
public OneOfSeveral
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "oneOfSeveralPixInt".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c5204
OneOfSeveralPixInt( // @004c52d8
GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number,
int x, int y, const char *image, int columns, int rows,
int *value_pointer, const char *identification_string);
~OneOfSeveralPixInt(); // @004c5364
};
//
// OneOfSeveralStates -- frame from a subsystem state value (clamped >=0).
// @004c5470 Make @004c5390 Execute clamp @004c552c
// Connection: @004c3324 (reads Subsystem state @0x14).
//
class OneOfSeveralStates :
public OneOfSeveral
{
public:
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *);
OneOfSeveralStates(GaugeRate, ModeMask, L4GaugeRenderer *, int,
int x, int y, const char *image, int columns, int rows,
Entity *subsystem_source, const char *);
~OneOfSeveralStates(); // @004c5500
void BecameActive(); // @004c552c
};
//#######################################################################
// HeadingPointer -- a rotating numeric compass-heading readout.
// @004c562c Make @004c554c Execute @004c5914 (rotate by torso yaw,
// degrees = radian * 57.2958). Owns a NumericDisplay (@0x44 object,
// FUN_004700bc) and a GraphicsViewRecord (@0xA4) for erase tracking.
//#######################################################################
class HeadingPointer :
public GraphicGauge
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "headingPointer".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c554c
//
// The binary ctor (@004c562c) takes 14 args -- the earlier header collapsed
// owner_ID/graphics_port_number and merged the colours. Also: fields
// @0x98/@0x9C are the needle's inner/outer RADIUS (Execute multiplies them
// by sin/cos of the heading), NOT a colour+spacing as the old names implied.
//
HeadingPointer( // @004c562c
GaugeRate, ModeMask, L4GaugeRenderer *,
unsigned int owner_ID,
int graphics_port_number,
int x, int y,
int needle_color, // -> @0x90 the drawn needle colour
int numeric_color, // -> NumericDisplay foreground (readout)
int erase_color, // -> @0x94 erase + NumericDisplay background
int inner_radius, // -> @0x98 needle near-end radius
int outer_radius, // -> @0x9C needle tip radius (the change key)
const char *font_name,
const char *identification_string = "HeadingPointer");
~HeadingPointer(); // @004c573c
Logical TestInstance() const;
void ShowInstance(char *indent); // @004c57d0
void BecameActive(); // @004c58e8
void Execute(); // @004c5914
protected:
int needleColor; // @0x90 needle (fg) colour
int eraseColor; // @0x94 erase (bg) colour
int innerRadius; // @0x98 needle near-end radius
int outerRadius; // @0x9C needle tip radius (change key)
char *imageName; // @0xA0 interned font name
GraphicsViewRecord
previousDrawing; // @0xA4 erase-tracking record
int previousX; // @0xB4
int previousY; // @0xB8
NumericDisplay
*numericDisplay; // @0xBC
};
//#######################################################################
// BitMap "wipe" gauges -- a fuel/leak style bar that reveals a bitmap up
// to a level. (names "BitMapInverseWipe" / "BitMapInverseWipeScalar" are
// best-effort from the pool; the Scalar variant adds a GaugeConnection.)
// BitMapInverseWipe @004c5b7c (used as a base by btl4gau2 LeakGauge)
// <wipe base> @004c5e84
// <wipe base>Scalar @004c61c8 (adds GaugeConnectionDirectOf<Scalar>)
//#######################################################################
class BitMapInverseWipe : // (name unconfirmed)
public GraphicGauge
{
public:
BitMapInverseWipe( // @004c5b7c
GaugeRate, ModeMask, L4GaugeRenderer *, int,
int x, int y, const char *image,
int color_a, int color_b, int third, int frames,
Scalar *value_pointer, const char *);
~BitMapInverseWipe(); // @004c5c80
Logical TestInstance() const;
void BecameActive(); // @004c5cf4
void Execute(); // @004c5d08
protected:
char *imageName; // @0x90 this[0x24]
int colorA; // @0x94 this[0x25]
int colorB; // @0x98 this[0x26]
int fullWidth; // @0x9C this[0x27] frames*2
int frameWidth; // @0xA8 this[0x2A] width/3
int frameHeight; // @0xA4 this[0x29]
int frames; // @0xA0 this[0x28]
int previousLevel; // @0xAC this[0x2B]
int third; // @0xB0 this[0x2C]
Scalar value; // @0xB4 this[0x2D] (connection)
};
//#######################################################################
// SegmentArc gauges -- needle / dial over a 270-degree arc. Derived from
// the MUNGA L4 arc primitives (FUN_004745e0 / FUN_00473f44).
// SegmentArc270 @004c6244
// SegmentArcRatio @004c6394 Make @004c62fc (adds two connections)
//#######################################################################
class SegmentArc270 :
public SegmentArc // MUNGA L4 base (FUN_004745e0; engine class SegmentArc)
{
public:
SegmentArc270(/* ...arc geometry... */); // @004c6244
~SegmentArc270(); // dtor thunk @004c66b3
protected:
int segmentSpan; // @0xC0 this[0x30]
};
class SegmentArcRatio :
public SegmentArc // MUNGA L4 base (engine SegmentArc, L4GAUGE.cpp:4827)
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "segmentArcRatio".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c62fc
SegmentArcRatio( // @004c6394
GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number,
int center_x, int center_y,
Scalar inner, Scalar outer, Scalar deg0, Scalar deg1,
int number_of_segs, int background_color, int foreground_color,
Scalar *numerator_pointer, Scalar *denominator_pointer,
const char *identification_string);
~SegmentArcRatio(); // dtor thunk @004c668d
void Execute(); // @004c6488 (ratio -> currentValue -> SegmentArc::Execute)
protected:
Scalar numerator; // @0xC0 this[0x30] (connection)
Scalar denominator; // @0xC4 this[0x31] (connection)
Scalar segmentSpan; // @0xC8 this[0x32] (float, read by Execute)
};
#endif