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>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cf9f56044e
commit
18d49491b7
+31
-7
@@ -93,13 +93,37 @@ ladder 0..1200 m, bottom heading tape, center cross + dot; tick ladders via FUN_
|
||||
range caret binds to the live target range (`BTSetHudTargetRange`, fed by mech4's targeting step).
|
||||
**Weapon pips:** the build loop (part_014.c:5386) registers EVERY subsystem
|
||||
`IsDerivedFrom(0x511830 = MechWeapon)` — lasers, PPCs AND missile launchers (BLH = 7 pips) — via
|
||||
`AddWeapon` @004cdac0 (verified store map in btl4vid.hpp), reading WeaponRange/PipPosition/
|
||||
PipColor/PipExtendedRange/TargetWithinRange/SimulationState(attrs 1+0x1c)/RearFiring. Pip A (lit,
|
||||
authored PipColor) when TargetWithinRange, else the dark ring B. [T2 geometry+wiring]
|
||||
The binary's per-frame `Execute` @004cdcf0 sits in an UN-EXPORTED decomp gap — the Draw dynamics
|
||||
(caret slide, pip lit/dark; lock rings/turn arrows built but static) are best-effort [T3].
|
||||
Deferred: PNAME1-8.bgf 3D pip meshes + the 3D marker chain; the `blx_cop` canopy shell renders
|
||||
black and is hidden by name filter (`BT_INSIDE_COCKPIT=1` shows it) — see [[open-questions]].
|
||||
`AddWeapon` @004cdac0 (verified store map in btl4vid.hpp).
|
||||
|
||||
**The per-frame `Execute` @004cdcf0 is RECOVERED (task #37, capstone disasm via
|
||||
`tools/disas2.py` — the full annotated read: the task-37 commit + btl4vid.cpp comments) [T1],
|
||||
and every instrument is now live [T2]:**
|
||||
- **Right ladder** = range 0–1200 m: a BAR from ladder-top to the caret + the yellow caret
|
||||
translate (`-scaleY·frac`); pegs at 1200 with no target.
|
||||
- **Bottom 21-tick tape = the TORSO-TWIST indicator** (NOT a heading tape): deflection line +
|
||||
carets at `∓(span/2)·(RotationOfTorsoHorizontal / HorizontalTorsoLimit)` (HUD attrs 4/5/6).
|
||||
Fixed-torso BLH: centred (authentic static).
|
||||
- **Circle-with-stem = the COMPASS** (HUD attr 0xD CompassHeading, rad→deg rotation) at
|
||||
`(botX, botY−3·tickMajor−0.03)`; the **THREAT trail** (attr 0xC ThreatVector) draws inside its
|
||||
rotated frame: 0.05-unit attack-direction marks, fresh <2 s red, expiring at 6 s, 1 s blink tick.
|
||||
Port feed: the player's TakeDamage handler pushes the impact direction.
|
||||
- **Pips** (composed into subB6, master-called): hidden when the weapon's DAMAGE state == 1
|
||||
(destroyed, attr 1); LIT (A) when the FIRE-CYCLE state == 2 (loaded, attr 0x1c; port source
|
||||
rechargeLevel ≥ 1) else dark ring (B, charging); filtered by `weaponMode & elementMask&0xF`
|
||||
(the weapon-GROUP bits Front/Rear/Left/Right). **Range plays NO part** — Execute never reads
|
||||
the stored TargetWithinRange slots.
|
||||
- **Lock ring** = subB9 (ring+cross) at frame centre, **SPINNING 4°/frame** while the Lock attr
|
||||
(0xA) is up (port: while a target is locked); the binary also hangs the PNAMEx.bgf player-name
|
||||
mesh on the 3D marker here (3D chain still deferred).
|
||||
- **Target HOTBOX** (attr 0xB HotBoxVector) = a rectangle hugging the projected extents — x±4
|
||||
around the hotbox point, +1/−11.5 vertical (K=2.8145 baked projection; the port uses the live
|
||||
per-axis projection) — switching to the left/right edge ARROW past ±1.6 or behind.
|
||||
- Reticle state Off/On + `PrimaryHudOn` (mask 0x20) picks full HUD vs the "simple X" list;
|
||||
the aim group translates by `reticlePosition` (SetMatrix, rebuilt on slew move).
|
||||
This recovery also CONFIRMS the HUD attr-table ids 4/5/6/8/0xA/0xB/0xC/0xD name↔use pairings
|
||||
(hud.hpp had flagged them uncertain). Deferred: PNAME1-8.bgf 3D marker chain; the `blx_cop`
|
||||
canopy shell renders black and is hidden by name filter (`BT_INSIDE_COCKPIT=1` shows it) — see
|
||||
[[open-questions]].
|
||||
|
||||
## Key Relationships
|
||||
- Full history: `docs/GAUGE_COMPOSITE.md`; reticle recovery: `phases/phase-02-dpl2d-reticle.md`.
|
||||
|
||||
@@ -60,15 +60,16 @@ authentic path scoped.
|
||||
by the mouse — wire the real channel when pod hardware lands; (c) pre-burial, the pick still
|
||||
tests the sinking wreck's collision box at its parked position (pre-existing wreck-targeting
|
||||
behavior).
|
||||
- **Cockpit HUD leftovers (task #35 residue, 2026-07-08)** — the reticle + 7 weapon pips are LIVE
|
||||
(see [[gauges-hud]] + `phases/phase-02-dpl2d-reticle.md`), but three pieces remain: (a) the
|
||||
- **Cockpit HUD leftovers (tasks #35-#37 residue, 2026-07-08)** — the reticle + 7 weapon pips +
|
||||
ALL the instrument dynamics are LIVE (the Execute @004cdcf0 gap was RECOVERED via capstone
|
||||
disasm, task #37 — see [[gauges-hud]] for the full instrument map). Remaining: (a) the
|
||||
**blx_cop canopy shell** renders BLACK (untextured/unlit in our inside view) so it's hidden by a
|
||||
name filter — `BT_INSIDE_COCKPIT=1` shows it; the authentic look needs its material/lighting
|
||||
path; (b) the **PNAME1-8.bgf 3D pip meshes** + the 3D target-designator marker chain (the ctor's
|
||||
15-arg form binds 8 HUD-subsystem attrs we bypass); (c) the binary per-frame **Execute
|
||||
@004cdcf0** is in an un-exported decomp gap — the Draw dynamics (lock rings, off-screen turn
|
||||
arrows, blink states, the 3 cached "SimulationState" constants 2/3/1) are best-effort [T3];
|
||||
a Ghidra re-export of the 0x4cdcf0 region would let us transcribe it properly.
|
||||
path; (b) the **3D marker chain** ([0x31c]/[0x320] dpl objects + the PNAME1-8.bgf player-name
|
||||
meshes the Execute hangs over the locked target); (c) the "simple X" list [0x264]
|
||||
(PrimaryHudOn off — the minimal reticle mode) is built empty in the port; (d) the HUD Lock
|
||||
attr's (0xA) own HudSimulation producer (port analog: locked == designated) and the compass
|
||||
rotation SIGN are [T3].
|
||||
- **StatusMessagePool (NULL stub, btstubs.cpp:62)** — the per-player status-message queue.
|
||||
BTPlayer+0x1dc is never populated → MessageBoard is empty (authentic for bring-up). What
|
||||
resolves it: wire the pool + decode `AddStatusMessage @0042e580` + the `Player__StatusMessage`
|
||||
|
||||
@@ -231,6 +231,45 @@ int BTProjectToReticle(const float world[3], float *rx, float *ry)
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// The target HOTBOX projection (the recovered reticle Execute @4cdff7-4ce0f9
|
||||
// [T1]): project the box hugging the target's extents -- x +-4 around the
|
||||
// hotbox point (the target's top-centre), +1 above / -11.5 below it (the
|
||||
// authored pod mech envelope; constants @4cee7c/4ceea0/4ceeac). Returns 1
|
||||
// with the reticle-frame edges when the box is displayable; 0 when the target
|
||||
// is BEHIND or both x-edges pass +-1.6 (constants @4cee88/90) -- then *side
|
||||
// carries the arrow side (-1 left / +1 right).
|
||||
//
|
||||
int BTProjectHotBox(const float top[3], float *xl, float *xr,
|
||||
float *yt, float *yb, int *side)
|
||||
{
|
||||
*side = 0;
|
||||
if (!gBTAimCamValid || gBTAimP11 <= 0.0f || gBTAimP22 <= 0.0f
|
||||
|| gBTAimVpH <= 0.0f)
|
||||
return 0;
|
||||
float rel[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
rel[i] = top[i] - gBTAimCamPos[i];
|
||||
const float xc = rel[0]*gBTAimCamX[0] + rel[1]*gBTAimCamX[1] + rel[2]*gBTAimCamX[2];
|
||||
const float yc = rel[0]*gBTAimCamY[0] + rel[1]*gBTAimCamY[1] + rel[2]*gBTAimCamY[2];
|
||||
const float zc = rel[0]*gBTAimCamZ[0] + rel[1]*gBTAimCamZ[1] + rel[2]*gBTAimCamZ[2];
|
||||
const float depth = -zc; // camera looks down -Z
|
||||
if (depth < 0.1f)
|
||||
{
|
||||
*side = (xc >= 0.0f) ? 1 : -1;
|
||||
return 0;
|
||||
}
|
||||
const float xs = (gBTAimP11 / depth) * (gBTAimVpW / gBTAimVpH);
|
||||
const float ys = gBTAimP22 / depth;
|
||||
*xl = (xc - 4.0f) * xs;
|
||||
*xr = (xc + 4.0f) * xs;
|
||||
*yt = -(yc + 1.0f) * ys; // 1 above the hotbox point
|
||||
*yb = -(yc - 11.5f) * ys; // 11.5 below it
|
||||
if (*xl > 1.6f && *xr > 1.6f) { *side = 1; return 0; }
|
||||
if (*xl < -1.6f && *xr < -1.6f) { *side = -1; return 0; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
void BTPushBeamKind(float fx, float fy, float fz, float tx, float ty, float tz,
|
||||
unsigned color, float ttl, float width, int kind)
|
||||
{
|
||||
|
||||
+263
-50
@@ -61,6 +61,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <time.h> // clock() -- the threat-trail ages (task #37)
|
||||
|
||||
|
||||
//
|
||||
@@ -1043,6 +1044,9 @@ BTReticleRenderable::BTReticleRenderable(Entity *entity, Scalar *range_attr)
|
||||
maxRange = kRetMaxRange; minRange = 0.0f;
|
||||
rangeScale= maxRange - minRange; // [0x8d] -- AddWeapon divides by it
|
||||
rangeAttr2= range_attr;
|
||||
pipsBuilt = 0; // recovered-Execute dynamic state
|
||||
lockShown = 0;
|
||||
lockSpinDeg= 0.0f;
|
||||
|
||||
masterList = dpl2d_NewDisplayList();
|
||||
aimDotList = dpl2d_NewDisplayList();
|
||||
@@ -1136,19 +1140,25 @@ BTReticleRenderable::BTReticleRenderable(Entity *entity, Scalar *range_attr)
|
||||
dpl2d_ClosePolyline(m);
|
||||
dpl2d_PopState(m);
|
||||
}
|
||||
// bottom-anchor group: a small arc + drop line under the centre
|
||||
// COMPASS group (Execute @4ce6e0-4ce7e4 [T1]): [0x278] bottomAnchor holds a
|
||||
// rotate(CompassHeading rad->deg) + translate to (botX, botY - 3*tickMajor
|
||||
// - 0.03) -- the compass rose (circle + north stem, authored at the origin)
|
||||
// spins with the mech heading at the bottom-left of the twist tape. The
|
||||
// THREAT trail [0x2e8] draws in the same frame: 0.05-unit direction marks
|
||||
// from the compass centre toward recent damage sources (fresh = red, aging
|
||||
// = yellow, expired at 6s -- Execute @4ce3e2-4ce6ce [T1]).
|
||||
dpl2d_PushState(m);
|
||||
dpl2d_CallList(m, bottomAnchor);
|
||||
dpl2d_Circle(m, 0.0f, 0.0f, 0.03f, 0); // 0x3cf5c28f-family arc [T3 radius]
|
||||
dpl2d_CallList(m, bottomAnchor); // [0x9e] the compass rotate+translate
|
||||
dpl2d_Circle(m, 0.0f, 0.0f, 0.03f, 0); // the rose ring [T3 radius]
|
||||
dpl2d_OpenLines(m);
|
||||
dpl2d_AddPoint(m, 0.0f, -0.04f);
|
||||
dpl2d_AddPoint(m, 0.0f, -0.04f); // the north stem
|
||||
dpl2d_AddPoint(m, 0.0f, -0.005f);
|
||||
dpl2d_CloseLines(m);
|
||||
dpl2d_CallList(m, subBA);
|
||||
dpl2d_CallList(m, subBA); // [0xba] the threat-direction trail
|
||||
dpl2d_PopState(m);
|
||||
dpl2d_CallList(m, subB6);
|
||||
dpl2d_CallList(m, headingList);
|
||||
dpl2d_CallList(m, subB7);
|
||||
dpl2d_CallList(m, subB6); // [0xb6] the composed weapon pips
|
||||
dpl2d_CallList(m, headingList); // [0x9d] the LOCK-RING SPIN matrix (4 deg/frame)
|
||||
dpl2d_CallList(m, subB7); // [0xb7] the lock-ring slot (subB9 when locked)
|
||||
dpl2d_End(m);
|
||||
dpl2d_Compile(m);
|
||||
|
||||
@@ -1230,14 +1240,24 @@ void
|
||||
if (range < minRange) range = minRange;
|
||||
if (range > maxRange) range = maxRange;
|
||||
Scalar frac = (range - minRange) / (maxRange - minRange);
|
||||
Scalar six[6] = { 1, 0, 0, 1, 0, -scaleY * frac };
|
||||
// [0x26c] = the range BAR (a line from the ladder TOP down to the caret
|
||||
// height) + the caret translate (Execute @4ceb16-4cebf8: AddPoint(originX,
|
||||
// originY-scaleY), AddPoint(originX, originY-scaleY*frac), then the
|
||||
// SetMatrix(translate(0, -scaleY*frac)) the caret triangles ride).
|
||||
dpl2d_Begin(rangeCaretR, 1);
|
||||
dpl2d_ConcatMatrix(rangeCaretR, six);
|
||||
dpl2d_OpenLines(rangeCaretR);
|
||||
dpl2d_AddPoint(rangeCaretR, originX, originY - scaleY);
|
||||
dpl2d_AddPoint(rangeCaretR, originX, originY - scaleY * frac);
|
||||
dpl2d_CloseLines(rangeCaretR);
|
||||
{
|
||||
Scalar six[6] = { 1, 0, 0, 1, 0, -scaleY * frac };
|
||||
dpl2d_ConcatMatrix(rangeCaretR, six);
|
||||
}
|
||||
dpl2d_End(rangeCaretR);
|
||||
dpl2d_Compile(rangeCaretR);
|
||||
|
||||
// the AIM translate [0x9a] (task #36): the slewed crosshair position, in
|
||||
// reticle coordinates (fed by the mouse / stick free-aim in mech4).
|
||||
// the AIM translate [0x9a] (Execute @4cde59-4cdedd [T1]: rebuilt on slew
|
||||
// move with SetMatrix(translate(reticlePosition))).
|
||||
{
|
||||
extern float gBTAimX, gBTAimY;
|
||||
Scalar t6[6] = { 1, 0, 0, 1, gBTAimX, gBTAimY };
|
||||
@@ -1247,32 +1267,182 @@ void
|
||||
dpl2d_Compile(aimDotList);
|
||||
}
|
||||
|
||||
// the TARGET DESIGNATOR slot [0xa1] (task #36, Reticle::TargetDesignatorOn
|
||||
// [T0]): the designated target's projected screen point gets the lock ring
|
||||
// (subB9 ring+cross while the crosshair is ON it, subB8 plain ring while
|
||||
// designated); off-screen / behind, the edge turn ARROW points the way back.
|
||||
// the TORSO-TWIST TAPE carets [0x9c] (Execute @4ce7e5-4cea9a [T1]): the
|
||||
// bottom 21-tick tape is the TWIST indicator -- a deflection line from the
|
||||
// tape centre plus the over/under carets translated by
|
||||
// offset = -/+(span/2) * (RotationOfTorsoHorizontal / twist limit)
|
||||
// (attrs 4/5/6: the live twist over the per-side limits; full deflection =
|
||||
// the torso hard against its stop). The fixed-torso BLH reads 0 (centred).
|
||||
{
|
||||
extern int gBTHudLockState; // 0 none / 1 designated / 2 hot
|
||||
extern float gBTHudLockWorld[3];
|
||||
extern int BTProjectToReticle(const float world[3], float *rx, float *ry);
|
||||
extern float gBTHudTwist, gBTHudTwistLimit;
|
||||
float off = 0.0f;
|
||||
if (gBTHudTwistLimit > 1e-4f)
|
||||
{
|
||||
off = -(kRetBotSpan * 0.5f) * (gBTHudTwist / gBTHudTwistLimit);
|
||||
if (off < -kRetBotSpan * 0.5f) off = -kRetBotSpan * 0.5f;
|
||||
if (off > kRetBotSpan * 0.5f) off = kRetBotSpan * 0.5f;
|
||||
}
|
||||
const float cx = kRetBotX + kRetBotSpan * 0.5f;
|
||||
dpl2d_Begin(rangeCaretB, 1);
|
||||
dpl2d_OpenLines(rangeCaretB);
|
||||
dpl2d_AddPoint(rangeCaretB, cx, kRetBotY);
|
||||
dpl2d_AddPoint(rangeCaretB, cx + off, kRetBotY);
|
||||
dpl2d_CloseLines(rangeCaretB);
|
||||
{
|
||||
Scalar t6[6] = { 1, 0, 0, 1, off, 0 };
|
||||
dpl2d_ConcatMatrix(rangeCaretB, t6);
|
||||
}
|
||||
dpl2d_End(rangeCaretB);
|
||||
dpl2d_Compile(rangeCaretB);
|
||||
}
|
||||
|
||||
// the COMPASS rotate [0x9e] (Execute @4ce6e0-4ce7e4 [T1]): the rose spins
|
||||
// by CompassHeading (radians; the binary converts x57.2958 for its degree
|
||||
// recorder) and sits at (botX, botY - 3*tickMajor - 0.03).
|
||||
{
|
||||
extern float gBTHudHeading;
|
||||
const float c = (float)cos((double)gBTHudHeading);
|
||||
const float s = (float)sin((double)gBTHudHeading);
|
||||
Scalar r6[6] = { c, s, -s, c,
|
||||
kRetBotX, kRetBotY - 3.0f * kRetTickMajor - 0.03f };
|
||||
dpl2d_Begin(bottomAnchor, 1);
|
||||
dpl2d_ConcatMatrix(bottomAnchor, r6);
|
||||
dpl2d_End(bottomAnchor);
|
||||
dpl2d_Compile(bottomAnchor);
|
||||
}
|
||||
|
||||
// the THREAT trail [0xba] (Execute @4ce3e2-4ce6ce [T1]): direction marks
|
||||
// from the compass centre toward recent damage sources. Each mark is a
|
||||
// 0.05-unit line along the (mech-local x,z) attack direction; FRESH marks
|
||||
// (< 2s) draw red, aging ones yellow, expired (> 6s) drop.
|
||||
{
|
||||
extern int BTTakeHudThreats(float out_xz[][2], float out_age[], int max_n);
|
||||
float txz[16][2]; float tage[16];
|
||||
const int n = BTTakeHudThreats(txz, tage, 16);
|
||||
dpl2d_Begin(subBA, 1);
|
||||
if (n > 0)
|
||||
{
|
||||
// stale (yellow) first, then fresh (red) -- the binary's color split
|
||||
dpl2d_SetColor(subBA, 0.75f, 0.75f, 0.0f);
|
||||
for (int pass = 0; pass < 2; ++pass)
|
||||
{
|
||||
const int wantFresh = (pass == 1);
|
||||
if (pass == 1)
|
||||
dpl2d_SetColor(subBA, 0.75f, 0.0f, 0.0f);
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
const int isFresh = (tage[i] < 2.0f);
|
||||
if (isFresh != wantFresh)
|
||||
continue;
|
||||
dpl2d_OpenLines(subBA);
|
||||
dpl2d_AddPoint(subBA, 0.0f, 0.0f);
|
||||
dpl2d_AddPoint(subBA, txz[i][0] * 0.05f, txz[i][1] * 0.05f);
|
||||
dpl2d_CloseLines(subBA);
|
||||
}
|
||||
}
|
||||
dpl2d_SetColor(subBA, 0.0f, 0.75f, 0.0f); // restore green
|
||||
}
|
||||
dpl2d_End(subBA);
|
||||
dpl2d_Compile(subBA);
|
||||
}
|
||||
|
||||
// the WEAPON PIPS [0xb6] (Execute @4ce2c2-4ce3e1 [T1]): the composed pip
|
||||
// list the master calls. Per weapon: skip unless its GROUP is displayed
|
||||
// (weaponMode & elementMask low bits); HIDE it when destroyed (attr 1 ==
|
||||
// 1); the LIT pip (A) when the fire cycle is LOADED (attr 0x1c == 2, our
|
||||
// source: rechargeLevel >= 1), else the dark ring (B, charging). Range
|
||||
// plays NO part -- the binary never reads TargetWithinRange here.
|
||||
{
|
||||
extern int gBTHudGroupMask; // element-mask low bits (0xF = all)
|
||||
int dirty = 0;
|
||||
for (int i = 0; i < weaponCount; ++i)
|
||||
{
|
||||
const int destroyed = (simStateAttr[i] != 0 && *simStateAttr[i] == 1);
|
||||
const int loaded = (cycleReady[i] != 0 && *cycleReady[i] >= 0.999f);
|
||||
if (destroyed != simStateCache[i] || loaded != alarmCache[i])
|
||||
{
|
||||
simStateCache[i] = destroyed;
|
||||
alarmCache[i] = loaded;
|
||||
dirty = 1;
|
||||
}
|
||||
}
|
||||
static int s_lastMask = -1;
|
||||
if (gBTHudGroupMask != s_lastMask) { s_lastMask = gBTHudGroupMask; dirty = 1; }
|
||||
if (dirty || !pipsBuilt)
|
||||
{
|
||||
pipsBuilt = 1;
|
||||
dpl2d_Begin(subB6, 1);
|
||||
for (int i = 0; i < weaponCount; ++i)
|
||||
{
|
||||
if ((weaponMode[i] & gBTHudGroupMask) == 0)
|
||||
continue; // group not displayed
|
||||
if (simStateCache[i])
|
||||
continue; // destroyed: no pip at all
|
||||
dpl2d_CallList(subB6,
|
||||
alarmCache[i] ? pipDisplayListA[i] : pipDisplayListB[i]);
|
||||
}
|
||||
dpl2d_End(subB6);
|
||||
dpl2d_Compile(subB6);
|
||||
}
|
||||
}
|
||||
|
||||
// the LOCK RING [0xb7] + its SPIN [0x9d] (Execute @4cebf9-4cee54 [T1]):
|
||||
// while a target is locked the ring+cross (subB9) draws at the reticle
|
||||
// frame centre, spinning 4 degrees per frame; unlocked it clears. (The
|
||||
// binary also hangs the PNAMEx.bgf player-name mesh on the 3D marker here
|
||||
// -- the 3D marker chain remains deferred.)
|
||||
{
|
||||
extern int gBTHudLockState;
|
||||
const int locked = (gBTHudLockState != 0);
|
||||
if (locked != lockShown)
|
||||
{
|
||||
lockShown = locked;
|
||||
dpl2d_Begin(subB7, 1);
|
||||
if (locked)
|
||||
dpl2d_CallList(subB7, subB9);
|
||||
dpl2d_End(subB7);
|
||||
dpl2d_Compile(subB7);
|
||||
}
|
||||
if (locked)
|
||||
{
|
||||
lockSpinDeg += 4.0f; // per FRAME, the binary's rate
|
||||
if (lockSpinDeg >= 360.0f) lockSpinDeg -= 360.0f;
|
||||
const float a = lockSpinDeg * 0.0174532925f;
|
||||
const float c = (float)cos((double)a), s = (float)sin((double)a);
|
||||
Scalar r6[6] = { c, s, -s, c, 0, 0 };
|
||||
dpl2d_Begin(headingList, 1);
|
||||
dpl2d_ConcatMatrix(headingList, r6);
|
||||
dpl2d_End(headingList);
|
||||
dpl2d_Compile(headingList);
|
||||
}
|
||||
}
|
||||
|
||||
// the TARGET HOTBOX / edge arrows [0xa1] (Execute @4cdf6f-4ce28b [T1]):
|
||||
// the box is a RECTANGLE hugging the target's projected extents -- x +-4
|
||||
// around the hotbox point, +1 above / -11.5 below it (the authored pod
|
||||
// mech envelope) -- switching to the left/right edge arrow when both
|
||||
// edges pass +-1.6 or the target is behind.
|
||||
{
|
||||
extern int gBTHudLockState;
|
||||
extern float gBTHudLockWorld[3]; // the target's hotbox point (top)
|
||||
extern int BTProjectHotBox(const float top[3], float *xl, float *xr,
|
||||
float *yt, float *yb, int *side);
|
||||
dpl2d_Begin(crossList, 1);
|
||||
if (gBTHudLockState != 0)
|
||||
{
|
||||
float tx = 0.0f, ty = 0.0f;
|
||||
const int onScreen = BTProjectToReticle(gBTHudLockWorld, &tx, &ty);
|
||||
const float xLimit = 1.55f; // past the ladders = off-screen
|
||||
if (onScreen && tx > -xLimit && tx < xLimit && ty > -1.0f && ty < 1.0f)
|
||||
float xl, xr, yt, yb; int side;
|
||||
if (BTProjectHotBox(gBTHudLockWorld, &xl, &xr, &yt, &yb, &side))
|
||||
{
|
||||
Scalar d6[6] = { 1, 0, 0, 1, tx, ty };
|
||||
dpl2d_PushState(crossList);
|
||||
dpl2d_ConcatMatrix(crossList, d6);
|
||||
dpl2d_CallList(crossList, (gBTHudLockState == 2) ? subB9 : subB8);
|
||||
dpl2d_PopState(crossList);
|
||||
dpl2d_OpenPolyline(crossList); // the closed hotbox rectangle
|
||||
dpl2d_AddPoint(crossList, xl, yt);
|
||||
dpl2d_AddPoint(crossList, xl, yb);
|
||||
dpl2d_AddPoint(crossList, xr, yb);
|
||||
dpl2d_AddPoint(crossList, xr, yt);
|
||||
dpl2d_ClosePolyline(crossList);
|
||||
}
|
||||
else
|
||||
{
|
||||
dpl2d_CallList(crossList, (tx < 0.0f) ? leftArrow : rightArrow);
|
||||
dpl2d_CallList(crossList, (side < 0) ? leftArrow : rightArrow);
|
||||
}
|
||||
}
|
||||
dpl2d_End(crossList);
|
||||
@@ -1280,14 +1450,6 @@ void
|
||||
}
|
||||
|
||||
dpl2d_ExecuteList(masterList, device);
|
||||
|
||||
for (int i = 0; i < weaponCount; ++i)
|
||||
{
|
||||
int inRange = 0;
|
||||
if (withinRangePtr[i] != 0) // [0x18c] TargetWithinRange
|
||||
inRange = (*withinRangePtr[i] != 0);
|
||||
dpl2d_ExecuteList(inRange ? pipDisplayListA[i] : pipDisplayListB[i], device);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1338,11 +1500,11 @@ BTReticleRenderable *BTBuildReticle(Entity *mech)
|
||||
(int *)wp->WithinRangePtr(),
|
||||
wp->PipExtendedRange(),
|
||||
r, g, b,
|
||||
(int *)wp->SimulationStatePtr(), // attr 0x1c "SimulationState"
|
||||
wp->RechargeLevelPtr(), // attr 0x1c analog: loaded when >= 1
|
||||
2, 3,
|
||||
(int *)wp->SimulationStatePtr(), // attr 1 "SimulationState"
|
||||
(int *)wp->SimulationStatePtr(), // attr 1: damage state (1 = destroyed)
|
||||
1,
|
||||
1 /* front (RearFiring==0 on every BLH weapon) */);
|
||||
1 /* Front group (RearFiring==0 on every BLH weapon) */);
|
||||
}
|
||||
DEBUG_STREAM << "[hud] reticle built: " << gBTReticle->WeaponCount()
|
||||
<< " weapon pip(s) registered\n" << std::flush;
|
||||
@@ -1352,6 +1514,56 @@ BTReticleRenderable *BTBuildReticle(Entity *mech)
|
||||
// the live target-range storage the reticle's caret binds to
|
||||
Scalar gBTHudRangeStorage = 0.0f;
|
||||
|
||||
//
|
||||
// THREAT trail store (recovered Execute @4ce3e2-4ce6ce): timestamped attack
|
||||
// directions pushed on player damage (mech.cpp handler); Draw ages them --
|
||||
// fresh < 2s (red), expired > 6s (dropped). World-frame (x,z) directions:
|
||||
// they draw inside the compass's rotated frame, so the marks stay
|
||||
// world-referenced on the rose like a true compass bearing.
|
||||
//
|
||||
struct BTHudThreat { float x, z; clock_t born; };
|
||||
static BTHudThreat gBTHudThreats[16];
|
||||
static int gBTHudThreatCount = 0;
|
||||
|
||||
void BTPushHudThreat(float wx, float wz)
|
||||
{
|
||||
float len = sqrtf(wx * wx + wz * wz);
|
||||
if (len < 1e-4f)
|
||||
return;
|
||||
if (gBTHudThreatCount >= 16) // drop the oldest
|
||||
{
|
||||
for (int i = 1; i < 16; ++i)
|
||||
gBTHudThreats[i - 1] = gBTHudThreats[i];
|
||||
gBTHudThreatCount = 15;
|
||||
}
|
||||
BTHudThreat &t = gBTHudThreats[gBTHudThreatCount++];
|
||||
t.x = wx / len;
|
||||
t.z = wz / len;
|
||||
t.born = clock();
|
||||
}
|
||||
|
||||
int BTTakeHudThreats(float out_xz[][2], float out_age[], int max_n)
|
||||
{
|
||||
const clock_t now = clock();
|
||||
int n = 0;
|
||||
for (int i = 0; i < gBTHudThreatCount; ++i)
|
||||
{
|
||||
const float age = (float)(now - gBTHudThreats[i].born) / (float)CLOCKS_PER_SEC;
|
||||
if (age > 6.0f)
|
||||
continue; // expired
|
||||
gBTHudThreats[n] = gBTHudThreats[i]; // compact in place
|
||||
if (n < max_n)
|
||||
{
|
||||
out_xz[n][0] = gBTHudThreats[n].x;
|
||||
out_xz[n][1] = gBTHudThreats[n].z;
|
||||
out_age[n] = age;
|
||||
}
|
||||
++n;
|
||||
}
|
||||
gBTHudThreatCount = n;
|
||||
return (n < max_n) ? n : max_n;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// BTReticleRenderable::AddWeapon
|
||||
@@ -1373,12 +1585,12 @@ void
|
||||
Scalar pip_red, // param_6..8 PipColor
|
||||
Scalar pip_green,
|
||||
Scalar pip_blue,
|
||||
int *alarm_value, // param_9 weapon attr 0x1c
|
||||
int const2, // param_10 (constant 2)
|
||||
int const3, // param_11 (constant 3)
|
||||
Scalar *cycle_ready, // param_9 (port: rechargeLevel)
|
||||
int const2, // param_10 (2 = loaded)
|
||||
int const3, // param_11 (3 = charging)
|
||||
int *sim_state_value, // param_12 weapon attr 1
|
||||
int const1, // param_13 (constant 1)
|
||||
int weapon_mode) // param_14 (1 front / 2 rear)
|
||||
int const1, // param_13 (1 = destroyed)
|
||||
int weapon_mode) // param_14 (group bit)
|
||||
{
|
||||
if (this->weaponCount /* [0x38] */ >= 10)
|
||||
{
|
||||
@@ -1389,16 +1601,17 @@ void
|
||||
|
||||
//
|
||||
// Record this weapon's control state in the parallel arrays -- the exact
|
||||
// store order of @004cdac0 (part_014.c:4827-4837). The binary read the
|
||||
// two attr objects' values at +0x14; the port takes value pointers direct.
|
||||
// store order of @004cdac0 (part_014.c:4827-4837). The caches hold the
|
||||
// pip's DERIVED display flags (loaded / destroyed) for the Execute-style
|
||||
// change detection in Draw.
|
||||
//
|
||||
this->stateConst3[n] /* [0x64+n*4] = param_11 */ = const3;
|
||||
this->stateConst2[n] /* [0x8c+n*4] = param_10 */ = const2;
|
||||
this->stateConst1[n] /* [0xb4+n*4] = param_13 */ = const1;
|
||||
this->alarmAttr[n] /* [0x130+n*4] = param_9 */ = alarm_value;
|
||||
this->alarmCache[n] /* [0xdc+n*4] = *(param_9+0x14) */ = *alarm_value;
|
||||
this->cycleReady[n] /* [0x130+n*4] = param_9 */ = cycle_ready;
|
||||
this->alarmCache[n] /* [0xdc+n*4] */ = (*cycle_ready >= 0.999f);
|
||||
this->simStateAttr[n] /* [0x158+n*4] = param_12 */ = sim_state_value;
|
||||
this->simStateCache[n] /* [0x104+n*4] = *(param_12+0x14) */ = *sim_state_value;
|
||||
this->simStateCache[n] /* [0x104+n*4] */ = (*sim_state_value == const1);
|
||||
this->withinRangePtr[n] /* [0x18c+n*4] = param_4 */ = within_range_value;
|
||||
this->withinRangeCache[n] /* [0x1b4+n*4] = *param_4 */ = *within_range_value;
|
||||
this->weaponMode[n] /* [0x3c+n*4] = param_14 */ = weapon_mode;
|
||||
|
||||
@@ -482,12 +482,15 @@ class BTReticleRenderable:
|
||||
|
||||
//
|
||||
// Append a weapon range/pip marker to the reticle. @004cdac0
|
||||
// Parameter order/count is the binary's; the two attr-object args
|
||||
// (param_9 / param_12 -- weapon attrs 0x1c and 1, BOTH named
|
||||
// "SimulationState" per the caller's abort strings @51d526/51d577)
|
||||
// are adapted to direct value pointers (the binary read the value at
|
||||
// attr+0x14; we have no 1995 attr-object layout -- databinding trap).
|
||||
// The caller passes the constants 2 / 3 / 1 verbatim.
|
||||
// Parameter order/count is the binary's. The RECOVERED Execute
|
||||
// (@004cdcf0, task #37) fixed the two state attrs' semantics:
|
||||
// param_9 = weapon attr 0x1c, the FIRE-CYCLE state: == const2 (2)
|
||||
// -> LOADED (lit pip A); == const3 (3) -> charging (dark
|
||||
// ring B). Port source: rechargeLevel (>= 1.0 == loaded).
|
||||
// param_12 = weapon attr 1, the DAMAGE state: == const1 (1)
|
||||
// -> Destroyed (the pip is HIDDEN entirely).
|
||||
// weapon_mode is a GROUP BIT (Front=1/Rear=2/Left=4/Right=8) tested
|
||||
// against the Reticle element mask's low nibble.
|
||||
//
|
||||
void
|
||||
AddWeapon(
|
||||
@@ -498,31 +501,32 @@ class BTReticleRenderable:
|
||||
Scalar pip_red, // param_6..8 PipColor
|
||||
Scalar pip_green,
|
||||
Scalar pip_blue,
|
||||
int *alarm_value, // param_9 weapon attr 0x1c
|
||||
int const2, // param_10 (constant 2)
|
||||
int const3, // param_11 (constant 3)
|
||||
Scalar *cycle_ready, // param_9 (port: rechargeLevel)
|
||||
int const2, // param_10 (2 = loaded)
|
||||
int const3, // param_11 (3 = charging)
|
||||
int *sim_state_value, // param_12 weapon attr 1
|
||||
int const1, // param_13 (constant 1)
|
||||
int weapon_mode); // param_14 (1 front / 2 rear)
|
||||
int const1, // param_13 (1 = destroyed)
|
||||
int weapon_mode); // param_14 (group bit)
|
||||
|
||||
protected:
|
||||
//
|
||||
// Per-weapon parallel arrays (cap 10). Slot mapping is the verified
|
||||
// store order of AddWeapon @004cdac0 (part_014.c:4827-4837) [T1]; the
|
||||
// three caller constants and the cached attr values are consumed by the
|
||||
// UN-EXPORTED Execute @004cdcf0, so their semantics are [T4].
|
||||
// store order of AddWeapon @004cdac0 (part_014.c:4827-4837) [T1];
|
||||
// semantics RECOVERED from the disassembled Execute @004cdcf0 (task
|
||||
// #37 [T1]): the caches are the change-detect state driving the pip
|
||||
// list rebuild; the constants are the fire-cycle/damage enum values.
|
||||
//
|
||||
int weaponCount; // +0x38
|
||||
int weaponMode[10]; // +0x3c param_14
|
||||
int stateConst3[10]; // +0x64 param_11 (3)
|
||||
int stateConst2[10]; // +0x8c param_10 (2)
|
||||
int stateConst1[10]; // +0xb4 param_13 (1)
|
||||
int alarmCache[10]; // +0xdc *param_9 at add time
|
||||
int simStateCache[10]; // +0x104 *param_12 at add time
|
||||
int *alarmAttr[10]; // +0x130 param_9
|
||||
int *simStateAttr[10]; // +0x158 param_12
|
||||
int *withinRangePtr[10]; // +0x18c param_4
|
||||
int withinRangeCache[10]; // +0x1b4 *param_4 at add time
|
||||
int weaponMode[10]; // +0x3c group bit (F/R/L/R)
|
||||
int stateConst3[10]; // +0x64 3 = charging (dark)
|
||||
int stateConst2[10]; // +0x8c 2 = loaded (lit)
|
||||
int stateConst1[10]; // +0xb4 1 = destroyed (hidden)
|
||||
int alarmCache[10]; // +0xdc port: LOADED flag cache
|
||||
int simStateCache[10]; // +0x104 port: DESTROYED flag cache
|
||||
Scalar *cycleReady[10]; // +0x130 port: rechargeLevel source
|
||||
int *simStateAttr[10]; // +0x158 damage state (attr 1)
|
||||
int *withinRangePtr[10]; // +0x18c param_4 (stored; Execute
|
||||
int withinRangeCache[10]; // +0x1b4 never reads it -- T1)
|
||||
dpl2d_DISPLAY *pipDisplayListB[10]; // +0x288
|
||||
dpl2d_DISPLAY *pipDisplayListA[10]; // +0x2b0
|
||||
|
||||
@@ -551,11 +555,16 @@ class BTReticleRenderable:
|
||||
dpl2d_DISPLAY *leftArrow; // [0x9f] left range arrow
|
||||
dpl2d_DISPLAY *rightArrow; // [0xa0] right range arrow
|
||||
dpl2d_DISPLAY *crossList; // [0xa1] the dotted cross
|
||||
dpl2d_DISPLAY *subB6; // [0xb6..0xba] aux sub-lists
|
||||
dpl2d_DISPLAY *subB7;
|
||||
dpl2d_DISPLAY *subB8; // [0xb8] green centre ring
|
||||
dpl2d_DISPLAY *subB9; // [0xb9] green ring + cross
|
||||
dpl2d_DISPLAY *subBA;
|
||||
dpl2d_DISPLAY *subB6; // [0xb6] the composed weapon pips
|
||||
dpl2d_DISPLAY *subB7; // [0xb7] the lock-ring slot
|
||||
dpl2d_DISPLAY *subB8; // [0xb8] plain ring (simple-X mode)
|
||||
dpl2d_DISPLAY *subB9; // [0xb9] ring + cross (the LOCK ring)
|
||||
dpl2d_DISPLAY *subBA; // [0xba] the threat-direction trail
|
||||
|
||||
// Recovered-Execute dynamic state (task #37)
|
||||
int pipsBuilt; // subB6 composed at least once
|
||||
int lockShown; // subB7 holds the ring (this[0x1dc])
|
||||
float lockSpinDeg; // the ring spin angle (this[0x240])
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
@@ -109,13 +109,14 @@
|
||||
// id 13 "CompassHeading" -> @0x208 (Vec3)
|
||||
// (FlickerRate name ptr @005110bc heads the table as the category label.)
|
||||
//
|
||||
// UNCERTAIN: several of these attribute name<->offset bindings do not line
|
||||
// up cleanly with the way the ctor/HudSimulation actually use the same
|
||||
// offsets (e.g. the ctor seeds @0x1D8 from the resource FlickerRate, while
|
||||
// HudSimulation treats @0x1EC as the computed range-to-target and @0x1F8 as
|
||||
// a boolean visibility flag). The names below follow usage where it is
|
||||
// unambiguous and are flagged where it is not. A human should reconcile
|
||||
// the gauge attribute map against the original HUD.HPP.
|
||||
// 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 {
|
||||
|
||||
@@ -554,6 +554,19 @@ void
|
||||
// WRITTEN -- a reconstruction gap; the natural authentic write site is
|
||||
// this handler (every damage message carries the inflictor). [T3]
|
||||
lastInflictingID = message->inflictingEntity;
|
||||
|
||||
// THREAT feed (task #37, the recovered reticle Execute's ThreatVector
|
||||
// attr 0xC): when the PLAYER takes a hit, push the attack direction --
|
||||
// world (x,z) from the mech toward the impact point -- onto the HUD's
|
||||
// compass-rose threat trail (fresh marks red, fading over 6s).
|
||||
if (application != 0 && (Entity *)this == application->GetViewpointEntity())
|
||||
{
|
||||
extern void BTPushHudThreat(float wx, float wz);
|
||||
BTPushHudThreat(
|
||||
(float)(message->damageData.impactPoint.x - localOrigin.linearPosition.x),
|
||||
(float)(message->damageData.impactPoint.z - localOrigin.linearPosition.z));
|
||||
}
|
||||
|
||||
DamageLookupTable *table = (DamageLookupTable *)damageLookupTable; // named member (Wword absorbs!)
|
||||
if (message->invalidDamageZone && table != 0)
|
||||
{
|
||||
|
||||
@@ -551,11 +551,18 @@ static Scalar gDriveHeading = 0.0f; // yaw about world up (Y)
|
||||
// the HUD Draw (crosshair position) and the pick-ray each sim frame.
|
||||
float gBTAimX = 0.0f;
|
||||
float gBTAimY = 0.0f;
|
||||
// The HUD designator feed (mech4 -> btl4vid Draw): the designated target's
|
||||
// world chest point + the lock state (0 = none, 1 = designated, 2 = HOT --
|
||||
// the crosshair is ON it right now).
|
||||
// The HUD designator feed (mech4 -> btl4vid Draw): the locked target's
|
||||
// HOTBOX point (its top-centre; the recovered Execute frames x+-4 around it,
|
||||
// +1 above / -11.5 below) + the lock state (0 = none, 2 = locked).
|
||||
int gBTHudLockState = 0;
|
||||
float gBTHudLockWorld[3] = { 0, 0, 0 };
|
||||
// Recovered-Execute instrument feeds (task #37): the compass heading, the
|
||||
// torso-twist tape (deflection over the per-mech twist limit), and the
|
||||
// weapon-group display mask (the Reticle element mask's low nibble).
|
||||
float gBTHudHeading = 0.0f;
|
||||
float gBTHudTwist = 0.0f;
|
||||
float gBTHudTwistLimit = 0.0f;
|
||||
int gBTHudGroupMask = 0xF;
|
||||
// BT_GOTO beeline harness outputs (consumed by the mapper bridge, mechmppr.cpp)
|
||||
int gBTGotoActive = 0;
|
||||
float gBTGotoTurn = 0.0f;
|
||||
@@ -2364,19 +2371,21 @@ void
|
||||
}
|
||||
}
|
||||
|
||||
// HUD feeds: the range caret + the designator box (world point + state).
|
||||
// HUD feeds: the range caret + the hotbox (world point + state) + the
|
||||
// recovered-Execute instruments (compass, twist tape, group mask).
|
||||
{
|
||||
extern void BTSetHudTargetRange(Scalar range);
|
||||
Entity *des = MECH_TARGET_ENTITY(this);
|
||||
if (des != 0)
|
||||
{
|
||||
Point3D dp = ((Mech *)des)->localOrigin.linearPosition;
|
||||
Mech *dm = (Mech *)des;
|
||||
Point3D dp = dm->localOrigin.linearPosition;
|
||||
float hddx = dp.x - localOrigin.linearPosition.x;
|
||||
float hddz = dp.z - localOrigin.linearPosition.z;
|
||||
BTSetHudTargetRange((Scalar)sqrtf(hddx*hddx + hddz*hddz));
|
||||
gBTHudLockState = 2; // locked == crosshair on it
|
||||
gBTHudLockWorld[0] = dp.x;
|
||||
gBTHudLockWorld[1] = dp.y + kMuzzleHeight;
|
||||
gBTHudLockWorld[0] = dp.x; // the HOTBOX point: top-centre
|
||||
gBTHudLockWorld[1] = dp.y + (float)dm->CylinderReferenceHeight();
|
||||
gBTHudLockWorld[2] = dp.z;
|
||||
}
|
||||
else
|
||||
@@ -2384,6 +2393,11 @@ void
|
||||
BTSetHudTargetRange(1200.0f); // no target: the binary default
|
||||
gBTHudLockState = 0; // (0x44960000 @part_013.c:5637)
|
||||
}
|
||||
|
||||
gBTHudHeading = gDriveHeading; // CompassHeading (attr 0xD)
|
||||
gBTHudTwist = (float)TorsoHeading(); // RotationOfTorsoHorizontal (attr 4)
|
||||
gBTHudTwistLimit = (float)GetHorizontalFiringReach();// HorizontalTorsoLimit (attrs 5/6)
|
||||
gBTHudGroupMask = (int)targetReticle.reticleElementMask & 0xF;
|
||||
}
|
||||
|
||||
if (gEnemyMech != 0)
|
||||
|
||||
@@ -251,7 +251,8 @@ class CockpitHud;
|
||||
int PipExtendedRange() const { return pipExtendedRange; }
|
||||
void *WithinRangePtr() { return &targetWithinRange; }
|
||||
void *WeaponAlarmPtr() { return &weaponAlarm; }
|
||||
void *SimulationStatePtr() { return &simulationState; } // attr 1 / 0x1c ("SimulationState")
|
||||
void *SimulationStatePtr() { return &simulationState; } // attr 1 ("SimulationState" -- damage state; 1 = Destroyed)
|
||||
Scalar *RechargeLevelPtr() { return &rechargeLevel; } // the pip ready source (1.0 == loaded/ready)
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user