The one un-exported gap in the reticle chain, read via capstone (tools/disas2.py; the annotated disasm preserved at reference/decomp/reticle_execute_004cdcf0.disasm.txt). Draw() is now a transcription of the real per-frame logic, and the HUD attr-table names (hud.hpp ids 4/5/6/8/A/B/C/D) are CONFIRMED by their Execute usage: - Range ladder: BAR from ladder-top to the caret + caret translate. - The bottom 21-tick tape is the TORSO-TWIST indicator (NOT heading): deflection = -/+(span/2) x (RotationOfTorsoHorizontal / twist limit), attrs 4/5/6. Fixed-torso BLH reads centred -- authentic static. - The circle-with-stem is the COMPASS (attr 0xD, rad->deg rotation) at (botX, botY - 3*tickMajor - 0.03), with the THREAT trail (attr 0xC) in its rotated frame: 0.05-unit attack-direction marks, fresh < 2s red, expiring at 6s. Port feed: player TakeDamage pushes the impact direction (dormant vs the passive test dummy). - Pips (composed into subB6): hidden when destroyed (attr 1 == 1), LIT when the fire cycle is LOADED (attr 0x1c == 2; port source rechargeLevel >= 1) else the dark charging ring; filtered by the weapon-GROUP bits (weaponMode & elementMask&0xF). Range plays NO part (the stored TargetWithinRange slots are never read by Execute). - Lock ring: subB9 at frame centre SPINNING 4 deg/frame while locked ([0x9d] is the spin matrix, not a heading list). - Target HOTBOX: a rectangle hugging the projected extents (x+-4 around the top-centre hotbox point, +1/-11.5 vertical; baked K=2.8145 -- the port projects through the live per-axis projection), switching to the edge arrows past +-1.6 or behind (BTProjectHotBox, L4VIDEO). - Reticle state Off/On + PrimaryHudOn full-HUD/simple-X switch, aim translate on slew move, 3D marker + PNAME player-name mesh identified (chain still deferred). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
260 lines
12 KiB
C++
260 lines
12 KiB
C++
//===========================================================================//
|
|
// File: hud.hpp //
|
|
// Project: BattleTech //
|
|
// Contents: Cockpit Heads-Up Display subsystem //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// --/--/95 ?? Initial coding. //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
|
|
// PROPRIETARY AND CONFIDENTIAL //
|
|
//===========================================================================//
|
|
//
|
|
// RECONSTRUCTED from the shipped binary (Ghidra pseudo-C, recovered shard
|
|
// part_013.c) cross-referenced with the sibling subsystem sources
|
|
// (powersub.hpp/.cpp, heat.hpp/.cpp, sensor.hpp/.cpp). NO HUD.HPP survived;
|
|
// the class body below is inferred from usage, the embedded string pool and
|
|
// the streamed-resource / attribute tables. Each method cites its @ADDR.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
// IDENTIFICATION (resolves a CLASSMAP "gap")
|
|
// ---------------------------------------------------------------------------
|
|
// * mech3.cpp factory map: HUD::CreateStreamedSubsystem @004b81d4,
|
|
// DefaultData &DAT_00511078.
|
|
// * vtable @0050511510 (slot0 dtor @004b816c), ctor @004b7f94.
|
|
// * CreateStreamedSubsystem @004b81d4 stamps classID 0x0BD6 and streamed
|
|
// model size 0x104 (resource +0x20 / +0x24), and parses the HUD-specific
|
|
// model fields "FlickerRate" / "HorizontalMovementPerSecond" /
|
|
// "HorizontalLimit" plus a "SegmentPageName" index.
|
|
// * String pool @005112d0..@00511390 ("HUD", "FlickerRate",
|
|
// "RotationOfTorsoHorizontal", "HorizontalTorsoLimitRight/Left",
|
|
// "SpeedOfTorsoHorizontal", "RangeToTarget", "Visible", "Lock",
|
|
// "HotBoxVector", "ThreatVector", "CompassHeading") names the gauge
|
|
// read-out attributes.
|
|
//
|
|
// CLASSMAP.md "Resolved conflict" already flagged @004b7f94 / vtable
|
|
// @00511510 as "the HUD torso-horizon gimbal display" mis-labelled
|
|
// "MechTech" by mech.cpp's factory switch (case 0xBD6). This file confirms
|
|
// that: classID 0xBD6, base PowerWatcher.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
// BASE CLASS
|
|
// ---------------------------------------------------------------------------
|
|
// HUD IS-A PowerWatcher (powersub.hpp): the ctor @004b7f94 chains to the
|
|
// PowerWatcher ctor FUN_004b18a4 and CreateStreamedSubsystem chains to
|
|
// PowerWatcher::CreateStreamedSubsystem FUN_004b198c. PowerWatcher is a
|
|
// HeatWatcher/HeatableSubsystem-derived *monitor* subsystem (NOT a
|
|
// PoweredSubsystem); the HUD therefore inherits the heat/voltage watchdog
|
|
// state (heat-state level @0x140, watched-voltage alarm level @0x198) used
|
|
// by the per-frame update. The shared base object occupies bytes 0..0x1D8;
|
|
// HUD's own members begin at 0x1D8. Total object size 0x2A4 (matches
|
|
// mech.cpp factory allocation for the 0xBD6 case).
|
|
//
|
|
#if !defined(HUD_HPP)
|
|
# define HUD_HPP
|
|
|
|
#if !defined(POWERSUB_HPP)
|
|
#include "powersub.hpp"
|
|
#endif
|
|
|
|
//##########################################################################
|
|
//####################### HUD::SubsystemResource #####################
|
|
//##########################################################################
|
|
//
|
|
// Extends the PowerWatcher/HeatableSubsystem resource record. The HUD fields
|
|
// begin at +0xF4 (see CreateStreamedSubsystem @004b81d4).
|
|
//
|
|
struct HUD__SubsystemResource:
|
|
public PowerWatcher::SubsystemResource
|
|
{
|
|
Scalar flickerRate; // +0xF4 "FlickerRate" (defaults to -1.0 in the SegmentPage form)
|
|
int segmentPageIndex; // +0xF8 resolved "SegmentPageName" (copied from +0x28)
|
|
Scalar horizontalMovementPerSecond; // +0xFC "HorizontalMovementPerSecond"
|
|
Scalar horizontalLimit; // +0x100 "HorizontalLimit"
|
|
};
|
|
|
|
//##########################################################################
|
|
//############################## HUD #################################
|
|
//##########################################################################
|
|
|
|
class HUD:
|
|
public PowerWatcher
|
|
{
|
|
friend struct HUDLayoutCheck; // live Watcher-branch re-base witness (hud.cpp)
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Shared Data Support
|
|
//
|
|
// DefaultData @00511078, ClassDerivations @00511088 ("HUD").
|
|
//
|
|
public:
|
|
static Derivation *GetClassDerivations(); // @00511088 (engine moved to accessor form)
|
|
static SharedData DefaultData; // @00511078
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Attribute Support
|
|
//
|
|
// AttributePointers IndexEntry table @005110c0 (each record binds a name
|
|
// to an attribute id and an object offset, the offset encoded as
|
|
// byte_offset|1 -- the same scheme used by sensor.hpp). Recovered:
|
|
// id 4 "RotationOfTorsoHorizontal" -> @0x1D8
|
|
// id 5 "HorizontalTorsoLimitRight" -> @0x1DC
|
|
// id 6 "HorizontalTorsoLimitLeft" -> @0x1E0
|
|
// id 7 "SpeedOfTorsoHorizontal" -> @0x1E4
|
|
// id 8 "RangeToTarget" -> @0x1E8
|
|
// id 9 "Visible" -> @0x1EC
|
|
// id 10 "Lock" -> @0x1F4
|
|
// id 11 "HotBoxVector" -> @0x1F8
|
|
// id 12 "ThreatVector" -> @0x1FC (Vec3)
|
|
// id 13 "CompassHeading" -> @0x208 (Vec3)
|
|
// (FlickerRate name ptr @005110bc heads the table as the category label.)
|
|
//
|
|
// CONFIRMED (task #37): the recovered reticle Execute @004cdcf0 binds and
|
|
// USES ids 4/5/6/8/0xA/0xB/0xC/0xD exactly per these names --
|
|
// RotationOfTorsoHorizontal drives the twist tape over the
|
|
// HorizontalTorsoLimit full-scales, RangeToTarget the range caret, Lock
|
|
// the spinning lock ring, HotBoxVector the target box (eye-space vec3),
|
|
// ThreatVector the compass threat trail, CompassHeading the rose
|
|
// rotation. (The earlier note about @0x1D8/@0x1EC/@0x1F8 usage remains
|
|
// for the ctor-side seeds only.)
|
|
//
|
|
public:
|
|
enum {
|
|
RotationOfTorsoHorizontalAttributeID = PowerWatcher::NextAttributeID, // 4
|
|
HorizontalTorsoLimitRightAttributeID, // 5
|
|
HorizontalTorsoLimitLeftAttributeID, // 6
|
|
SpeedOfTorsoHorizontalAttributeID, // 7
|
|
RangeToTargetAttributeID, // 8
|
|
VisibleAttributeID, // 9
|
|
LockAttributeID, // 10
|
|
HotBoxVectorAttributeID, // 11
|
|
ThreatVectorAttributeID, // 12
|
|
CompassHeadingAttributeID, // 13
|
|
NextAttributeID
|
|
};
|
|
|
|
private:
|
|
static const IndexEntry AttributePointers[]; // @005110c0
|
|
protected:
|
|
static AttributeIndexSet AttributeIndex;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Local data. Offsets are byte offsets into the shipped object; names are
|
|
// best-effort from the attribute pool + ctor/HudSimulation usage.
|
|
//
|
|
protected:
|
|
Scalar flickerRate; // @0x1D8 resource +0xF4 (flicker / blink period threshold)
|
|
// Torso / target read-outs sampled each construction from the linked
|
|
// targeting entity (owner Mech +0x438 -> this->linkedEntity @0x234):
|
|
Scalar torsoLimitRight; // @0x1DC <- linkedEntity +0x1D8
|
|
Scalar torsoLimitLeft; // @0x1E0 <- linkedEntity +0x1DC
|
|
Scalar torsoSpeed; // @0x1E4 <- linkedEntity +0x1E0
|
|
Scalar rangeToTarget; // @0x1E8 <- linkedEntity +0x1E8 (and recomputed in sim @0x1EC)
|
|
Scalar targetRange; // @0x1EC computed line-of-sight distance to the target
|
|
int lockFlag; // @0x1F4 init 1
|
|
int visible; // @0x1F8 set 0/1 from heat + line-of-sight each frame
|
|
Point3D threatVector; // @0x1FC init (0,0,0) ("ThreatVector")
|
|
Point3D compassHeading; // @0x208 init (0,0,0) ("CompassHeading"); see SetCompassHeading
|
|
Scalar hotBoxScalar; // @0x214 init 0
|
|
Scalar blinkTimer; // @0x218 accumulates time, reset against flickerRate
|
|
Scalar flickerTimer; // @0x21C
|
|
int blinkState; // @0x224 init 1 (visible phase of the blink)
|
|
int transitionFlag; // @0x22C init 0
|
|
Scalar previousHorizontal; // @0x230
|
|
int linkedEntity; // @0x234 owner Mech +0x438 (active weapon/reticle source)
|
|
HeatAlarm statusAlarm; // @0x238 2-level page/status indicator (Initialize/SetLevel surface)
|
|
// Free-aim / reticle-slew demand: WRITTEN by MechControlsMapper::InterpretControls
|
|
// (@004afd10 cockpit+0x28c <- stick yaw when the torso's horizontal twist is
|
|
// disabled; zeroed in Basic mode). Was placeholder-named flickerPhase (init-only).
|
|
Scalar freeAimSlew; // @0x28C
|
|
public:
|
|
void SetFreeAimSlew(Scalar v) { freeAimSlew = v; } // mapper write (@004afd10 cockpit+0x28c)
|
|
protected:
|
|
Scalar scratch290; // @0x290 init 0
|
|
Scalar horizontalTorsoOffset; // @0x294 init 0; clamped to +/- horizontalLimit, written to owner +0x36C
|
|
Scalar horizontalMovementPerSecond; // @0x298 resource +0xFC
|
|
Scalar horizontalLimit; // @0x29C resource +0x100
|
|
int flickerActive; // @0x2A0 current flicker on/off (UpdateFlicker @004b7ed4)
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Simulation Support
|
|
//
|
|
public:
|
|
typedef void
|
|
(HUD::*Performance)(Scalar time_slice);
|
|
|
|
void
|
|
SetPerformance(Performance performance)
|
|
{
|
|
Check(this);
|
|
activePerformance = (Simulation::Performance)performance;
|
|
}
|
|
|
|
void
|
|
HudSimulation(Scalar time_slice); // @004b7830 (Performance, PTR @00511170)
|
|
|
|
void
|
|
SetCompassHeading(const Point3D &heading); // @004b7810 (writes compassHeading @0x208)
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Subsystem virtual overrides (vtable @00511510)
|
|
//
|
|
public:
|
|
Logical
|
|
HandleMessage(int message); // slot 9, @004b7780 (body not captured)
|
|
void
|
|
ResetToInitialState(Logical powered = True); // slot 10, @004b77bc
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Test Class Support
|
|
//
|
|
public:
|
|
static Logical TestClass(Mech&);
|
|
Logical TestInstance() const; // @004b81b8
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction and Destruction
|
|
//
|
|
public:
|
|
typedef HUD__SubsystemResource SubsystemResource;
|
|
|
|
HUD(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data = DefaultData
|
|
); // @004b7f94
|
|
~HUD(); // @004b816c
|
|
|
|
static int
|
|
CreateStreamedSubsystem( // @004b81d4
|
|
NotationFile *model_file,
|
|
const char *model_name,
|
|
const char *subsystem_name,
|
|
SubsystemResource *subsystem_resource,
|
|
NotationFile *subsystem_file,
|
|
const ResourceDirectories *directories,
|
|
int passes = 1
|
|
);
|
|
|
|
protected:
|
|
// Flicker helper @004b7ed4: drives flickerActive (@0x2A0) toward 0,
|
|
// returns non-zero while still flickering.
|
|
Logical UpdateFlicker(Scalar time_slice); // @004b7ed4
|
|
|
|
//
|
|
// Reconstruction accessors for the inherited watchdog state read by
|
|
// HudSimulation: heat-watcher flag (this+0x40), watched-voltage alarm
|
|
// level (@0x198) and heat-state level (@0x140). The PowerWatcher /
|
|
// HeatWatcher base in this header set does not expose these, so they are
|
|
// best-effort placeholders that let the per-frame page-gating logic
|
|
// compile; a human should bind them to the real inherited members.
|
|
// (See CROSS-FAMILY NEEDS.)
|
|
//
|
|
int HeatWatcherFlag() const { return 0; }
|
|
int WatchedVoltageLevel() const { return 0; }
|
|
int HeatStateLevel() const { return 0; }
|
|
};
|
|
|
|
#endif
|