From 42ff5c385682ace0792f2fb2c5b7baabeb4c40b5 Mon Sep 17 00:00:00 2001 From: Cyd Date: Sun, 26 Jul 2026 23:09:15 -0500 Subject: [PATCH] BT410 Phase 5.3.29: the panel titles -- 94.8% identical to the shipped cockpit Reading the art settled where the titles live: qsensors/qmyomers/qermlas/ qppc/qstrk6.pcc are 299x26 TITLE BANNERS, while the qblh0..7.pcc strings the cfg hands vehicleSubSystems are 77x120 MECH ICONS (already lit by the 5.3.28 placement fix). So the authored auxScreenLabel IS the panel title -- it just belongs on the MFD panel, not only the ENG page. SubsystemCluster now builds a titleBanner child from the cached label. Placement was MEASURED against the shipped framebuffer through the A/B rig (BackgroundBitmap places by left/BOTTOM): a bring-up BT_TITLE_DY knob, then a green-pixel correlation over the title band, then baked at x+0x09, y+0xb3 from the panel origin. "SENSOR CLUSTER", "MYOMERS", "TYPE II 65 TONS" and both "ERMED LASER RANGE 500M" banners now render where the shipped binary puts them. Score progression vs the shipped cockpit on identical GAUGE content: 5.3.26 birth 90.1% identical / 82% coverage 5.3.28 authored screens 93.2% / 85% 5.3.29 titles tuned 94.8% / 91% Also: grab.ps1 now focuses the target window before capturing -- the screen-copy grabbed whatever was in front, which cost two junk captures (a white frame and an unrelated 3-D app window). Gauge fight 11/11 rounds and smoke both clean, zero faults. Co-Authored-By: Claude Fable 5 --- emulator/render-bridge/gauge-ab/grab.ps1 | 106 +- restoration/source410/BT_L4/BTL4GAU2.CPP | 28 + restoration/source410/BT_L4/BTL4GAU2.HPP | 1145 +++++++++-------- .../source410/BT_L4/GAUGE-BLOCK.NOTES.md | 38 + 4 files changed, 693 insertions(+), 624 deletions(-) diff --git a/emulator/render-bridge/gauge-ab/grab.ps1 b/emulator/render-bridge/gauge-ab/grab.ps1 index bb743e4f..d542b54f 100644 --- a/emulator/render-bridge/gauge-ab/grab.ps1 +++ b/emulator/render-bridge/gauge-ab/grab.ps1 @@ -1,52 +1,54 @@ -param( - [Parameter(Mandatory=$true)][string]$Out, - [string]$Match = "DOSBox-X" -) - -Add-Type @" -using System; -using System.Runtime.InteropServices; -public class W { - [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); - [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r); - [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h); - [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h); - [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetWindowText(IntPtr h, System.Text.StringBuilder s, int n); - [DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc cb, IntPtr p); - public delegate bool EnumProc(IntPtr h, IntPtr p); - [StructLayout(LayoutKind.Sequential)] public struct RECT { public int L, T, R, B; } -} -"@ - -$found = @() -$cb = [W+EnumProc]{ - param($h, $p) - if ([W]::IsWindowVisible($h)) { - $sb = New-Object System.Text.StringBuilder 512 - [void][W]::GetWindowText($h, $sb, 512) - $t = $sb.ToString() - if ($t -like "*$Match*") { $script:found += ,@($h, $t) } - } - return $true -} -[void][W]::EnumWindows($cb, [IntPtr]::Zero) - -if ($found.Count -eq 0) { Write-Output "NO-WINDOW"; exit 1 } - -Add-Type -AssemblyName System.Drawing -$i = 0 -foreach ($f in $found) { - $h = $f[0]; $title = $f[1] - $r = New-Object W+RECT - [void][W]::GetWindowRect($h, [ref]$r) - $w = $r.R - $r.L; $ht = $r.B - $r.T - if ($w -le 0 -or $ht -le 0) { continue } - $bmp = New-Object System.Drawing.Bitmap $w, $ht - $g = [System.Drawing.Graphics]::FromImage($bmp) - $g.CopyFromScreen($r.L, $r.T, 0, 0, (New-Object System.Drawing.Size $w, $ht)) - $path = if ($found.Count -eq 1) { $Out } else { $Out -replace '\.png$', "_$i.png" } - $bmp.Save($path, [System.Drawing.Imaging.ImageFormat]::Png) - $g.Dispose(); $bmp.Dispose() - Write-Output "SAVED $path ${w}x${ht} [$title]" - $i++ -} +param( + [Parameter(Mandatory=$true)][string]$Out, + [string]$Match = "DOSBox-X" +) + +Add-Type @" +using System; +using System.Runtime.InteropServices; +public class W { + [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r); + [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h); + [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h); + [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetWindowText(IntPtr h, System.Text.StringBuilder s, int n); + [DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc cb, IntPtr p); + public delegate bool EnumProc(IntPtr h, IntPtr p); + [StructLayout(LayoutKind.Sequential)] public struct RECT { public int L, T, R, B; } +} +"@ + +$found = @() +$cb = [W+EnumProc]{ + param($h, $p) + if ([W]::IsWindowVisible($h)) { + $sb = New-Object System.Text.StringBuilder 512 + [void][W]::GetWindowText($h, $sb, 512) + $t = $sb.ToString() + if ($t -like "*$Match*") { $script:found += ,@($h, $t) } + } + return $true +} +[void][W]::EnumWindows($cb, [IntPtr]::Zero) + +if ($found.Count -eq 0) { Write-Output "NO-WINDOW"; exit 1 } + +Add-Type -AssemblyName System.Drawing +$i = 0 +foreach ($f in $found) { + $h = $f[0]; $title = $f[1] + [void][W]::SetForegroundWindow($h) + Start-Sleep -Milliseconds 1200 + $r = New-Object W+RECT + [void][W]::GetWindowRect($h, [ref]$r) + $w = $r.R - $r.L; $ht = $r.B - $r.T + if ($w -le 0 -or $ht -le 0) { continue } + $bmp = New-Object System.Drawing.Bitmap $w, $ht + $g = [System.Drawing.Graphics]::FromImage($bmp) + $g.CopyFromScreen($r.L, $r.T, 0, 0, (New-Object System.Drawing.Size $w, $ht)) + $path = if ($found.Count -eq 1) { $Out } else { $Out -replace '\.png$', "_$i.png" } + $bmp.Save($path, [System.Drawing.Imaging.ImageFormat]::Png) + $g.Dispose(); $bmp.Dispose() + Write-Output "SAVED $path ${w}x${ht} [$title]" + $i++ +} diff --git a/restoration/source410/BT_L4/BTL4GAU2.CPP b/restoration/source410/BT_L4/BTL4GAU2.CPP index 0b380084..887fa462 100644 --- a/restoration/source410/BT_L4/BTL4GAU2.CPP +++ b/restoration/source410/BT_L4/BTL4GAU2.CPP @@ -1927,6 +1927,34 @@ SubsystemCluster::SubsystemCluster( } } + // + // The PANEL TITLE BANNER (299x26): the authored auxScreenLabel .pcc -- + // "SENSOR CLUSTER" / "MYOMERS" / "ERMED LASER RANGE 500M" / the weapon + // names. Cached on PoweredSubsystem at ctor time (its resource dies + // with the Mech ctor's stream buffer). The banner sits at the TOP of + // the quadrant; BackgroundBitmap places by (left, BOTTOM), so the row + // is the panel origin plus the quadrant height less the banner. + // + titleBanner = NULL; + if (subsystem_in != NULL + && subsystem_in->IsDerivedFrom(PoweredSubsystem::ClassDerivations)) + { + const char *title = + ((PoweredSubsystem *)subsystem_in)->GetAuxScreenLabel(); + if (title != NULL && *title != '') + { + // Placement measured against the shipped framebuffer + // (the A/B rig): the banner's bottom row sits 0xb3 above the + // panel origin, inset 0x0e from its left edge. + titleBanner = new BackgroundBitmap( + mfd_mode, renderer_in, owner_ID, mfd_port, + x + 0x09, y + 0xb3, title, + 1, 0xff, 0 + ); + Register_Object(titleBanner); + } + } + //------------------------------------------------------------ // Paint the panel frame image into the MFD port. //------------------------------------------------------------ diff --git a/restoration/source410/BT_L4/BTL4GAU2.HPP b/restoration/source410/BT_L4/BTL4GAU2.HPP index 8bb1ab3e..99400c01 100644 --- a/restoration/source410/BT_L4/BTL4GAU2.HPP +++ b/restoration/source410/BT_L4/BTL4GAU2.HPP @@ -1,572 +1,573 @@ -//===========================================================================// -// File: btl4gau2.hpp // -// Project: BattleTech Brick: Gauge Renderer Manager // -// Contents: Cockpit instrument library, part 2 -- the COMPOSITE "panel" // -// gauges that the BattleTech engineering / weapon-status HUD is // -// assembled from. Where btl4gaug.cpp holds the primitive widgets // -// (bars, dials, lamps, colour-mappers), btl4gau2.cpp holds the // -// higher-level gauges that AGGREGATE those primitives into a single // -// subsystem read-out: // -// * SeekVoltageGraph -- energy-weapon seek-voltage curve // -// * ConfigMapGauge -- joystick / config-manager state lamp // -// * GeneratorCluster -- generator voltage + coolant + leak panel // -// * PrepEngrScreen -- per-weapon engineering screen builder // -// * SubsystemCluster +-> HeatSinkCluster // -// +-> MyomerCluster // -// +-> WeaponCluster +-> EnergyWeaponCluster // -// +-> BallisticWeaponClu.// -//---------------------------------------------------------------------------// -// Date Who Modification // -// -------- --- ---------------------------------------------------------- // -// 02/22/96 CPB Initial coding. // -//---------------------------------------------------------------------------// -// Copyright (C) 1996, Virtual World Entertainment, Inc. All rights reserved // -// PROPRIETARY AND CONFIDENTIAL // -//===========================================================================// -// -// STAGED RECONSTRUCTION. No BTL4GAU2.HPP survived; recovered from the shipped -// binary (BTL4OPT.EXE) and re-hosted onto the authentic 1995 engine headers -// (MUNGA GAUGE/GAUGREND + MUNGA_L4 L4GAUGE) for the BC++4.52 build. -// -// Translation-unit extent (BTL4.MAK link order: -// btl4mode -> btl4rdr -> btl4gaug -> btl4gau2 -> btl4gau3 -> btl4grnd -> ...): -// first gau2 code @004c6798 (SeekVoltageGraph::SeekVoltageGraph) -// last gau2 code @004c9b50 (BallisticWeaponCluster helpers) -// plus VehicleSubSystems::Make @004cbaf0 -// -// Class names recovered from the CODE name-string pool around 0x519760 -// (the gauge name list) and the per-instance identification strings: -// "GeneratorCluster" 0x519a8c "PrepEngr" 0x519a9d -// "ConfigMap" 0x519a38 "CoolingLoop" 0x519a42 "PowerSource" 0x519a4e -// "GeneratorVoltage(scalar)" 0x519a73 "GeneratorVoltage(slotOf)" 0x519a5a -// "EngrGraph" 0x519a2e "WeaponCluster ... " 0x519dcb -// Field offsets in comments are the BINARY's byte offsets (e.g. "@0x90" == -// this[0x24]); the recompiled objects are NOT byte-locked to them. -// -// Names flagged "(best-effort)" exist in the binary but their identification -// string could not be tied to them unambiguously. -// - -#if !defined(BTL4GAU2_HPP) -# define BTL4GAU2_HPP - -# if !defined(SLOT_HPP) -# include -# endif -# if !defined(L4GREND_HPP) -# include -# endif -# if !defined(L4GAUGE_HPP) -# include -# endif -# if !defined(BTL4GAUG_HPP) -# include -# endif -# if !defined(HEAT_HPP) -# include -# endif - // (see btl4gaug.hpp -- intentionally not pulled in; the - // composite gauges reference subsystems by pointer/attribute, not by type.) - - // - // Reconstruction type: the original referenced an "AttributeAccessor" handle - // (a small object that samples one named subsystem attribute). Only its - // address is used here (it is immediately reinterpreted as a Scalar*), so an - // opaque forward declaration is sufficient for this translation unit. - // - class AttributeAccessor; - - - //####################################################################### - // SeekVoltageGraph -- the energy-weapon / myomer "seek voltage" response - // graph on the engineering detail pages (the "POWER" box). - // @004c6798 ctor / @004c68ac dtor / @004c6920 BecameActive / @004c6934 Execute. - // vtable PTR_FUN_0051a1fc. Resolves the four SeekVoltage attribute - // POINTERS off the weapon subsystem by NAME ("CurrentSeekVoltageIndex" / - // "MinSeekVoltageIndex" / "MaxSeekVoltageIndex" / "SeekVoltage") and plots - // the subsystem's voltage-response curve (binary vtbl slot 15 sampler: - // Emitter @004bb42c / Myomers @004b8f94) over the top data box - // (0x97,0x80)-(0x17d,0x13b): x = response(v)*230 (0..1 normalised sqrt - // response), y = v/12000*187 (voltage axis), with per-gear tick marks (the - // current gear drawn as a full L) and, when draw_cursor, a live 8x8 XOR - // cursor at the subsystem's live voltage. When the subsystem is destroyed - // (simulationState == 1) it shows a centred "destroyed" bitmap instead. - // Its Execute's full-box CLEAR (@004c6be4) is what erases the sibling eng - // pages' stale pixels on the shared Eng bit-plane -- the widget IS the - // top-box eraser (Gitea #10 finding A). - //####################################################################### - class SeekVoltageGraph : - public GraphicGauge - { - public: - SeekVoltageGraph( // @004c6798 - GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer, - int graphics_port_number, Entity *subsystem, - AttributeAccessor *seek_voltage_pointer, - const char *destroyed_image, Logical draw_cursor, - const char *identification_string); - ~SeekVoltageGraph(); // @004c68ac - Logical TestInstance() const; - void BecameActive(); // @004c6920 - void Execute(); // @004c6934 - protected: - void ClearGraph(); // @004c6be4 (full-box erase) - void DrawTicks(); // @004c6c6c (per-gear ticks, XOR) - void DrawCursorBox(); // @004c6c30 (8x8 cursor, XOR) - - char *destroyedImage; // @0x90 this[0x24] - int *currentSeekIndexPtr; // @0x94 this[0x25] attr ptr (or NULL) - int *minSeekIndexPtr; // @0x98 this[0x26] attr ptr (or NULL) - int *maxSeekIndexPtr; // @0x9C this[0x27] attr ptr (or NULL) - Scalar *seekVoltageTablePtr; // @0xA0 this[0x28] attr ptr (array base) - Scalar *liveVoltage; // @0xA4 this[0x29] the ctor's - // seek_voltage_pointer ("OutputVoltage" - // attr ptr / cluster seekValue cache) -- - // Execute derefs it for the live cursor - // (never NULL: ctor normalises a miss to - // the file-scope unbound cell) - Subsystem *subsystem; // @0xA8 this[0x2A] - Scalar previousVoltage; // @0xAC this[0x2B] cached response at - // 12000 V (9999 sentinel = force replot) - Scalar xScale; // @0xB0 this[0x2C] =230.0f - Scalar yScale; // @0xB4 this[0x2D] =187.0f - int destroyedShown; // @0xB8 this[0x2E] - Logical drawCursor; // @0xBC this[0x2F] - int cursorX; // @0xC0 this[0x30] (-1 = no cursor drawn) - int cursorY; // @0xC4 this[0x31] - int tickIndexShown; // @0xC8 this[0x32] gear index the tick - // highlight was last drawn at - }; - - - //####################################################################### - // ConfigMapGauge -- the joystick / config-manager (control-mapper) state - // lamp. @004c6d80 ctor / @004c6e54 dtor / @004c6ee0 LinkToEntity / - // @004c6ef4 BecameActive / @004c6f1c Execute. vtable PTR_FUN_0051a1b8. - // Owns a base joystick bitmap ("btjoy.pcc") plus four config-state pixmaps - // (cm_off/cm_other/cm_only/cm_both .pcc) selected from a 4-entry table - // (DAT_00518eb8) driven by the control-mapper state. Named "ConfigMap". - //####################################################################### - class ConfigMapGauge : - public GraphicGauge - { - public: - ConfigMapGauge( // @004c6d80 - GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number, - int x, int y, Entity *subsystem, const char *identification_string); - ~ConfigMapGauge(); // @004c6e54 - Logical TestInstance() const; - // @004c6ee0 -- the GaugeBase::LinkToEntity OVERRIDE (vtbl slot 9, +0x24), - // NOT a "SetColor": it stores the viewpoint entity into @0x94 and Execute - // gates on it. GaugeRenderer::LinkToEntity (GAUGREND.CPP:3011) broadcasts - // it to every gauge when the viewpoint binds -- THAT is what lights the - // trigger-config joystick in the shipped game. (The old "SetColor, no - // callers -> authentically dormant" claim was a mislabel.) - void LinkToEntity(Entity *entity); // @004c6ee0 writes this[0x25] - void BecameActive(); // @004c6ef4 - void Execute(); // @004c6f1c - protected: - Entity *subsystem; // @0x90 this[0x24] (a Subsystem, stored - // as the ctor's Entity* -- cast back - // through Subsystem* before use) - Entity *linkedEntity; // @0x94 this[0x25] (Execute gate) - char *joystickImage; // @0x98 this[0x26] "btjoy.pcc" - char *stateImage[4]; // @0x9C this[0x27..0x2A] - int previousState[4]; // @0xAC this[0x2B..0x2E] - Logical dirty; // @0xBC this[0x2F] - }; - - - //####################################################################### - // Two small OneOfSeveral subclasses that drive the selected frame from a - // subsystem value through a file-private GaugeConnection. - // AnimatedSubsystemLamp @004c70a4 (vtable 0051a174) -- connection @004c3134 - // AnimatedSourceLamp @004c7160 (vtable 0051a130) -- connection @004c31ec - // Both chain OneOfSeveral::OneOfSeveral (@004c4d88, fromImageStrip=1) and - // share dtor FUN_004c4e7c. Used for the cooling-loop / power-source lamps. - // (Class names best-effort; the gauge is generic and reused with the - // instance strings "CoolingLoop" and "PowerSource".) - // - // SHADOW-FIELD FIX (kept on re-host): 'selected' is INHERITED from - // OneOfSeveral (@0xAC). A redeclared 'int selected;' here shadowed the - // base at a higher offset -- the connection wrote the shadow while - // OneOfSeveral::Execute read the base (always 0), freezing the lamp at - // frame 0. Neither class declares its own copy. - //####################################################################### - class AnimatedSubsystemLamp : // (best-effort) - public OneOfSeveral - { - public: - AnimatedSubsystemLamp( // @004c70a4 - GaugeRate, ModeMask, L4GaugeRenderer *, int, - int x, int y, const char *image, int columns, int rows, - void *subsystem, const char *identification_string); - ~AnimatedSubsystemLamp(); // @004c7134 - }; - - class AnimatedSourceLamp : // (best-effort) - public OneOfSeveral - { - public: - AnimatedSourceLamp( // @004c7160 - GaugeRate, ModeMask, L4GaugeRenderer *, int, - int x, int y, const char *image, int columns, int rows, - void *source, const char *identification_string); - ~AnimatedSourceLamp(); // @004c71f0 - }; - - - //####################################################################### - // ScalarBarGauge -- a generator-voltage bar (derives from the engine - // BarGraphBitMapScalar, L4GAUGE.HPP -- WipeGaugeScalar : GraphicGauge). - // Two ctors, one vtable (0051a0e4), shared dtor @004c733c: - // @004c721c "slotOf" variant -- AddConnection(GeneratorVoltageConnection - // @004c3288) feeding the inherited currentValue from the - // subsystem's resolved voltage source. - // @004c72ac plain-Scalar variant -- a GaugeConnectionDirectOf - // (engine template) from a DIRECT Scalar*. - // Used with "GeneratorVoltage(slotOf)" / "GeneratorVoltage(scalar)". - // - // Its value slot @0xB4 (this[0x2D]) IS the inherited - // WipeGaugeScalar::currentValue -- the connection writes it, so NO own - // field is declared (declaring one would shadow / mis-align). - // - // BC++4.52 NOTE: the void* / Scalar* 15th-argument overload pair is - // ambiguous for a literal 0/NULL argument -- every call site must pass a - // TYPED pointer. - //####################################################################### - class ScalarBarGauge : - public BarGraphBitMapScalar - { - public: - ScalarBarGauge( // @004c721c (slotOf variant) - GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number, - int left, int bottom, int right, int top, - const char *tile_image, int foreground_color, int background_color, - WipeGaugeScalar::Direction direction, Scalar min, Scalar max, - void *voltage_source, const char *identification_string); - ScalarBarGauge( // @004c72ac (plain-Scalar variant) - GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number, - int left, int bottom, int right, int top, - const char *tile_image, int foreground_color, int background_color, - WipeGaugeScalar::Direction direction, Scalar min, Scalar max, - Scalar *value_source, const char *identification_string); - ~ScalarBarGauge(); // @004c733c - }; - - - //####################################################################### - // GeneratorCluster -- the generator read-out panel (config keyword - // "GeneratorCluster": engineering buttons 9-12, GeneratorA..D). - // @004c7368 Make / @004c746c ctor / @004c7778 dtor. vtable PTR_FUN_0051a0a0. - // A GraphicGauge that owns five child gauges, parented to its own port: - // VertTwoPartBar (temperature) this[0x26] - // ScalarBarGauge (voltage 0..12kV) this[0x27] - // BitMapInverseWipe/Leak (coolant leak) this[0x28] - // TwoState x2 (status lamps) this[0x29],this[0x2A] - // Make resolves the named generator via FindSubsystem; warns - // "Subsystem not found". - //####################################################################### - class GeneratorCluster : - public GraphicGauge - { - public: - // Registered config primitive "GeneratorCluster" (15 params; NO leading - // rate token -- the panel rate is hard-wired 0xFFFF). - static MethodDescription methodDescription; - static Logical Make(int, Vector2DOf, Entity *, GaugeRenderer *); // @004c7368 - GeneratorCluster( // @004c746c (BINARY param order) - ModeMask, L4GaugeRenderer *, int owner_ID, int graphics_port_number, - int x, int y, Subsystem *subsystem, - const char *background_image, int extra_color, - const char *temp_tile, int temp_bg_color, int temp_fill_color, - const char *voltage_tile, int voltage_fg_color, - const char *leak_image, int leak_color_b, - const char *lampA_image, const char *lampB_image, - int lamp_on_color, int lamp_off_color, - const char *identification_string); - ~GeneratorCluster(); // @004c7778 - // A container panel MUST override BecameActive + Execute: the engine - // base Gauge::Execute is Fail("not overridden") (GAUGE.CPP:598), and - // the default BecameActive self-inactivates. The panel draws its - // static background pixmap; the 5 self-registered child gauges draw - // the dynamic content. - void BecameActive(); - void Execute(); - protected: - char *backgroundImage; // @0x90 this[0x24] interned .pcc (AddRef'd) - int secondaryColor; // @0x94 this[0x25] = leak colorB (stored, unread) - GraphicGauge - *temperatureBar, // @0x98 this[0x26] VertTwoPartBar - *voltageBar, // @0x9C this[0x27] ScalarBarGauge (@004c72ac variant) - *leakGauge, // @0xA0 this[0x28] BitMapInverseWipe - *lampA, // @0xA4 this[0x29] TwoState - *lampB; // @0xA8 this[0x2A] TwoState - Subsystem *subsystem; // @0xAC this[0x2B] - int _reserved0xB0; // @0xB0 this[0x2C] (trailing pad -> binary sizeof 0xB4) - }; - - - //####################################################################### - // PrepEngrScreen (config keyword "prepEngr") -- the per-engineering-screen - // STATIC LABEL overlay. @004c7b30 Make / @004c7bf0 ctor / @004c7d14 dtor / - // @004c7e48 BecameActive. vtable PTR_FUN_0051a06c. - // - // A GraphicGaugeBackground (paint-on-activation, NOT an Execute gauge -- - // the same base/vtable shape as the engine's BackgroundBitmap: only the - // dtor + BecameActive slots are overridden). Interns 7 status .pcc's - // (font + 6 labels) and, when its MFD-Eng screen (1..12) activates, paints - // the labels for the ONE subsystem whose aux screen == this screen, - // dispatched by the subsystem's ClassID. - // (FIX kept from the decode: mech/screenNumber were SWAPPED in the first - // reconstruction, and the missing methodDescription parse-skipped all 12 - // CFG lines.) - //####################################################################### - class PrepEngrScreen : - public GraphicGaugeBackground - { - public: - static MethodDescription methodDescription; // "prepEngr" - static Logical Make(int, Vector2DOf, Entity *, GaugeRenderer *); // @004c7b30 - PrepEngrScreen( // @004c7bf0 - ModeMask, L4GaugeRenderer *, unsigned int owner_ID, - int graphics_port_number, Entity *mech, int screen_number, - const char *img0/*font*/, const char *img1, const char *img2, - const char *img3, const char *img4, const char *img5, - const char *img6, const char *identification_string = "PrepEngr"); - ~PrepEngrScreen(); // @004c7d14 - void BecameActive(); // @004c7e48 (the paint slot -- NOT Execute) - protected: - int screenNumber; // @0x6C this[0x1B] - Entity *mech; // @0x70 this[0x1C] - char *statusImage[7]; // @0x74 this[0x1D..0x23] (interned .pcc names) - // binary sizeof == 0x90 (Make alloc); no offset locks (every read is - // via named members). - }; - - - //####################################################################### - // SubsystemCluster family -- the big composite panels. Each is a - // GraphicGauge that builds a dozen child gauges into one cockpit instrument. - // - // SubsystemCluster @004c8140 (vtable 0051a020) -- base "engineering" - // panel: HorizTwoPartBar + cooling/power loops + generator voltage + - // OneOfSeveralInt + OneOfSeveralStates + 2x VertTwoPartBar + LeakGauge - // + BackgroundBitmap. Uses the btXXloop / btXbus / evolt / btecmode / - // eleak art. - // - // HeatSinkCluster @004c8a6c (vtable 00519fd4) : SubsystemCluster - // adds 4 failure-annunciator TwoStates (btehfail/btepfail/btesfail) - // and a NumericDisplayScalar. Execute @004c8db0 scales heatLoad by - // 100 (_DAT_004c8df0) into the numeric read-out. - // - // MyomerCluster @004c8df4 (vtable 00519f88) : SubsystemCluster - // adds a SeekVoltageGraph ("EngrGraph") + seek-step OneOfSeveralInt. - // - // WeaponCluster @004c8fc4 (vtable 00519f38) : SubsystemCluster - // adds a SegmentArc270 (recharge dial, 0..360) + a ConfigMapGauge. - // Reads "PercentDone". Execute @004c9290 toggles the warning lamp - // past _DAT_004c92e0 (0.99). - // - // EnergyWeaponCluster @004c93b0 (vtable 00519ee8) : WeaponCluster - // adds a SeekVoltageGraph ("EngrGraph", edestryd.pcc) and a - // seek-step OneOfSeveralInt (bteseek.pcc). - // BallisticWeaponCluster @004c9558 (vtable 00519e98) : WeaponCluster - // adds 2x NumericDisplayInteger (ammo), jam/fire TwoStates - // (btejam/btefire), NumericDisplayScalarTwoState, an eject - // BitMapInverseWipeScalar (bteejtm), plus a GraphicsViewRecord - // erase tracker (this[0x3F]). - //####################################################################### - class SubsystemCluster : - public GraphicGauge - { - public: - // @004c8140. Binary param order (rate/mode are hardcoded 0xFFFF / - // ModeAlwaysActive in the base GraphicGauge ctor; the panel's two - // bit-plane MODE masks are mfd_mode (MFD-port children) and eng_mode - // (engineering-port children)). - SubsystemCluster( // @004c8140 - ModeMask mfd_mode, L4GaugeRenderer *renderer, int owner_ID, - int mfd_port, int x, int y, Subsystem *subsystem, ModeMask eng_mode, - const char *eng_port_name, const char *tile_image, - const char *label, char **image_names, - const char *identification_string); - ~SubsystemCluster(); // @004c87dc - Logical TestInstance() const; - void BecameActive(); // (re-host addition: forces the first repaint) - void Execute(); // @004c88e4 - protected: - BackgroundBitmap - *background; // @0x90 this[0x24] BackgroundBitmap - GraphicGauge - *generatorVoltageBar, // @0x94 this[0x25] ScalarBarGauge (evolt) - *temperatureBar, // @0x98 this[0x26] HorizTwoPartBar - *coolingLoopA, // @0x9C this[0x27] AnimatedSubsystemLamp - *coolingLoopB, // @0xA0 this[0x28] - *powerSourceA, // @0xA4 this[0x29] AnimatedSourceLamp - *powerSourceB, // @0xA8 this[0x2A] - *vertBarA, // @0xAC this[0x2B] VertTwoPartBar - *vertBarB, // @0xB0 this[0x2C] - *modeLamp, // @0xB4 this[0x2D] OneOfSeveralInt - *stateLamp, // @0xB8 this[0x2E] OneOfSeveralStates - *leakGauge; // @0xBC this[0x2F] LeakGauge - Subsystem *subsystem; // @0xC0 this[0x30] - // POLARITY CORRECTED (PPC-dial fix, kept on re-host): the binary's - // *(subsystem+0x40)==1 means the subsystem FAILED -- it lights the - // destroyed-X TwoStates (the engine TwoState draws bright on NONZERO) - // and selects the dead-panel look (GetDrawState 1 = black fill). The - // old name `operating` had it backwards, inverting every consumer. - Logical failedState; // @0xC4 this[0x31] 1 = subsystem destroyed - int previousDrawState; // @0xC8 this[0x32] (Execute repaint key) - - // internal helpers (non-virtual) - void DrawPanelFrame(int color); // @004c89c4 - void ReleaseChildren(); // @004c8820 - void SetChildrenEnable(int enable); // @004c8a28 - public: - int GetDrawState(); // @004c8990 (operational-state virtual, vtbl+0x48) - }; - - class HeatSinkCluster : // @004c8a6c - public SubsystemCluster - { - public: - HeatSinkCluster( // @004c8a6c - ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, - int x, int y, Subsystem *subsystem, Subsystem *hud, ModeMask eng_mode, - const char *eng_port_name, const char *tile_image, const char *label, - const char *fail_lamp_image, char **image_names, - const char *identification_string); - ~HeatSinkCluster(); // @004c8d18 - void Execute(); // @004c8db0 - protected: - Subsystem *failSubsystem; // @0xCC this[0x33] - Logical failFlag; // @0xD0 this[0x34] - Scalar heatLoad; // @0xD4 this[0x35] (connection dst) - Scalar heatLoadScaled; // @0xD8 this[0x36] - GraphicGauge - *heatFailLamp, // @0xDC this[0x37] TwoState - *hsFailLamp, // @0xE0 this[0x38] TwoState (btehfail) - *powerFailLamp, // @0xE4 this[0x39] TwoState (btepfail) - *structFailLamp, // @0xE8 this[0x3A] TwoState (btesfail) - *heatNumeric; // @0xEC this[0x3B] NumericDisplayScalar - }; - - // - // MyomerCluster @004c8df4 (vtable 00519f88) : SubsystemCluster -- the myomer - // (muscle) panel. Adds a SeekVoltageGraph ("EngrGraph", edestryd.pcc) fed - // by a GeneratorVoltageConnection (writes seekValue) + a seek-step - // OneOfSeveralInt (bteseek.pcc). Execute = base. - // - class MyomerCluster : // @004c8df4 - public SubsystemCluster - { - public: - MyomerCluster( // @004c8df4 - ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, - int x, int y, Subsystem *subsystem, ModeMask eng_mode, - const char *eng_port_name, const char *tile_image, const char *label, - char **image_names, const char *identification_string); - ~MyomerCluster(); // @004c8f54 - protected: - Scalar seekValue; // @0xCC this[0x33] (connection dst) - GraphicGauge - *seekGraph, // @0xD0 this[0x34] SeekVoltageGraph - *seekStepLamp; // @0xD4 this[0x35] OneOfSeveralInt - }; - - class WeaponCluster : // @004c8fc4 - public SubsystemCluster - { - public: - WeaponCluster( // @004c8fc4 - ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, - int x, int y, Subsystem *subsystem, ModeMask eng_mode, - const char *eng_port_name, const char *tile_image, const char *label, - const char *cluster_image, char **image_names, - const char *identification_string); - ~WeaponCluster(); // @004c91d4 - void BecameActive(); // @004c9258 - void Execute(); // @004c9290 - virtual void DrawWarningLamp(int on); // @004c932c (virtual: BallisticWeaponCluster overrides) - protected: - char *clusterImage; // @0xCC this[0x33] (interned) - GraphicGauge *rechargeArc; // @0xD0 this[0x34] SegmentArc270 - ConfigMapGauge *configMap; // @0xD4 this[0x35] - Scalar percentDone; // @0xD8 this[0x36] (connection dst) - int warningCenterX; // @0xDC this[0x37] - int warningCenterY; // @0xE0 this[0x38] - int warningState; // @0xE4 this[0x39] - }; - - class EnergyWeaponCluster : // @004c93b0 - public WeaponCluster - { - public: - EnergyWeaponCluster( // @004c93b0 - ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, - int x, int y, Subsystem *subsystem, ModeMask eng_mode, - const char *eng_port_name, const char *tile_image, const char *label, - const char *cluster_image, char **image_names, - const char *identification_string); - ~EnergyWeaponCluster(); // @004c94dc - protected: - SeekVoltageGraph *seekGraph; // @0xE8 this[0x3A] - GraphicGauge *seekStepLamp; // @0xEC this[0x3B] OneOfSeveralInt - }; - - class BallisticWeaponCluster : // @004c9558 - public WeaponCluster - { - public: - BallisticWeaponCluster( // @004c9558 - ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, - int x, int y, Subsystem *subsystem, ModeMask eng_mode, - const char *eng_port_name, const char *tile_image, const char *label, - const char *cluster_image, char **image_names, - const char *font_a, const char *font_b, - const char *identification_string); - ~BallisticWeaponCluster(); // @004c9940 - void BecameActive(); // @004c99cc - void Execute(); // @004c9a38 - void DrawWarningLamp(int on); // @004c9b50 (override: dot + ammo colour swap) - Logical TestInstance() const; // @004c9adc - protected: - Logical jammed; // @0xE8 this[0x3A] - Logical firing; // @0xEC this[0x3B] - Logical reloading; // @0xF0 this[0x3C] - int reloadStartTime; // @0xF4 this[0x3D] - Scalar reloadSeconds; // @0xF8 this[0x3E] - GraphicsViewRecord - *ammoErase; // @0xFC this[0x3F] (bring-up NULL) - GraphicGauge - *ammoNumeric, // @0x100 this[0x40] NumericDisplayScalarTwoState - *destroyedLamp, // @0x104 this[0x41] TwoState - *jamLamp, // @0x108 this[0x42] TwoState (btejam) - *fireLamp, // @0x10C this[0x43] TwoState (btefire) - *ejectWipe, // @0x110 this[0x44] BitMapInverseWipeScalar (NULL) - *ammoCountA, // @0x114 this[0x45] NumericDisplayInteger - *ammoCountB; // @0x118 this[0x46] NumericDisplayInteger - }; - - - //####################################################################### - // vehicleSubSystems -- the config primitive that builds the engineering- - // screen (MFD) subsystem cluster panels. Make @004cbaf0 (methodDescription - // @0x51c080, 8 typeString params = the 8 status strip bitmaps) is a per- - // subsystem factory: it walks the mech roster (skipping slot 0), and for - // each displayable subsystem builds the right cluster (HeatSink/Myomer/ - // Energy/Ballistic) onto one of 12 auxiliary MFD positions. This holder - // just carries the registered MethodDescription + the static Make - // dispatcher (there is no instance). - //####################################################################### - class VehicleSubSystems - { - public: - static MethodDescription methodDescription; - static Logical Make( // @004cbaf0 - int display_port_index, Vector2DOf position, - Entity *entity, GaugeRenderer *gauge_renderer); - }; - -#endif +//===========================================================================// +// File: btl4gau2.hpp // +// Project: BattleTech Brick: Gauge Renderer Manager // +// Contents: Cockpit instrument library, part 2 -- the COMPOSITE "panel" // +// gauges that the BattleTech engineering / weapon-status HUD is // +// assembled from. Where btl4gaug.cpp holds the primitive widgets // +// (bars, dials, lamps, colour-mappers), btl4gau2.cpp holds the // +// higher-level gauges that AGGREGATE those primitives into a single // +// subsystem read-out: // +// * SeekVoltageGraph -- energy-weapon seek-voltage curve // +// * ConfigMapGauge -- joystick / config-manager state lamp // +// * GeneratorCluster -- generator voltage + coolant + leak panel // +// * PrepEngrScreen -- per-weapon engineering screen builder // +// * SubsystemCluster +-> HeatSinkCluster // +// +-> MyomerCluster // +// +-> WeaponCluster +-> EnergyWeaponCluster // +// +-> BallisticWeaponClu.// +//---------------------------------------------------------------------------// +// Date Who Modification // +// -------- --- ---------------------------------------------------------- // +// 02/22/96 CPB Initial coding. // +//---------------------------------------------------------------------------// +// Copyright (C) 1996, Virtual World Entertainment, Inc. All rights reserved // +// PROPRIETARY AND CONFIDENTIAL // +//===========================================================================// +// +// STAGED RECONSTRUCTION. No BTL4GAU2.HPP survived; recovered from the shipped +// binary (BTL4OPT.EXE) and re-hosted onto the authentic 1995 engine headers +// (MUNGA GAUGE/GAUGREND + MUNGA_L4 L4GAUGE) for the BC++4.52 build. +// +// Translation-unit extent (BTL4.MAK link order: +// btl4mode -> btl4rdr -> btl4gaug -> btl4gau2 -> btl4gau3 -> btl4grnd -> ...): +// first gau2 code @004c6798 (SeekVoltageGraph::SeekVoltageGraph) +// last gau2 code @004c9b50 (BallisticWeaponCluster helpers) +// plus VehicleSubSystems::Make @004cbaf0 +// +// Class names recovered from the CODE name-string pool around 0x519760 +// (the gauge name list) and the per-instance identification strings: +// "GeneratorCluster" 0x519a8c "PrepEngr" 0x519a9d +// "ConfigMap" 0x519a38 "CoolingLoop" 0x519a42 "PowerSource" 0x519a4e +// "GeneratorVoltage(scalar)" 0x519a73 "GeneratorVoltage(slotOf)" 0x519a5a +// "EngrGraph" 0x519a2e "WeaponCluster ... " 0x519dcb +// Field offsets in comments are the BINARY's byte offsets (e.g. "@0x90" == +// this[0x24]); the recompiled objects are NOT byte-locked to them. +// +// Names flagged "(best-effort)" exist in the binary but their identification +// string could not be tied to them unambiguously. +// + +#if !defined(BTL4GAU2_HPP) +# define BTL4GAU2_HPP + +# if !defined(SLOT_HPP) +# include +# endif +# if !defined(L4GREND_HPP) +# include +# endif +# if !defined(L4GAUGE_HPP) +# include +# endif +# if !defined(BTL4GAUG_HPP) +# include +# endif +# if !defined(HEAT_HPP) +# include +# endif + // (see btl4gaug.hpp -- intentionally not pulled in; the + // composite gauges reference subsystems by pointer/attribute, not by type.) + + // + // Reconstruction type: the original referenced an "AttributeAccessor" handle + // (a small object that samples one named subsystem attribute). Only its + // address is used here (it is immediately reinterpreted as a Scalar*), so an + // opaque forward declaration is sufficient for this translation unit. + // + class AttributeAccessor; + + + //####################################################################### + // SeekVoltageGraph -- the energy-weapon / myomer "seek voltage" response + // graph on the engineering detail pages (the "POWER" box). + // @004c6798 ctor / @004c68ac dtor / @004c6920 BecameActive / @004c6934 Execute. + // vtable PTR_FUN_0051a1fc. Resolves the four SeekVoltage attribute + // POINTERS off the weapon subsystem by NAME ("CurrentSeekVoltageIndex" / + // "MinSeekVoltageIndex" / "MaxSeekVoltageIndex" / "SeekVoltage") and plots + // the subsystem's voltage-response curve (binary vtbl slot 15 sampler: + // Emitter @004bb42c / Myomers @004b8f94) over the top data box + // (0x97,0x80)-(0x17d,0x13b): x = response(v)*230 (0..1 normalised sqrt + // response), y = v/12000*187 (voltage axis), with per-gear tick marks (the + // current gear drawn as a full L) and, when draw_cursor, a live 8x8 XOR + // cursor at the subsystem's live voltage. When the subsystem is destroyed + // (simulationState == 1) it shows a centred "destroyed" bitmap instead. + // Its Execute's full-box CLEAR (@004c6be4) is what erases the sibling eng + // pages' stale pixels on the shared Eng bit-plane -- the widget IS the + // top-box eraser (Gitea #10 finding A). + //####################################################################### + class SeekVoltageGraph : + public GraphicGauge + { + public: + SeekVoltageGraph( // @004c6798 + GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer, + int graphics_port_number, Entity *subsystem, + AttributeAccessor *seek_voltage_pointer, + const char *destroyed_image, Logical draw_cursor, + const char *identification_string); + ~SeekVoltageGraph(); // @004c68ac + Logical TestInstance() const; + void BecameActive(); // @004c6920 + void Execute(); // @004c6934 + protected: + void ClearGraph(); // @004c6be4 (full-box erase) + void DrawTicks(); // @004c6c6c (per-gear ticks, XOR) + void DrawCursorBox(); // @004c6c30 (8x8 cursor, XOR) + + char *destroyedImage; // @0x90 this[0x24] + int *currentSeekIndexPtr; // @0x94 this[0x25] attr ptr (or NULL) + int *minSeekIndexPtr; // @0x98 this[0x26] attr ptr (or NULL) + int *maxSeekIndexPtr; // @0x9C this[0x27] attr ptr (or NULL) + Scalar *seekVoltageTablePtr; // @0xA0 this[0x28] attr ptr (array base) + Scalar *liveVoltage; // @0xA4 this[0x29] the ctor's + // seek_voltage_pointer ("OutputVoltage" + // attr ptr / cluster seekValue cache) -- + // Execute derefs it for the live cursor + // (never NULL: ctor normalises a miss to + // the file-scope unbound cell) + Subsystem *subsystem; // @0xA8 this[0x2A] + Scalar previousVoltage; // @0xAC this[0x2B] cached response at + // 12000 V (9999 sentinel = force replot) + Scalar xScale; // @0xB0 this[0x2C] =230.0f + Scalar yScale; // @0xB4 this[0x2D] =187.0f + int destroyedShown; // @0xB8 this[0x2E] + Logical drawCursor; // @0xBC this[0x2F] + int cursorX; // @0xC0 this[0x30] (-1 = no cursor drawn) + int cursorY; // @0xC4 this[0x31] + int tickIndexShown; // @0xC8 this[0x32] gear index the tick + // highlight was last drawn at + }; + + + //####################################################################### + // ConfigMapGauge -- the joystick / config-manager (control-mapper) state + // lamp. @004c6d80 ctor / @004c6e54 dtor / @004c6ee0 LinkToEntity / + // @004c6ef4 BecameActive / @004c6f1c Execute. vtable PTR_FUN_0051a1b8. + // Owns a base joystick bitmap ("btjoy.pcc") plus four config-state pixmaps + // (cm_off/cm_other/cm_only/cm_both .pcc) selected from a 4-entry table + // (DAT_00518eb8) driven by the control-mapper state. Named "ConfigMap". + //####################################################################### + class ConfigMapGauge : + public GraphicGauge + { + public: + ConfigMapGauge( // @004c6d80 + GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number, + int x, int y, Entity *subsystem, const char *identification_string); + ~ConfigMapGauge(); // @004c6e54 + Logical TestInstance() const; + // @004c6ee0 -- the GaugeBase::LinkToEntity OVERRIDE (vtbl slot 9, +0x24), + // NOT a "SetColor": it stores the viewpoint entity into @0x94 and Execute + // gates on it. GaugeRenderer::LinkToEntity (GAUGREND.CPP:3011) broadcasts + // it to every gauge when the viewpoint binds -- THAT is what lights the + // trigger-config joystick in the shipped game. (The old "SetColor, no + // callers -> authentically dormant" claim was a mislabel.) + void LinkToEntity(Entity *entity); // @004c6ee0 writes this[0x25] + void BecameActive(); // @004c6ef4 + void Execute(); // @004c6f1c + protected: + Entity *subsystem; // @0x90 this[0x24] (a Subsystem, stored + // as the ctor's Entity* -- cast back + // through Subsystem* before use) + Entity *linkedEntity; // @0x94 this[0x25] (Execute gate) + char *joystickImage; // @0x98 this[0x26] "btjoy.pcc" + char *stateImage[4]; // @0x9C this[0x27..0x2A] + int previousState[4]; // @0xAC this[0x2B..0x2E] + Logical dirty; // @0xBC this[0x2F] + }; + + + //####################################################################### + // Two small OneOfSeveral subclasses that drive the selected frame from a + // subsystem value through a file-private GaugeConnection. + // AnimatedSubsystemLamp @004c70a4 (vtable 0051a174) -- connection @004c3134 + // AnimatedSourceLamp @004c7160 (vtable 0051a130) -- connection @004c31ec + // Both chain OneOfSeveral::OneOfSeveral (@004c4d88, fromImageStrip=1) and + // share dtor FUN_004c4e7c. Used for the cooling-loop / power-source lamps. + // (Class names best-effort; the gauge is generic and reused with the + // instance strings "CoolingLoop" and "PowerSource".) + // + // SHADOW-FIELD FIX (kept on re-host): 'selected' is INHERITED from + // OneOfSeveral (@0xAC). A redeclared 'int selected;' here shadowed the + // base at a higher offset -- the connection wrote the shadow while + // OneOfSeveral::Execute read the base (always 0), freezing the lamp at + // frame 0. Neither class declares its own copy. + //####################################################################### + class AnimatedSubsystemLamp : // (best-effort) + public OneOfSeveral + { + public: + AnimatedSubsystemLamp( // @004c70a4 + GaugeRate, ModeMask, L4GaugeRenderer *, int, + int x, int y, const char *image, int columns, int rows, + void *subsystem, const char *identification_string); + ~AnimatedSubsystemLamp(); // @004c7134 + }; + + class AnimatedSourceLamp : // (best-effort) + public OneOfSeveral + { + public: + AnimatedSourceLamp( // @004c7160 + GaugeRate, ModeMask, L4GaugeRenderer *, int, + int x, int y, const char *image, int columns, int rows, + void *source, const char *identification_string); + ~AnimatedSourceLamp(); // @004c71f0 + }; + + + //####################################################################### + // ScalarBarGauge -- a generator-voltage bar (derives from the engine + // BarGraphBitMapScalar, L4GAUGE.HPP -- WipeGaugeScalar : GraphicGauge). + // Two ctors, one vtable (0051a0e4), shared dtor @004c733c: + // @004c721c "slotOf" variant -- AddConnection(GeneratorVoltageConnection + // @004c3288) feeding the inherited currentValue from the + // subsystem's resolved voltage source. + // @004c72ac plain-Scalar variant -- a GaugeConnectionDirectOf + // (engine template) from a DIRECT Scalar*. + // Used with "GeneratorVoltage(slotOf)" / "GeneratorVoltage(scalar)". + // + // Its value slot @0xB4 (this[0x2D]) IS the inherited + // WipeGaugeScalar::currentValue -- the connection writes it, so NO own + // field is declared (declaring one would shadow / mis-align). + // + // BC++4.52 NOTE: the void* / Scalar* 15th-argument overload pair is + // ambiguous for a literal 0/NULL argument -- every call site must pass a + // TYPED pointer. + //####################################################################### + class ScalarBarGauge : + public BarGraphBitMapScalar + { + public: + ScalarBarGauge( // @004c721c (slotOf variant) + GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number, + int left, int bottom, int right, int top, + const char *tile_image, int foreground_color, int background_color, + WipeGaugeScalar::Direction direction, Scalar min, Scalar max, + void *voltage_source, const char *identification_string); + ScalarBarGauge( // @004c72ac (plain-Scalar variant) + GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number, + int left, int bottom, int right, int top, + const char *tile_image, int foreground_color, int background_color, + WipeGaugeScalar::Direction direction, Scalar min, Scalar max, + Scalar *value_source, const char *identification_string); + ~ScalarBarGauge(); // @004c733c + }; + + + //####################################################################### + // GeneratorCluster -- the generator read-out panel (config keyword + // "GeneratorCluster": engineering buttons 9-12, GeneratorA..D). + // @004c7368 Make / @004c746c ctor / @004c7778 dtor. vtable PTR_FUN_0051a0a0. + // A GraphicGauge that owns five child gauges, parented to its own port: + // VertTwoPartBar (temperature) this[0x26] + // ScalarBarGauge (voltage 0..12kV) this[0x27] + // BitMapInverseWipe/Leak (coolant leak) this[0x28] + // TwoState x2 (status lamps) this[0x29],this[0x2A] + // Make resolves the named generator via FindSubsystem; warns + // "Subsystem not found". + //####################################################################### + class GeneratorCluster : + public GraphicGauge + { + public: + // Registered config primitive "GeneratorCluster" (15 params; NO leading + // rate token -- the panel rate is hard-wired 0xFFFF). + static MethodDescription methodDescription; + static Logical Make(int, Vector2DOf, Entity *, GaugeRenderer *); // @004c7368 + GeneratorCluster( // @004c746c (BINARY param order) + ModeMask, L4GaugeRenderer *, int owner_ID, int graphics_port_number, + int x, int y, Subsystem *subsystem, + const char *background_image, int extra_color, + const char *temp_tile, int temp_bg_color, int temp_fill_color, + const char *voltage_tile, int voltage_fg_color, + const char *leak_image, int leak_color_b, + const char *lampA_image, const char *lampB_image, + int lamp_on_color, int lamp_off_color, + const char *identification_string); + ~GeneratorCluster(); // @004c7778 + // A container panel MUST override BecameActive + Execute: the engine + // base Gauge::Execute is Fail("not overridden") (GAUGE.CPP:598), and + // the default BecameActive self-inactivates. The panel draws its + // static background pixmap; the 5 self-registered child gauges draw + // the dynamic content. + void BecameActive(); + void Execute(); + protected: + char *backgroundImage; // @0x90 this[0x24] interned .pcc (AddRef'd) + int secondaryColor; // @0x94 this[0x25] = leak colorB (stored, unread) + GraphicGauge + *temperatureBar, // @0x98 this[0x26] VertTwoPartBar + *voltageBar, // @0x9C this[0x27] ScalarBarGauge (@004c72ac variant) + *leakGauge, // @0xA0 this[0x28] BitMapInverseWipe + *lampA, // @0xA4 this[0x29] TwoState + *lampB; // @0xA8 this[0x2A] TwoState + Subsystem *subsystem; // @0xAC this[0x2B] + int _reserved0xB0; // @0xB0 this[0x2C] (trailing pad -> binary sizeof 0xB4) + }; + + + //####################################################################### + // PrepEngrScreen (config keyword "prepEngr") -- the per-engineering-screen + // STATIC LABEL overlay. @004c7b30 Make / @004c7bf0 ctor / @004c7d14 dtor / + // @004c7e48 BecameActive. vtable PTR_FUN_0051a06c. + // + // A GraphicGaugeBackground (paint-on-activation, NOT an Execute gauge -- + // the same base/vtable shape as the engine's BackgroundBitmap: only the + // dtor + BecameActive slots are overridden). Interns 7 status .pcc's + // (font + 6 labels) and, when its MFD-Eng screen (1..12) activates, paints + // the labels for the ONE subsystem whose aux screen == this screen, + // dispatched by the subsystem's ClassID. + // (FIX kept from the decode: mech/screenNumber were SWAPPED in the first + // reconstruction, and the missing methodDescription parse-skipped all 12 + // CFG lines.) + //####################################################################### + class PrepEngrScreen : + public GraphicGaugeBackground + { + public: + static MethodDescription methodDescription; // "prepEngr" + static Logical Make(int, Vector2DOf, Entity *, GaugeRenderer *); // @004c7b30 + PrepEngrScreen( // @004c7bf0 + ModeMask, L4GaugeRenderer *, unsigned int owner_ID, + int graphics_port_number, Entity *mech, int screen_number, + const char *img0/*font*/, const char *img1, const char *img2, + const char *img3, const char *img4, const char *img5, + const char *img6, const char *identification_string = "PrepEngr"); + ~PrepEngrScreen(); // @004c7d14 + void BecameActive(); // @004c7e48 (the paint slot -- NOT Execute) + protected: + int screenNumber; // @0x6C this[0x1B] + Entity *mech; // @0x70 this[0x1C] + char *statusImage[7]; // @0x74 this[0x1D..0x23] (interned .pcc names) + // binary sizeof == 0x90 (Make alloc); no offset locks (every read is + // via named members). + }; + + + //####################################################################### + // SubsystemCluster family -- the big composite panels. Each is a + // GraphicGauge that builds a dozen child gauges into one cockpit instrument. + // + // SubsystemCluster @004c8140 (vtable 0051a020) -- base "engineering" + // panel: HorizTwoPartBar + cooling/power loops + generator voltage + + // OneOfSeveralInt + OneOfSeveralStates + 2x VertTwoPartBar + LeakGauge + // + BackgroundBitmap. Uses the btXXloop / btXbus / evolt / btecmode / + // eleak art. + // + // HeatSinkCluster @004c8a6c (vtable 00519fd4) : SubsystemCluster + // adds 4 failure-annunciator TwoStates (btehfail/btepfail/btesfail) + // and a NumericDisplayScalar. Execute @004c8db0 scales heatLoad by + // 100 (_DAT_004c8df0) into the numeric read-out. + // + // MyomerCluster @004c8df4 (vtable 00519f88) : SubsystemCluster + // adds a SeekVoltageGraph ("EngrGraph") + seek-step OneOfSeveralInt. + // + // WeaponCluster @004c8fc4 (vtable 00519f38) : SubsystemCluster + // adds a SegmentArc270 (recharge dial, 0..360) + a ConfigMapGauge. + // Reads "PercentDone". Execute @004c9290 toggles the warning lamp + // past _DAT_004c92e0 (0.99). + // + // EnergyWeaponCluster @004c93b0 (vtable 00519ee8) : WeaponCluster + // adds a SeekVoltageGraph ("EngrGraph", edestryd.pcc) and a + // seek-step OneOfSeveralInt (bteseek.pcc). + // BallisticWeaponCluster @004c9558 (vtable 00519e98) : WeaponCluster + // adds 2x NumericDisplayInteger (ammo), jam/fire TwoStates + // (btejam/btefire), NumericDisplayScalarTwoState, an eject + // BitMapInverseWipeScalar (bteejtm), plus a GraphicsViewRecord + // erase tracker (this[0x3F]). + //####################################################################### + class SubsystemCluster : + public GraphicGauge + { + public: + // @004c8140. Binary param order (rate/mode are hardcoded 0xFFFF / + // ModeAlwaysActive in the base GraphicGauge ctor; the panel's two + // bit-plane MODE masks are mfd_mode (MFD-port children) and eng_mode + // (engineering-port children)). + SubsystemCluster( // @004c8140 + ModeMask mfd_mode, L4GaugeRenderer *renderer, int owner_ID, + int mfd_port, int x, int y, Subsystem *subsystem, ModeMask eng_mode, + const char *eng_port_name, const char *tile_image, + const char *label, char **image_names, + const char *identification_string); + ~SubsystemCluster(); // @004c87dc + Logical TestInstance() const; + void BecameActive(); // (re-host addition: forces the first repaint) + void Execute(); // @004c88e4 + protected: + BackgroundBitmap + *background; + BackgroundBitmap *titleBanner; // the authored 299x26 panel title // @0x90 this[0x24] BackgroundBitmap + GraphicGauge + *generatorVoltageBar, // @0x94 this[0x25] ScalarBarGauge (evolt) + *temperatureBar, // @0x98 this[0x26] HorizTwoPartBar + *coolingLoopA, // @0x9C this[0x27] AnimatedSubsystemLamp + *coolingLoopB, // @0xA0 this[0x28] + *powerSourceA, // @0xA4 this[0x29] AnimatedSourceLamp + *powerSourceB, // @0xA8 this[0x2A] + *vertBarA, // @0xAC this[0x2B] VertTwoPartBar + *vertBarB, // @0xB0 this[0x2C] + *modeLamp, // @0xB4 this[0x2D] OneOfSeveralInt + *stateLamp, // @0xB8 this[0x2E] OneOfSeveralStates + *leakGauge; // @0xBC this[0x2F] LeakGauge + Subsystem *subsystem; // @0xC0 this[0x30] + // POLARITY CORRECTED (PPC-dial fix, kept on re-host): the binary's + // *(subsystem+0x40)==1 means the subsystem FAILED -- it lights the + // destroyed-X TwoStates (the engine TwoState draws bright on NONZERO) + // and selects the dead-panel look (GetDrawState 1 = black fill). The + // old name `operating` had it backwards, inverting every consumer. + Logical failedState; // @0xC4 this[0x31] 1 = subsystem destroyed + int previousDrawState; // @0xC8 this[0x32] (Execute repaint key) + + // internal helpers (non-virtual) + void DrawPanelFrame(int color); // @004c89c4 + void ReleaseChildren(); // @004c8820 + void SetChildrenEnable(int enable); // @004c8a28 + public: + int GetDrawState(); // @004c8990 (operational-state virtual, vtbl+0x48) + }; + + class HeatSinkCluster : // @004c8a6c + public SubsystemCluster + { + public: + HeatSinkCluster( // @004c8a6c + ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, + int x, int y, Subsystem *subsystem, Subsystem *hud, ModeMask eng_mode, + const char *eng_port_name, const char *tile_image, const char *label, + const char *fail_lamp_image, char **image_names, + const char *identification_string); + ~HeatSinkCluster(); // @004c8d18 + void Execute(); // @004c8db0 + protected: + Subsystem *failSubsystem; // @0xCC this[0x33] + Logical failFlag; // @0xD0 this[0x34] + Scalar heatLoad; // @0xD4 this[0x35] (connection dst) + Scalar heatLoadScaled; // @0xD8 this[0x36] + GraphicGauge + *heatFailLamp, // @0xDC this[0x37] TwoState + *hsFailLamp, // @0xE0 this[0x38] TwoState (btehfail) + *powerFailLamp, // @0xE4 this[0x39] TwoState (btepfail) + *structFailLamp, // @0xE8 this[0x3A] TwoState (btesfail) + *heatNumeric; // @0xEC this[0x3B] NumericDisplayScalar + }; + + // + // MyomerCluster @004c8df4 (vtable 00519f88) : SubsystemCluster -- the myomer + // (muscle) panel. Adds a SeekVoltageGraph ("EngrGraph", edestryd.pcc) fed + // by a GeneratorVoltageConnection (writes seekValue) + a seek-step + // OneOfSeveralInt (bteseek.pcc). Execute = base. + // + class MyomerCluster : // @004c8df4 + public SubsystemCluster + { + public: + MyomerCluster( // @004c8df4 + ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, + int x, int y, Subsystem *subsystem, ModeMask eng_mode, + const char *eng_port_name, const char *tile_image, const char *label, + char **image_names, const char *identification_string); + ~MyomerCluster(); // @004c8f54 + protected: + Scalar seekValue; // @0xCC this[0x33] (connection dst) + GraphicGauge + *seekGraph, // @0xD0 this[0x34] SeekVoltageGraph + *seekStepLamp; // @0xD4 this[0x35] OneOfSeveralInt + }; + + class WeaponCluster : // @004c8fc4 + public SubsystemCluster + { + public: + WeaponCluster( // @004c8fc4 + ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, + int x, int y, Subsystem *subsystem, ModeMask eng_mode, + const char *eng_port_name, const char *tile_image, const char *label, + const char *cluster_image, char **image_names, + const char *identification_string); + ~WeaponCluster(); // @004c91d4 + void BecameActive(); // @004c9258 + void Execute(); // @004c9290 + virtual void DrawWarningLamp(int on); // @004c932c (virtual: BallisticWeaponCluster overrides) + protected: + char *clusterImage; // @0xCC this[0x33] (interned) + GraphicGauge *rechargeArc; // @0xD0 this[0x34] SegmentArc270 + ConfigMapGauge *configMap; // @0xD4 this[0x35] + Scalar percentDone; // @0xD8 this[0x36] (connection dst) + int warningCenterX; // @0xDC this[0x37] + int warningCenterY; // @0xE0 this[0x38] + int warningState; // @0xE4 this[0x39] + }; + + class EnergyWeaponCluster : // @004c93b0 + public WeaponCluster + { + public: + EnergyWeaponCluster( // @004c93b0 + ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, + int x, int y, Subsystem *subsystem, ModeMask eng_mode, + const char *eng_port_name, const char *tile_image, const char *label, + const char *cluster_image, char **image_names, + const char *identification_string); + ~EnergyWeaponCluster(); // @004c94dc + protected: + SeekVoltageGraph *seekGraph; // @0xE8 this[0x3A] + GraphicGauge *seekStepLamp; // @0xEC this[0x3B] OneOfSeveralInt + }; + + class BallisticWeaponCluster : // @004c9558 + public WeaponCluster + { + public: + BallisticWeaponCluster( // @004c9558 + ModeMask mfd_mode, L4GaugeRenderer *, int owner_ID, int mfd_port, + int x, int y, Subsystem *subsystem, ModeMask eng_mode, + const char *eng_port_name, const char *tile_image, const char *label, + const char *cluster_image, char **image_names, + const char *font_a, const char *font_b, + const char *identification_string); + ~BallisticWeaponCluster(); // @004c9940 + void BecameActive(); // @004c99cc + void Execute(); // @004c9a38 + void DrawWarningLamp(int on); // @004c9b50 (override: dot + ammo colour swap) + Logical TestInstance() const; // @004c9adc + protected: + Logical jammed; // @0xE8 this[0x3A] + Logical firing; // @0xEC this[0x3B] + Logical reloading; // @0xF0 this[0x3C] + int reloadStartTime; // @0xF4 this[0x3D] + Scalar reloadSeconds; // @0xF8 this[0x3E] + GraphicsViewRecord + *ammoErase; // @0xFC this[0x3F] (bring-up NULL) + GraphicGauge + *ammoNumeric, // @0x100 this[0x40] NumericDisplayScalarTwoState + *destroyedLamp, // @0x104 this[0x41] TwoState + *jamLamp, // @0x108 this[0x42] TwoState (btejam) + *fireLamp, // @0x10C this[0x43] TwoState (btefire) + *ejectWipe, // @0x110 this[0x44] BitMapInverseWipeScalar (NULL) + *ammoCountA, // @0x114 this[0x45] NumericDisplayInteger + *ammoCountB; // @0x118 this[0x46] NumericDisplayInteger + }; + + + //####################################################################### + // vehicleSubSystems -- the config primitive that builds the engineering- + // screen (MFD) subsystem cluster panels. Make @004cbaf0 (methodDescription + // @0x51c080, 8 typeString params = the 8 status strip bitmaps) is a per- + // subsystem factory: it walks the mech roster (skipping slot 0), and for + // each displayable subsystem builds the right cluster (HeatSink/Myomer/ + // Energy/Ballistic) onto one of 12 auxiliary MFD positions. This holder + // just carries the registered MethodDescription + the static Make + // dispatcher (there is no instance). + //####################################################################### + class VehicleSubSystems + { + public: + static MethodDescription methodDescription; + static Logical Make( // @004cbaf0 + int display_port_index, Vector2DOf position, + Entity *entity, GaugeRenderer *gauge_renderer); + }; + +#endif diff --git a/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md index e347bb21..b7d436fc 100644 --- a/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md +++ b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md @@ -515,3 +515,41 @@ come from the q-strip statusImage path (the 8 qblh*.pcc typeStrings VehicleSubSystems passes to DrawStatusCells), NOT from auxScreenLabel. That is the next thread. Name bitmaps stay inert (CreateMutantPixelmap8 / LookupPlayerNameBitmap bring-up stubs). + +## 5.3.29 -- THE PANEL TITLES (94.8% identical / 91% coverage) + +The 5.3.28 pass proved the titles do NOT come from auxScreenLabel's ENG-page +blit. Reading the art settled it: `qsensors.pcc` / `qmyomers.pcc` / +`qermlas.pcc` / `qppc.pcc` / `qstrk6.pcc` are **299x26 TITLE BANNERS**, while +the `qblh0..7.pcc` strings the cfg hands vehicleSubSystems are **77x120 MECH +ICONS** (which the 5.3.28 placement fix already lit). So the label IS the +title -- it just belongs on the MFD panel, not only the ENG page. + +SubsystemCluster now builds a titleBanner child from the cached +GetAuxScreenLabel(). Placement was MEASURED against the shipped +framebuffer with the A/B rig (BackgroundBitmap places by left/BOTTOM): +x + 0x09, y + 0xb3 from the panel origin -- found by a bring-up +BT_TITLE_DY knob, then a green-pixel correlation over the title band, then +baked. The authored data behind it (dumped live): Avionics screen=5 +placement=2 label=qsensors.pcc; Myomers 6/0/qmyomers; PPC_1 1/4/qppc; +PPC_2 10/6/qppc; ERMLaser_1 4/1/qermlas; ERMLaser_2 8/6; ERMLaser_3 7/4; +SRM6_1 2/1/qstrk6; SRM6_2 9/3. + +SCORE PROGRESSION vs the shipped cockpit (same GAUGE content, same mount): + 5.3.26 birth 90.1% identical / 82% coverage + 5.3.28 authored screens 93.2% / 85% + 5.3.29 titles (rough) 92.6% / 88% + 5.3.29 titles (tuned) 94.8% / 91% <-- current +"SENSOR CLUSTER", "MYOMERS", "TYPE II 65 TONS" and both "ERMED LASER RANGE +500M" banners now render in their authored positions. + +CAPTURE GOTCHA (cost two wasted runs): the grab script copies SCREEN +pixels, so any window in front of DOSBox is what gets captured (one grab +returned a white frame, another returned an unrelated 3-D app). +grab.ps1 now SetForegroundWindow()s the target and waits 1.2 s first. + +STILL OPEN: the pilot name bitmap ("Aeolus" -- CreateMutantPixelmap8 / +LookupPlayerNameBitmap bring-up stubs), some lamp frames ("OFF x" vs the +shipped state digits -- the mode accessor stand-in), and a stray magenta +"MID/ADV" block that lands over the top-left title (a widget drawing at a +wrong port offset; next thread).