//===========================================================================// // File: btl4gaug.cpp // // Project: BattleTech Brick: Gauge Renderer Manager // // Contents: The cockpit instrument gauge library (see btl4gaug.hpp). // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 02/22/96 CPB Initial coding. // //---------------------------------------------------------------------------// // Copyright (C) 1996, Virtual World Entertainment, Inc. All rights reserved // // PROPRIETARY AND CONFIDENTIAL // //===========================================================================// // // RECONSTRUCTED from the shipped binary (BTL4OPT.EXE). Behaviour follows the // Ghidra pseudo-C in recovered/all/part_013.c (@004c2f94..@004c5e84) and // part_014.c (@004c5e84..@004c6394). Class/member names are taken from the // CODE name-string pool; per-method @ADDR evidence is cited inline. // // Translation-unit extent (linked between btl4rdr.obj and btl4gau2.obj): // first gaug code @004c2f94 (DrawTiledBitmap helper @004c2ff8) // anchor @004c3f6c ColorMapperHeat::ColorMapperHeat // assert "...\BTL4GAUG.CPP" line 0x68a // last gaug code @004c6394 SegmentArcRatio (+ dtor thunks @004c66xx) // btl4gau2 begins @004c6798 (SeekVoltage gauge, string pool 0x5199xx) // // Engine-internal helper map (consistent with btl4rdr.cpp's table): // FUN_00444308 Gauge ctor FUN_00444360 Gauge dtor // FUN_004443a4 Gauge::TestInstance // FUN_00444124 GaugeConnection ctor FUN_00444148 GaugeConnection dtor // FUN_00444818 GraphicGauge ctor FUN_00444870 GraphicGauge dtor // FUN_004448ac GraphicGauge::TestInstance FUN_004448bc ::ShowInstance // FUN_004700ac GaugeRenderer::InternName(name) -> char* (interned) // FUN_00447f84 GaugeRenderer::GetGraphicsPort(renderer, portNumber) // FUN_00442f6a / 00443090 PaletteCache AddRef / Release (renderer+0x4C, +0x3c) // FUN_00442aec / 00442c12 BitMapCache AddRef / Release (renderer+0x4C, +4) // FUN_00442d2b / 00442e51 PixMapCache AddRef / Release (renderer+0x4C, +0x20) // FUN_0041f98c Entity::FindSubObject(name) // FUN_0041bfc0 Entity::FindAttributeIndex(name) (btl4gau2) // FUN_0041a1a4 IsDerivedFrom(classDerivations) // FUN_00417ab4 SharedData::Resolve() / operator Entity* // FUN_0042076c Subsystem::FindDamageZone(record) -> index // FUN_004dcd94 Round(Scalar) -> int // FUN_0040385c Verify(msg,file,line) // FUN_004dbb24 DebugStream << (char*) FUN_004db78c DebugStream << (int) // FUN_004d4b58 stricmp FUN_004d49b8 strcat // FUN_00402298 operator new FUN_004022e8/004022d0 operator delete // FUN_00474855 GaugeConnectionDirectOf::ctor // FUN_004749de GaugeConnectionDirectOf::ctor // FUN_0040954c Quaternion->Euler FUN_00408328 SinCos // FUN_004700bc NumericDisplay ctor FUN_0047018c ::dtor // FUN_004703f4 NumericDisplay::Reset FUN_00470430 ::DrawAt // FUN_0044a5b4/5dc GraphicsViewRecord ctor/dtor // FUN_0044a650/630 GraphicsViewRecord Erase / Reset // DAT_00524e20 DebugStream (warning channel) // 0x50e3ec / 0x50e604 heat-subsystem class-derivation tables // // GraphicsView vtable slots used below (this+0x48): // +0x08 SetExtent +0x10 SetOrigin +0x18 SetColor +0x24 MoveTo // +0x38 LineTo/FillTo +0x48 FillTo +0x4C Fill +0x54 Blit // +0x58 BlitClipped +0x5C BlitStretched +0x64 BeginClip +0x68 EndClip // // Recovered constant-pool floats (section_dump.txt): // _DAT_004c452c = 0.0f (HorizTwoPartBar low clamp) // _DAT_004c4b00 = 0.0f (VertTwoPartBar low clamp) // _DAT_004c4d3c = 0.0f , _DAT_004c4d40 = 1.0f (VertNormalSlider [0,1] clamp) // _DAT_004c5acc = 0.0f , 0x42652ee1 = 57.2958f (HeadingPointer rad->deg) // _DAT_004c62d4 = 0.75f, _DAT_004c6484 = 0.75f (arc span fraction) // _DAT_0050e3d8 = 0.0025f (OneOfSeveralStates 'critical' threshold) // FUN_004c2f88() returns 7 (shared blip colour, defined in btl4rdr.cpp) // // The shared reconstruction headers heat.hpp and mechrecon.hpp each define a // `DebugStream` diagnostic sink, and they do so incompatibly (std::ostream& // vs the ReconStream artifact struct). heat.hpp's variant is the optional one // (gated on this guard); pre-defining it keeps the single mechrecon ReconStream // definition active throughout this translation unit and avoids the clash. #define BT_DEBUGSTREAM_DEFINED #include #pragma hdrstop #if !defined(BTL4GAUG_HPP) # include #endif #if !defined(APP_HPP) # include #endif static const Scalar ZeroClamp = 0.0f; // _DAT_004c452c / _DAT_004c4b00 static const Scalar OneClamp = 1.0f; // _DAT_004c4d40 static const Scalar RadiansToDegrees = 57.2958f; // 0x42652ee1 static const Scalar StatesCriticalLevel = 0.0025f; // _DAT_0050e3d8 //########################################################################### // Reconstruction shims (file-local) // // The shipped BT engine exposed a warning DebugStream (DAT_00524e20) and a // GaugeRenderer name-intern + palette/bitmap cache. In the WinTesla engine // these headers describe, the same concepts are reached through std::cerr and // the Warehouse resource bins (Warehouse::palette8Bin), so the bodies below // bind to those. Behaviour is preserved; only the access path differs. // (The DebugStream warning channel, DAT_00524e20, is provided by the shared // reconstruction headers -- heat.hpp / mechrecon.hpp.) //########################################################################### // // HeatConnection (file-private) -- FUN_004c3664. Drives a ColorMapper's colour // index from a heat-bearing subsystem's live temperature. Only its // construction is emitted in this translation unit (ColorMapperHeat's ctor); // the per-frame Transfer() is summarised in the notes above. // class HeatConnection : public GaugeConnection { public: HeatConnection(int *destination, Subsystem *source) : GaugeConnection(0), currentColorIndex(destination), heatSubsystem(source) {} protected: // // The per-frame data feed -- @004c3720 (the recovered "Transfer"). The // gauge framework calls this once per frame (Plug/GaugeConnection vtbl // virtual Update()). It samples the live heat-subsystem temperature and // writes the colour index that ColorMapper::Execute then pushes into the // hardware palette slot, tinting the cockpit heat art. // // no subsystem -> 100 (fail safe: full tint) // subsystem destroyed -> 100 (recovered "+0x40 == 1" path) // else -> Round(currentTemperature @0x114) // // NOTE (flagged): the recovered code rounds the value resolved at +0x114 // (currentTemperature) directly into the 0..255 colour index that // ColorMapper::Execute then clamps to [0,255]. The exact temperature-> // percentage scaling (HEAT.TCP seeds currentTemperature at 300.0) is a // tuning detail that a human should reconcile against the original // l4gauge.cfg palette ramp; the live data path itself is correct. // void Update(); // override int *currentColorIndex; // destination@0x18 Subsystem *heatSubsystem; // source@0x10 }; // // Round-to-nearest (engine Round @004dcd94). // static inline int HeatRound(Scalar value) { return (int)((value >= 0.0f) ? (value + 0.5f) : (value - 0.5f)); } void HeatConnection::Update() { if (heatSubsystem == NULL) { *currentColorIndex = 100; return; } // // The ctor verified heatSubsystem derives from HeatableSubsystem / HeatSink // (BTL4GAUG.CPP:0x68a), so the live temperature and damage state are reached // through the HeatableSubsystem layout. // HeatableSubsystem *heat = (HeatableSubsystem *)heatSubsystem; if (heat->IsDamaged()) // inherited MechSubsystem simulationState (+0x40) -> max tint { *currentColorIndex = 100; return; } *currentColorIndex = HeatRound(heat->currentTemperature); // +0x114 live feed } //########################################################################### //########################################################################### // File-private GaugeConnection subclasses // // Every gauge in this module that is "driven by mech subsystem state" is fed // through one of these tiny GaugeConnection objects. The base GaugeConnection // stores source@0x10 and destination@0x14 ; Transfer() (a virtual) computes // a value from the source and writes it to *destination. The gauge framework // calls Transfer() once per frame (after AddConnection, vtbl+0x34). // // Layouts below use the ctor argument order: // FUN_00444124(this, 0) base ctor // this[4] (@0x10) = source this[5] (@0x14) = destination // BEST-EFFORT: these classes were emitted alongside the gauges that own them // and share this translation unit; their template spelling is inferred. //########################################################################### //########################################################################### // // SubsystemTorsoHeading connection -- @004c3134 ctor / @004c31a0 Transfer. // If the owner mech's torso (sub-object index 3) is present, copies its // heading (entity+0x1d4) to the destination, else 0. // // SubsystemNameId connection -- @004c31ec / @004c3258 (video-object+0x1e0). // SubsystemAltId connection -- @004c3288 / @004c32f4 (video-object+0x1dc). // (These three feed numeric / heading readouts.) // // StateConnection -- @004c3324 / @004c3390 -- copies Subsystem state@0x14. // Used by OneOfSeveralStates. // // ArmorZoneConnection -- @004c33a4 ctor / @004c3430 Transfer. // ctor resolves a single damage-zone slot: source = Subsystem.zones@0x120 // [zone_index] (or 0 if zone_index < 0). Transfer: no zone -> 100, else the // zone's damage percentage (Round). Used by ColorMapperArmor. // // MultiArmorConnection -- @004c346c ctor / @004c34f4 Transfer. // ctor copies 8 zone indices (this[6..13]). Transfer scans the 8 zones, // keeps the maximum damage ratio (zone+0x158), Round -> destination (100 if // none present). Used by ColorMapperMultiArmor. // // CriticalConnection -- @004c3598 ctor / @004c3610 Transfer. // source = a Subsystem. Transfer: no subsystem -> 0 ; subsystem operational // (subsys+0x40 == 1) -> 100 ; else Round of the live value. // Used by ColorMapperCritical. // // HeatConnection -- @004c3664 ctor / @004c3720 Transfer. // ctor: source = the heat subsystem; flag@0x14 records whether it is the // "primary" heat class (IsDerivedFrom 0x50e3ec) vs. the alternate (0x50e604); // destination@0x18. Transfer: no subsystem -> 100 ; subsystem operational // (+0x40 == 1) -> 100 ; else Round( Resolve(subsys+0x114 currentTemperature) ). // Used by ColorMapperHeat -- THIS is the heat gauge's data feed. // // All Transfer bodies are reproduced inside the owning gauge's notes; the // connection classes themselves are otherwise trivial (TestInstance returns // True; ShowInstance is the inherited no-op). // //########################################################################### //########################################################################### // ColorMapper (base : Gauge) //########################################################################### //########################################################################### // // @004c37dc -- vtable PTR_FUN_00518e10, base name passed through to Gauge. // Interns both palette names, flags twoColorMode when they differ, validates // that both palettes exist in the renderer's palette cache (warns otherwise), // stashes the target hardware palette slot and resolves the renderer graphics // port that owns the live palette. // ColorMapper::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 ): Gauge(rate, mode_mask, renderer, /*owner*/0, identification_string) // FUN_00444308 { // Original interned a private copy of each name (FUN_004700ac); the resource // strings handed in here persist for the gauge's lifetime, so retain them. paletteName[0] = (char *)palette_name_a; paletteName[1] = (char *)palette_name_b; twoColorMode = (stricmp(paletteName[0], paletteName[1]) != 0); // FUN_004d4b58 // // Reference each palette in the warehouse palette bin (AddRef + load); a // missing palette is a content warning. (PaletteCache AddRef = FUN_00442f6a.) // WarehouseBinOf &palettes = renderer->warehousePointer->palette8Bin; for (int i = 0; i < 2; ++i) { if (palettes.Get(paletteName[i]) == NULL) { DebugStream << "ColorMapper cannot find palette " << paletteName[i] << "\n"; } } previousColorIndex = -1; // this[0x16] (force first push) previousRed = previousGreen = previousBlue = 0; colorSlot = color_index; // this[0x17] paletteToggle = 0; // this[0x18] graphicsPort = renderer->GetGraphicsPort(graphics_port_number); // FUN_00447f84 -> this[0x1A] } // // @004c38dc -- release both palette refs, then Gauge::~Gauge. (The deleting // destructor thunks @004c66ff / @004c6725 / @004c674b / @004c6771 forward here // for the four derived ColorMappers.) // ColorMapper::~ColorMapper() { WarehouseBinOf &palettes = renderer->warehousePointer->palette8Bin; for (int i = 0; i < 2; ++i) { palettes.Release(paletteName[i]); // FUN_00443090 (release the AddRef'd palette) paletteName[i] = NULL; // names not separately owned (no intern copy) } // base ~Gauge -- FUN_00444360 } // // @004c3980 -- the palette push. Clamp the driving value to a [0,255] colour // index, choose the active palette (flashing alternates the two when // twoColorMode), read the index's R/G/B triple and, if it differs from last // frame, write it straight into the hardware palette slot. (Called by every // derived ColorMapper's Execute -- e.g. ColorMapperCritical::Execute @004c3c1c.) // void ColorMapper::Execute() { if (currentColorIndex > 254) currentColorIndex = 255; if (currentColorIndex < 1) currentColorIndex = 0; if (!twoColorMode) { if (currentColorIndex == previousColorIndex) { return; // nothing changed } previousColorIndex = currentColorIndex; } else { // // Flash: alternate palette 0 / palette 1 every frame. // if (++paletteToggle > 1) { paletteToggle = 0; } } WarehouseBinOf &palettes = renderer->warehousePointer->palette8Bin; Palette8 *palette = palettes.GetIfAlreadyExists(paletteName[paletteToggle]); // FUN_00442f6a (peek, no AddRef) if (palette != NULL) { // // Read the index's R/G/B triple and, if it differs from last frame, // push it straight into the hardware palette slot. // PaletteTriplet &entry = palette->Color[currentColorIndex]; if (previousRed != entry.Red || previousGreen != entry.Green || previousBlue != entry.Blue) { previousRed = entry.Red; previousGreen = entry.Green; previousBlue = entry.Blue; graphicsPort->SetColor(&entry, colorSlot); // GraphicsPort::SetColor(PaletteTriplet*,int) } } } //########################################################################### // ColorMapperArmor @004c3aa4 Make / @004c3b98 ctor //########################################################################### // // @004c3aa4 -- Make: build the damage-zone descriptor (DAT_00514414), look up // the zone index on the host subsystem (FUN_0042076c; warns "damage zone ... // not found" on failure), allocate (0x70) and construct a ColorMapperArmor // wired with an ArmorZoneConnection. // // @004c3b98 -- ctor: ColorMapper base (vtable PTR_FUN_00518dcc), this[0x1B]=0, // then AddConnection(new ArmorZoneConnection(¤tColorIndex, subsystem, // zone_index)) (connection ctor FUN_004c33a4, 0x18 bytes). // // ArmorZoneConnection::Transfer @004c3430: // if (source == 0) *destination = 100; // else *destination = Round(); // //########################################################################### // ColorMapperMultiArmor @004c3c48 Make / @004c3d60 ctor //########################################################################### // // @004c3c48 -- Make: probe an 8-entry table of damage-zone descriptors // (DAT_00514704 stride 0x44); requires at least one to resolve (else warns // "No Damage zones found"). Allocates (0x6c) and constructs. // // @004c3d60 -- ctor: ColorMapper base (vtable PTR_FUN_00518d88) + // AddConnection(new MultiArmorConnection(¤tColorIndex, subsystem, // zoneIndices[8])) (connection ctor FUN_004c346c, 0x3c bytes). // // MultiArmorConnection::Transfer @004c34f4: // scan the 8 zones; keep the worst (max) damage ratio (zone+0x158); // no zone present -> *destination = 100, else Round(worst). // //########################################################################### // ColorMapperCritical @004c3ddc Make / @004c3e40 ctor //########################################################################### // // @004c3ddc -- Make: allocate (0x6c) and construct with the four resource // strings (DAT_00514b50..) and name "ColorMapperCritical". // // @004c3e40 -- ctor: ColorMapper base (vtable PTR_FUN_00518d44). Resolves the // named subsystem via Entity::FindSubObject (FUN_0041f98c); warns // "ColorMapperCritical warning: subsystem ... does not exist" if absent. // AddConnection(new CriticalConnection(¤tColorIndex, subsystem)) // (connection ctor FUN_004c3598, 0x18 bytes). // // CriticalConnection::Transfer @004c3610: // no subsystem -> *destination = 0; // subsystem +0x40 == 1 -> *destination = 100; (fully operational) // else -> *destination = Round(); // //########################################################################### // ColorMapperHeat @004c3f08 Make / @004c3f6c ctor *** ANCHOR *** //########################################################################### // // @004c3f08 -- Make. Allocate a ColorMapperHeat (0x6c bytes) and construct it // from the four resource strings (DAT_00514f98 rate, DAT_00514fdc mode-mask, // 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.) // 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 Logical ColorMapperHeat::Make( int /*display_port_index*/, Vector2DOf /*position*/, Entity *entity, GaugeRenderer *gauge_renderer ) { ColorMapperHeat *gauge = (ColorMapperHeat *)operator new(0x6c); // FUN_00402298(0x6c) if (gauge != NULL) { new (gauge) ColorMapperHeat( // FUN_004c3f6c (GaugeRate)DAT_00514f98, (ModeMask) DAT_00514fdc, (L4GaugeRenderer *)gauge_renderer, DAT_00515020, // graphics port number (int)&DAT_00515064, // colour slot resource entity, (char *)&DAT_005150a8, // palette name A (char *)&DAT_005150ec, // palette name B (char *)"ColorMapperHeat", // subsystem name resource / id "ColorMapperHeat"); } return True; } // // @004c3f6c -- ColorMapperHeat constructor. THE ANCHOR (assert path // "d:\tesla\bt\bt_l4\BTL4GAUG.CPP", line 0x68a). // // Builds a ColorMapper (vtable PTR_FUN_00518d00), locates the named heat // subsystem on the host entity, *verifies it is a heat-bearing subsystem*, // and wires a HeatConnection so that the subsystem's live temperature drives // the palette tint. // ColorMapperHeat::ColorMapperHeat( GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer, int graphics_port_number, int color_index, Entity *entity, const char *palette_a, const char *palette_b, const char *heat_subsystem_name, const char *identification_string ): ColorMapper( // FUN_004c37dc rate, mode_mask, renderer, graphics_port_number, color_index, palette_a, palette_b, identification_string) { // vtable already set to PTR_FUN_00518d00 by this point. Subsystem *subsystem = entity->FindSubsystem(heat_subsystem_name); // FUN_0041f98c if (subsystem == NULL) { DebugStream << "ColorMapperHeat warning: subsystem " << heat_subsystem_name << " does not exist\n"; // FUN_004dbb24 x3 } else { // // The subsystem MUST be heat-bearing (one of the two heat class // derivation tables) or the data feed is meaningless. // if (!subsystem->IsDerivedFrom(*HeatableSubsystem::GetClassDerivations()) && // FUN_0041a1a4 (0x50e3ec) !subsystem->IsDerivedFrom(*HeatSink::GetClassDerivations())) // (0x50e604) { Verify(False); // FUN_0040385c -- "Bad subsystem type" BTL4GAUG.CPP:0x68a } } // // HeatConnection: copies the subsystem's currentTemperature (@0x114) into // ColorMapper::currentColorIndex every frame (clamped to 0..100; 100 when // the subsystem is absent or fully operational). // HeatConnection *connection = (HeatConnection *)operator new(0x1c); // FUN_00402298(0x1c) if (connection != NULL) { new (connection) HeatConnection( // FUN_004c3664 ¤tColorIndex, subsystem); } AddConnection(connection); // (*this+0x34) } //########################################################################### //########################################################################### // Two-part fill bars //########################################################################### //########################################################################### // // @004c2ff8 -- DrawTiledBitmap helper (file-private, shared by both bars). // Tiles a source bitmap (param_7) across a destination rectangle, clipping the // last partial row/column. For each tile it MoveTo()s (vtbl+0x24) and issues // a clipped blit (vtbl+0x58). bitmap tile size is read from src+0x0C (width) // and src+0x10 (height). // static void DrawTiledBitmap( GraphicsView *view, int originX, int originY, int width, int height, int color, BitMap *tile ) { int tileW = tile->Data.Size.x; // +0x0C int tileH = tile->Data.Size.y; // +0x10 (void)color; // (BlitClipped colour arg has no GraphicsView::DrawBitMap analog) for (int y = 0; y < height; y += tileH) { int rowH = height - y; if (rowH > tileH) rowH = tileH; if (rowH <= 0) continue; for (int x = 0; x < width; x += tileW) { int colW = width - x; if (colW > tileW) colW = tileW; if (colW <= 0) continue; view->MoveToAbsolute(x, y); // vtbl+0x24 view->DrawBitMap(0, tile, // vtbl+0x58 (clipped blit) originX, originY, originX + colW - 1, originY + rowH - 1); } } } // // @004c407c -- HorizTwoPartBar::Make. Allocates (0xb8), constructs, and // verifies the tile image exists (warns "HorizTwoPartBar: Missing image ..."). // // @004c4170 -- ctor: GraphicGauge base (vtable PTR_FUN_00518cbc), interns + // ref-counts the tile image, sets the graphics-port extent // (graphicsView.SetExtent(left,bottom,right,top)), stores fill/background // colours and the bar size, then wires three GaugeConnectionDirectOf // feeds (value, low, high -> this[0x24..0x26]). FUN_00474855 x3. // // @004c4324 -- BecameActive: previousFill=0; previousFull=width. // // @004c4340 -- Execute: clamp value to [low,high], map to a pixel column, // repaint only the changed sub-rectangles (tiled fill for the "filled" part, // background fill for the remainder). Uses _DAT_004c452c (0.0) as the floor. // // // @004c462c Make / @004c4724 ctor / @004c48e0 BecameActive / @004c48fc Execute // -- VertTwoPartBar: identical structure to HorizTwoPartBar but grows // bottom->top (vtable PTR_FUN_00518c78). Floor constant _DAT_004c4b00 (0.0). // // // @004c4b84 ctor / @004c4cac BecameActive / @004c4cc0 Execute // -- VertNormalSlider (vtable PTR_FUN_00518c34): one GaugeConnectionDirectOf // drives a normalised [0,1] value (clamped via _DAT_004c4d3c=0.0 / // _DAT_004c4d40=1.0, saturating to 0x3f800000); Execute @004c4cc0 maps it to a // row and calls Draw @004c4d48 which MoveTo()s and FillTo()s the lit rectangle. // //########################################################################### //########################################################################### // OneOfSeveral family -- multi-frame bitmap selectors //########################################################################### //########################################################################### // // @004c4d88 -- OneOfSeveral base ctor (vtable PTR_FUN_00518bf0). Stores the // strip-vs-image flag (this[0x24]), interns the image name, computes the // per-frame width/height by dividing the source bitmap (or pixmap) size by the // column/row counts, and sets the graphics-port origin. Two resource caches // are used depending on fromImageStrip: BitMapCache (+4) vs PixMapCache (+0x20). // // @004c4e7c -- dtor: release the image from the appropriate cache, free the // interned name, GraphicGauge::~GraphicGauge. // // @004c4f14 -- BecameActive: previousSelected = -1 (force first redraw). // // @004c4f28 -- Execute: if selected != previousSelected, compute the frame's // (row,col) sub-rectangle within the strip and blit it (clipped, vtbl+0x5C/0x58) // or stretch-blit the pixmap path. // // // @004c5068 Make / @004c5148 ctor -- OneOfSeveralInt (vtable PTR_FUN_00518bac): // OneOfSeveral(fromImageStrip=1) + one GaugeConnectionDirectOf // (FUN_004749de) feeding the frame index. dtor @004c51d8. // // @004c5204 Make / @004c52d8 ctor -- OneOfSeveralPixInt (vtable PTR_FUN_00518b68): // OneOfSeveral(fromImageStrip=0) + GaugeConnectionDirectOf. dtor @004c5364. // // @004c5390 Make / @004c5470 ctor -- OneOfSeveralStates (vtable PTR_FUN_00518b24): // OneOfSeveral(fromImageStrip=1) + a StateConnection (FUN_004c3324) reading the // subsystem state word @0x14. BecameActive @004c552c clamps the state >= 0 // before chaining to OneOfSeveral::Execute @004c4f28. dtor @004c5500. // //########################################################################### //########################################################################### // HeadingPointer @004c554c Make / @004c562c ctor //########################################################################### //########################################################################### // // @004c562c -- ctor (vtable PTR_FUN_00518ae0). GraphicGauge base, then builds // a GraphicsViewRecord (this+0x29, FUN_0044a5b4) for erase tracking, sets the // port origin, stores colours/spacing, interns + ref-counts the pointer image, // and -- centring on the image's mid-point -- constructs an owned NumericDisplay // (this[0x2F], FUN_004700bc, 0x44 bytes) offset by half the glyph metrics. // half-extent maths: w' = (imageWidth/14)*3 ; centre = (-w'/2, -h/2). // // @004c573c -- dtor: release the image, delete the NumericDisplay // (FUN_0047018c), tear down the GraphicsViewRecord (FUN_0044a5dc), // GraphicGauge::~GraphicGauge. // // @004c57d0 -- ShowInstance: prints "HeadingPointer" + the fg/bg colours // (this+0x94 / this+0x90) then chains to GraphicGauge::ShowInstance. // // @004c58e8 -- BecameActive: previousX = previousY = -999 (0xfffffc19) and // reset the NumericDisplay (FUN_004703f4). // // @004c5914 -- Execute. Resolve the owner's heading: take the renderer owner // entity (Resolve renderer+0x40), extract Euler yaw from its orientation // quaternion (entity+0x10c, FUN_0040954c), negate it, SinCos (FUN_00408328), // and Round the rotated pointer endpoint. When the rounded position changes, // erase the previous drawing (GraphicsViewRecord), re-blit the pointer image // rotated to the new heading (BeginClip/SetColor/MoveTo/LineTo/EndClip), then // draw the numeric heading via NumericDisplay::DrawAt (FUN_00470430), guarding // the readout below _DAT_004c5acc (0.0). // // (@004c591a is the same routine re-listed from a frame-pointer-relative // disassembly view; it carries no distinct code.) // //########################################################################### //########################################################################### // BitMap wipe gauges and arc gauges //########################################################################### //########################################################################### // // @004c5b7c ctor / @004c5c80 dtor / @004c5cf4 BecameActive / @004c5d08 Execute // -- BitMapInverseWipe (vtable PTR_FUN_00518a9c; name unconfirmed). // A 3-frame strip (frameWidth = imageWidth/3) revealed in steps. Execute // @004c5d08 clamps the level to [0, frames], promotes it to 1 when the value // is above _DAT_0050e3d8 (0.0025) but still rounds to 0, then blits each lit // segment, stepping the colour through colorA/colorB by 2s. Used as a base by // btl4gau2's LeakGauge. // // @004c5e84 ctor / @004c5f30 dtor / @004c5fa4 BecameActive / @004c5fb8 Execute // -- BitMapInverseWipe base (vtable PTR_FUN_00518a58): a two-segment reveal // of one strip image; frame size read from the bitmap (src+0x0C/0x10). // // @004c61c8 ctor (vtable PTR_FUN_00518a14) -- the Scalar-driven variant: // chains @004c5e84 then AddConnection(new GaugeConnectionDirectOf // (&level)) (FUN_00474855). Deleting-dtor thunk @004c66d9 -> @004c5f30. // // @004c6244 ctor (vtable PTR_FUN_005189d0) -- SegmentArc270: derives from the // MUNGA L4 arc primitive (FUN_004745e0). Precomputes the per-segment span: // segmentSpan = (int)( (float)(|n|/(|n|-1)) * 0.75f ) // _DAT_004c62d4 // Deleting-dtor thunk @004c66b3 -> FUN_00474094 (arc base dtor). // // @004c62fc Make / @004c6394 ctor (vtable PTR_FUN_0051898c) -- SegmentArcRatio: // derives from the second MUNGA L4 arc primitive (FUN_00473f44) and adds TWO // GaugeConnectionDirectOf feeds (numerator @0x30, denominator @0x31, // FUN_00474855 x2); precomputes segmentSpan with _DAT_004c6484 (0.75f). // Make @004c62fc allocates 0xcc and supplies the arc geometry resources // (DAT_00517eb0..00518158) and the name "SegmentArcRatio". // Deleting-dtor thunk @004c668d -> FUN_00474094. // // === btl4gau2.cpp begins at @004c6798 (SeekVoltage gauge) -- not part of this TU.