Files
arcattackandClaude Fable 5 18d49491b7 Reticle Execute @004cdcf0 RECOVERED: every HUD instrument now live (task #37)
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>
2026-07-08 22:38:50 -05:00

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