HUD reticle + weapon pips LIVE: dpl2d 2D display-list port (task #35)

- dpl2d API fully recovered from the binary recorders (@487f34-488630):
  opcode model (points/lines/polyline/circle/color/width/matrix/push-pop),
  CallList = INLINE include (state persists to caller), centered coordinate
  frame (unit = half viewport height). game/reconstructed/dpl2d.cpp rework.
- BTReticleRenderable ctor @004cc40c transcribed with the authentic
  calibration (originX .35, originY .25, scaleY .5, 0..1200m right range
  ladder, bottom heading tape, FUN_004cd938 tick ladders, lock rings,
  turn arrows); range caret slides from the live target range fed by the
  mech4 targeting step (BTSetHudTargetRange).
- Weapon pips: the binary gate is IsDerivedFrom(0x511830 =
  MechWeapon::ClassDerivations) [T1: part_014.c:5386 hard-aborts on missing
  weapon attrs; part_012 counts + roster ORs capabilityFlags@+0x334] so ALL
  7 BLH weapons register (3 lasers + 2 PPCs + 2 MissileLaunchers). Pip A
  (lit, authored PipColor) on TargetWithinRange, else dark ring B.
- AddWeapon @004cdac0 store map corrected to the verified order
  (part_014.c:4827-4837); both state attrs are literally named
  "SimulationState" (strings @51d526/51d577) -> weapon simulationState.
- Mech roster this[0x1ef] renamed poweredSubsystems -> weaponRoster
  (0x511830 is MechWeapon, not PoweredSubsystem=0x50f4bc); derivation-tag
  table added to context/decomp-reference.md.
- Draw hook BTDrawReticle after the 3D scene, cockpit view only. Binary
  Execute @004cdcf0 is an un-exported gap -> Draw dynamics [T3], tracked
  in context/open-questions.md with the blx_cop canopy + PNAME pip meshes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-08 20:05:44 -05:00
co-authored by Claude Fable 5
parent 1cd8ca1a80
commit 48b17750e5
15 changed files with 593 additions and 109 deletions
+390 -24
View File
@@ -35,6 +35,9 @@
//
#include <bt.hpp>
#if !defined(EMITTER_HPP)
# include <emitter.hpp> // Emitter/PPC (the reticle's per-weapon pip wiring)
#endif
#pragma hdrstop
#if !defined(BTL4VID_HPP)
@@ -525,6 +528,20 @@ HierarchicalDrawComponent*
SwapToWreck(entity);
}
//
// The TARGETING RETICLE (the main-view HUD): built for the player's mech,
// per the 1996 inside-view path (@part_014.c:5127-5158 constructs the
// 0x358 BTReticleRenderable, then :5390-5436 registers one pip per weapon
// from its TargetWithinRange / WeaponRange / PipPosition / PipColor /
// PipExtendedRange / SimulationState attributes). Drawn by BTDrawReticle
// in the cockpit view only.
//
if (buildDebugChaseCamera)
{
extern BTReticleRenderable *BTBuildReticle(Entity *mech);
BTBuildReticle(entity);
}
// DEV: BT_START_INSIDE=1 begins in the cockpit view (also exercises the
// inside-skeleton swap headlessly).
if (buildDebugChaseCamera && getenv("BT_START_INSIDE"))
@@ -937,6 +954,347 @@ void BTSwapMechToWreck(Entity *victim)
}
//
//#############################################################################
// BTReticleRenderable -- ctor (@004cc40c) + Draw (Execute @004cdcf0 is in an
// un-exported gap; the draw dynamics here are [T3], the GLYPHS are [T1])
//#############################################################################
//
// The live reticle instance (the player's; drawn by BTDrawReticle in the
// cockpit view only).
//
static BTReticleRenderable *gBTReticle = 0;
extern Scalar gBTHudRangeStorage; // live target range (defined below)
static int gBTHudInside = 0; // cockpit view live (set by SetViewInside)
// the dpl2d rasteriser (dpl2d.cpp; device type opaque here)
extern void dpl2d_ExecuteList(dpl2d_DISPLAY *list, struct IDirect3DDevice9 *device);
void BTSetHudTargetRange(Scalar range) { gBTHudRangeStorage = range; }
void BTSetHudInside(int inside) { gBTHudInside = inside; }
//
// FUN_004cd938 -- the tick-LADDER builder: `count` ticks stepped along an axis
// (dir 0/1 = +x/-x travel with vertical ticks, 2/3 = +y/-y with horizontal
// ticks), a MAJOR tick (halfMajor) every (majorEvery+1)th, minor otherwise.
//
static void
BTReticleTickLadder(dpl2d_DISPLAY *list, int count, int majorEvery,
float span, float x0, float y0, int dir, float halfMinor, float halfMajor)
{
float step = span / (float)(count - 1);
float tickMinX = 0, tickMinY = 0, tickMajX = 0, tickMajY = 0;
float stepX = 0, stepY = 0;
switch (dir)
{
case 0: stepX = step; tickMajY = halfMajor; tickMinY = halfMinor; break;
case 1: stepX = -step; tickMajY = halfMajor; tickMinY = halfMinor; break;
case 2: stepY = step; tickMajX = halfMajor; tickMinX = halfMinor; break;
case 3: stepY = -step; tickMajX = halfMajor; tickMinX = halfMinor; break;
}
float x = x0, y = y0;
int untilMajor = 0;
dpl2d_OpenLines(list);
for (int i = 0; i < count; ++i)
{
if (untilMajor == 0)
{
dpl2d_AddPoint(list, x + tickMajX, y + tickMajY);
dpl2d_AddPoint(list, x - tickMajX, y - tickMajY);
untilMajor = majorEvery;
}
else
{
dpl2d_AddPoint(list, x + tickMinX, y + tickMinY);
dpl2d_AddPoint(list, x - tickMinX, y - tickMinY);
--untilMajor;
}
x += stepX; y += stepY;
}
dpl2d_CloseLines(list);
}
//
// The authentic calibration constants (the binary ctor's own values).
//
static const float kRetOriginX = 0.35f; // [0x7f] right range-ladder x
static const float kRetOriginY = 0.25f; // [0x80] ladder bottom y
static const float kRetScaleY = 0.5f; // [0x81] ladder span
static const float kRetTickMinor = 0.008f; // [0x83]
static const float kRetTickMajor = 0.016f; // [0x82]
static const float kRetBotX = -0.25f; // [0x84] bottom heading-ladder x0
static const float kRetBotY = 0.35f; // [0x85] heading ladder y
static const float kRetBotSpan = 0.5f; // [0x86]
static const float kRetCaret = 0.02f; // _DAT_004cd7f4 (caret triangle size) [T3]
static const int kRetTicksR = 13; // [9] right ladder tick count
static const int kRetTicksB = 21; // [0xb] bottom ladder tick count
static const float kRetMaxRange = 1200.0f; // ctor param 11 (0x44960000)
BTReticleRenderable::BTReticleRenderable(Entity *entity, Scalar *range_attr)
: VideoRenderable(entity, VideoRenderable::Dynamic)
{
weaponCount = 0;
originX = kRetOriginX; originY = kRetOriginY;
scaleY = kRetScaleY; biasX = kRetTickMajor;
maxRange = kRetMaxRange; minRange = 0.0f;
rangeScale= maxRange - minRange; // [0x8d] -- AddWeapon divides by it
rangeAttr2= range_attr;
masterList = dpl2d_NewDisplayList();
aimDotList = dpl2d_NewDisplayList();
rangeCaretR = dpl2d_NewDisplayList();
rangeCaretB = dpl2d_NewDisplayList();
headingList = dpl2d_NewDisplayList();
bottomAnchor = dpl2d_NewDisplayList();
leftArrow = dpl2d_NewDisplayList();
rightArrow = dpl2d_NewDisplayList();
crossList = dpl2d_NewDisplayList();
subB6 = dpl2d_NewDisplayList();
subB7 = dpl2d_NewDisplayList();
subB8 = dpl2d_NewDisplayList();
subB9 = dpl2d_NewDisplayList();
subBA = dpl2d_NewDisplayList();
//
// The MASTER list (@4511-4601, faithfully transcribed). Colours: green
// 0.75 for the frame, yellow for the range caret; widths 1/2/3.
//
dpl2d_DISPLAY *m = masterList;
dpl2d_Begin(m, 1);
dpl2d_SetLineWidth(m, 1.0f);
dpl2d_FullScreenClip(m);
dpl2d_SetColor(m, 0.75f, 0.0f, 0.0f);
dpl2d_CallList(m, crossList); // [0xa1] target-box slot (empty until lock)
dpl2d_SetColor(m, 0.0f, 0.75f, 0.0f);
dpl2d_CallList(m, aimDotList); // [0x9a] the aim translate
dpl2d_OpenPolypoint(m); // centre dot
dpl2d_AddPoint(m, 0.0f, 0.0f);
dpl2d_ClosePolypoint(m);
dpl2d_OpenLines(m); // inner cross (gap at centre)
dpl2d_AddPoint(m, 0.04f, 0.0f); dpl2d_AddPoint(m, 0.10f, 0.0f);
dpl2d_AddPoint(m, -0.04f, 0.0f); dpl2d_AddPoint(m, -0.10f, 0.0f);
dpl2d_AddPoint(m, 0.0f, 0.04f); dpl2d_AddPoint(m, 0.0f, 0.10f);
dpl2d_AddPoint(m, 0.0f, -0.04f); dpl2d_AddPoint(m, 0.0f, -0.10f);
dpl2d_CloseLines(m);
dpl2d_SetLineWidth(m, 3.0f);
dpl2d_OpenLines(m); // heavy outer cross
dpl2d_AddPoint(m, 0.10f, 0.0f); dpl2d_AddPoint(m, 0.16f, 0.0f);
dpl2d_AddPoint(m, -0.10f, 0.0f); dpl2d_AddPoint(m, -0.16f, 0.0f);
dpl2d_AddPoint(m, 0.0f, 0.10f); dpl2d_AddPoint(m, 0.0f, 0.16f);
dpl2d_AddPoint(m, 0.0f, -0.10f); dpl2d_AddPoint(m, 0.0f, -0.16f);
dpl2d_CloseLines(m);
dpl2d_SetLineWidth(m, 1.0f);
// the RIGHT range ladder (13 ticks up the right side, dir 3 = -y travel)
BTReticleTickLadder(m, kRetTicksR, 1, kRetScaleY,
kRetOriginX, kRetOriginY, 3, kRetTickMinor, kRetTickMajor);
// the range CARET (yellow triangle; its called list holds the live
// translate the Draw rebuilds each frame from the target range)
dpl2d_PushState(m);
dpl2d_SetLineWidth(m, 2.0f);
dpl2d_SetColor(m, 0.75f, 0.75f, 0.0f);
dpl2d_CallList(m, rangeCaretR);
dpl2d_OpenPolyline(m);
dpl2d_AddPoint(m, kRetOriginX - kRetTickMajor, kRetOriginY);
dpl2d_AddPoint(m, kRetOriginX - kRetCaret - kRetTickMajor, kRetOriginY + kRetCaret);
dpl2d_AddPoint(m, kRetOriginX - kRetCaret - kRetTickMajor, kRetOriginY - kRetCaret);
dpl2d_ClosePolyline(m);
dpl2d_PopState(m);
// the BOTTOM heading ladder (21 ticks across, dir 0 = +x travel)
BTReticleTickLadder(m, kRetTicksB, 1, kRetBotSpan,
kRetBotX, kRetBotY, 0, kRetTickMinor, kRetTickMajor);
// the heading carets (over/under triangles at the ladder centre, shifted
// by the called heading translate)
{
float cx = kRetBotX + kRetBotSpan * 0.5f; // _DAT_004cd7f8 = 0.5 [T1]
dpl2d_PushState(m);
dpl2d_SetLineWidth(m, 2.0f);
dpl2d_SetColor(m, 0.75f, 0.75f, 0.0f);
dpl2d_CallList(m, rangeCaretB);
dpl2d_OpenPolyline(m);
dpl2d_AddPoint(m, cx, kRetBotY - kRetTickMajor);
dpl2d_AddPoint(m, cx + kRetCaret, kRetBotY - kRetCaret - kRetTickMajor);
dpl2d_AddPoint(m, cx - kRetCaret, kRetBotY - kRetCaret - kRetTickMajor);
dpl2d_ClosePolyline(m);
dpl2d_OpenPolyline(m);
dpl2d_AddPoint(m, cx, kRetBotY + kRetTickMajor);
dpl2d_AddPoint(m, cx + kRetCaret, kRetBotY + kRetCaret + kRetTickMajor);
dpl2d_AddPoint(m, cx - kRetCaret, kRetBotY + kRetCaret + kRetTickMajor);
dpl2d_ClosePolyline(m);
dpl2d_PopState(m);
}
// bottom-anchor group: a small arc + drop line under the centre
dpl2d_PushState(m);
dpl2d_CallList(m, bottomAnchor);
dpl2d_Circle(m, 0.0f, 0.0f, 0.03f, 0); // 0x3cf5c28f-family arc [T3 radius]
dpl2d_OpenLines(m);
dpl2d_AddPoint(m, 0.0f, -0.04f);
dpl2d_AddPoint(m, 0.0f, -0.005f);
dpl2d_CloseLines(m);
dpl2d_CallList(m, subBA);
dpl2d_PopState(m);
dpl2d_CallList(m, subB6);
dpl2d_CallList(m, headingList);
dpl2d_CallList(m, subB7);
dpl2d_End(m);
dpl2d_Compile(m);
//
// The green centre-ring sub-lists ([0xb8]/[0xb9]) -- the lock indicator
// rings the binary Execute swaps in on target state.
//
dpl2d_Begin(subB8, 1);
dpl2d_SetColor(subB8, 0.0f, 0.75f, 0.0f);
dpl2d_Circle(subB8, 0.0f, 0.0f, 0.12f, 0);
dpl2d_End(subB8); dpl2d_Compile(subB8);
dpl2d_Begin(subB9, 1);
dpl2d_SetColor(subB9, 0.0f, 0.75f, 0.0f);
dpl2d_Circle(subB9, 0.0f, 0.0f, 0.12f, 0);
dpl2d_OpenLines(subB9);
dpl2d_AddPoint(subB9, 0.14f, 0.0f); dpl2d_AddPoint(subB9, 0.10f, 0.0f);
dpl2d_AddPoint(subB9, -0.14f, 0.0f); dpl2d_AddPoint(subB9, -0.10f, 0.0f);
dpl2d_AddPoint(subB9, 0.0f, 0.14f); dpl2d_AddPoint(subB9, 0.0f, 0.10f);
dpl2d_AddPoint(subB9, 0.0f, -0.14f); dpl2d_AddPoint(subB9, 0.0f, -0.10f);
dpl2d_CloseLines(subB9);
dpl2d_End(subB9); dpl2d_Compile(subB9);
//
// The off-screen turn ARROWS ([0x9f]/[0xa0]) -- big width-12 chevrons at
// x = +-1.2..1.5 (screen edges), half-alpha; positioned/enabled by the
// un-exported Execute -> built but not drawn statically.
//
dpl2d_Begin(leftArrow, 1);
dpl2d_SetLineWidth(leftArrow, 12.0f);
dpl2d_OpenLines(leftArrow);
dpl2d_AddPoint(leftArrow, -1.2f, -0.30011f);
dpl2d_AddPoint(leftArrow, -1.5f, 0.0f);
dpl2d_AddPoint(leftArrow, -1.5f, 0.0f);
dpl2d_AddPoint(leftArrow, -1.2f, 0.30011f);
dpl2d_CloseLines(leftArrow);
dpl2d_SetLineWidth(leftArrow, 1.0f);
dpl2d_End(leftArrow); dpl2d_Compile(leftArrow);
dpl2d_Begin(rightArrow, 1);
dpl2d_SetLineWidth(rightArrow, 12.0f);
dpl2d_OpenLines(rightArrow);
dpl2d_AddPoint(rightArrow, 1.2f, 0.30011f);
dpl2d_AddPoint(rightArrow, 1.5f, 0.0f);
dpl2d_AddPoint(rightArrow, 1.5f, 0.0f);
dpl2d_AddPoint(rightArrow, 1.2f, -0.30011f);
dpl2d_CloseLines(rightArrow);
dpl2d_SetLineWidth(rightArrow, 1.0f);
dpl2d_End(rightArrow); dpl2d_Compile(rightArrow);
// empty placeholders (filled per frame / on lock)
dpl2d_Begin(crossList, 1); dpl2d_End(crossList); dpl2d_Compile(crossList);
dpl2d_Begin(aimDotList, 1); dpl2d_End(aimDotList); dpl2d_Compile(aimDotList);
dpl2d_Begin(rangeCaretR, 1); dpl2d_End(rangeCaretR); dpl2d_Compile(rangeCaretR);
dpl2d_Begin(rangeCaretB, 1); dpl2d_End(rangeCaretB); dpl2d_Compile(rangeCaretB);
dpl2d_Begin(headingList, 1); dpl2d_End(headingList); dpl2d_Compile(headingList);
dpl2d_Begin(bottomAnchor,1); dpl2d_End(bottomAnchor);dpl2d_Compile(bottomAnchor);
dpl2d_Begin(subB6, 1); dpl2d_End(subB6); dpl2d_Compile(subB6);
dpl2d_Begin(subB7, 1); dpl2d_End(subB7); dpl2d_Compile(subB7);
dpl2d_Begin(subBA, 1); dpl2d_End(subBA); dpl2d_Compile(subBA);
}
BTReticleRenderable::~BTReticleRenderable()
{
if (gBTReticle == this)
gBTReticle = 0;
}
//
// Per-frame draw. Rebuild the live translate lists (the range caret slides
// along its ladder with the target range -- the ctor's translate(0,
// -scaleY * rangeFraction) at @4608-4613), then draw the master, then each
// weapon's pip: the LIT pip (list A) while its within-range flag is up, the
// dark ring (list B) otherwise. [T3 dynamics / T1 geometry]
//
void
BTReticleRenderable::Draw(struct IDirect3DDevice9 *device)
{
// the range caret translate, from the live target range
Scalar range = (rangeAttr2 != 0) ? *rangeAttr2 : 0.0f;
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 };
dpl2d_Begin(rangeCaretR, 1);
dpl2d_ConcatMatrix(rangeCaretR, six);
dpl2d_End(rangeCaretR);
dpl2d_Compile(rangeCaretR);
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);
}
}
//
// The render-loop hook: draw the player's reticle over the finished 3D frame,
// COCKPIT VIEW ONLY (the 1996 build constructed it only for insideEntity).
//
void BTDrawReticle(struct IDirect3DDevice9 *device)
{
if (gBTReticle != 0 && gBTHudInside)
gBTReticle->Draw(device);
}
//
// Build the player's reticle + register one pip per weapon (the 1996 wiring:
// @part_014.c:5386-5436). The binary's gate is IsDerivedFrom(0x511830) --
// MechWeapon::ClassDerivations [T1: the loop hard-aborts on missing
// WeaponRange/PipPosition/... attrs, which only MechWeapons publish] -- so
// EVERY mounted weapon registers a pip: lasers, PPCs AND missile launchers.
// Per weapon it reads WeaponRange / PipPosition / TargetWithinRange /
// PipExtendedRange / PipColor / SimulationState (attrs 1 + 0x1c) / RearFiring;
// mode 1 = front (RearFiring==0), which every BLH weapon is.
//
BTReticleRenderable *BTBuildReticle(Entity *mech)
{
if (gBTReticle != 0)
return gBTReticle;
extern void BTSetHudTargetRange(Scalar range); // (self; range fed by mech4)
extern Scalar gBTHudRangeStorage; // defined below
gBTReticle = new BTReticleRenderable(mech, &gBTHudRangeStorage);
Mech *m = (Mech *)mech;
for (int wi = 0; wi < m->GetSubsystemCount(); ++wi)
{
Subsystem *ws = m->GetSubsystem(wi);
if (ws == 0)
continue;
if (!ws->IsDerivedFrom(MechWeapon::ClassDerivations)) // 0x511830
continue;
MechWeapon *wp = (MechWeapon *)ws;
RGBColor pc = wp->PipColor();
float r = (float)pc.Red, g = (float)pc.Green, b = (float)pc.Blue;
if (r < 0.0f || g < 0.0f || b < 0.0f) { r = 0.78f; g = 0.08f; b = 0.02f; }
gBTReticle->AddWeapon(
wp->WeaponRange(),
wp->PipPosition(),
(int *)wp->WithinRangePtr(),
wp->PipExtendedRange(),
r, g, b,
(int *)wp->SimulationStatePtr(), // attr 0x1c "SimulationState"
2, 3,
(int *)wp->SimulationStatePtr(), // attr 1 "SimulationState"
1,
1 /* front (RearFiring==0 on every BLH weapon) */);
}
DEBUG_STREAM << "[hud] reticle built: " << gBTReticle->WeaponCount()
<< " weapon pip(s) registered\n" << std::flush;
return gBTReticle;
}
// the live target-range storage the reticle's caret binds to
Scalar gBTHudRangeStorage = 0.0f;
//
//#############################################################################
// BTReticleRenderable::AddWeapon
@@ -951,19 +1309,19 @@ void BTSwapMechToWreck(Entity *victim)
//
void
BTReticleRenderable::AddWeapon(
Scalar weapon_range,
int pip_position,
StateIndicator *within_range_attr,
int extended_range,
Scalar pip_red,
Scalar pip_green,
Scalar pip_blue,
StateIndicator *sim_state,
int arg9, // constant 2 from caller
int arg10, // constant 3 from caller
StateIndicator *sim_state2,
int arg12, // constant 1 from caller
int weapon_mode) // 1 front / 2 rear
Scalar weapon_range, // param_2
int pip_position, // param_3
int *within_range_value, // param_4 TargetWithinRange
int extended_range, // param_5 PipExtendedRange
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)
int *sim_state_value, // param_12 weapon attr 1
int const1, // param_13 (constant 1)
int weapon_mode) // param_14 (1 front / 2 rear)
{
if (this->weaponCount /* [0x38] */ >= 10)
{
@@ -973,18 +1331,20 @@ void
int n = this->weaponCount;
//
// Record this weapon's control attributes in the parallel arrays.
// 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.
//
this->pipColor[n] /* [0x64+n*4] */ = pip_red;
this->withinRangeAttr[n] /* [0x8c+n*4] */ = within_range_attr;
this->simState[n] /* [0xb4+n*4] */ = sim_state;
this->simState2Ptr[n] /* [0x130+n*4] */ = sim_state2;
this->simState2Value[n] /* [0xdc+n*4] */ = *(int *)(sim_state2 + 0x14);
this->arg12Ptr[n] /* [0x158+n*4] */ = arg12;
this->arg12Value[n] /* [0x104+n*4] */ = *(int *)(arg12 + 0x14);
this->rangeAttr[n] /* [0x18c+n*4] */ = (Scalar *)within_range_attr; // param_4
this->rangeCache[n] /* [0x1b4+n*4] */ = *(Scalar *)within_range_attr;
this->weaponMode[n] /* [0x3c+n*4] */ = weapon_mode;
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->simStateAttr[n] /* [0x158+n*4] = param_12 */ = sim_state_value;
this->simStateCache[n] /* [0x104+n*4] = *(param_12+0x14) */ = *sim_state_value;
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;
dpl2d_DISPLAY *pip_list = dpl2d_NewDisplayList(); // FUN_00487f34
dpl2d_DISPLAY *arc_list = dpl2d_NewDisplayList();
@@ -1352,6 +1712,12 @@ void
DEBUG_STREAM << "[view] " << (inside ? "COCKPIT eyepoint" : "external chase")
<< (mCamera == mEyeCockpit ? " (cockpit live)" : " (chase live)")
<< "\n" << std::flush;
// the HUD overlay draws in the cockpit view only
{
extern void BTSetHudInside(int inside);
BTSetHudInside(mCamera == mEyeCockpit ? 1 : 0);
}
}
//