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:
co-authored by
Claude Fable 5
parent
1cd8ca1a80
commit
48b17750e5
@@ -77,6 +77,18 @@ Identify a class at runtime with `sub->GetClassID()` (== `sub+4`). [T1]
|
||||
- **Heat leaf:** `PoweredSubsystem : HeatSink : HeatableSubsystem : MechSubsystem` (Emitter/PPC/Sensor/Myomers/weapons chain here). [T1]
|
||||
- **Watcher branch:** `Torso/Gyroscope/Searchlight/ThermalSight/HUD : PowerWatcher : HeatWatcher : MechSubsystem`; `AmmoBin : HeatWatcher`. Uses a 0xC connection + 0x54 alarm (NOT the 4/8-byte heat-leaf types). [T1]
|
||||
|
||||
### Derivation-tag addresses (`FUN_0041a1a4(obj, tag)` = IsDerivedFrom)
|
||||
| Tag | Class | Evidence |
|
||||
|---|---|---|
|
||||
| **0x511830** | **MechWeapon**::ClassDerivations | The reticle AddWeapon loop (part_014.c:5386) hard-aborts on missing WeaponRange/PipPosition/… attrs for every member; part_012.c:9344 counts ready weapons; the Mech ctor roster at this[0x1ef] (`weaponRoster`, ex-mislabel "poweredSubsystems") ORs weapon `capabilityFlags`@+0x334. [T1] |
|
||||
| 0x50f4bc | PoweredSubsystem | btl4gau2 gauge wave. [T1] |
|
||||
| 0x50e3ec | HeatableSubsystem | btl4gaug widget gate. [T1] |
|
||||
| 0x50e604 | HeatSink | btl4gaug widget gate. [T1] |
|
||||
| 0x51155c | roster at Mech this[0x1eb] (`heatableSubsystems` label) | tag ≠ 0x50e3ec — the label is UNVERIFIED [T4]; arbitrate before relying on it. |
|
||||
| 0x50e4fc | roster at Mech this[499] (`damageableSubsystems` label) | [T3] |
|
||||
| 0x50bdb4 | Mech | emitter.cpp destroyed-check. [T1] |
|
||||
| 0x5121a8 | ProjectileWeapon-family (checked after 0x511830; reads ammo state @+0x364) | [T3] |
|
||||
|
||||
---
|
||||
|
||||
## 3. Mech struct offsets (`Mech*`, binary layout)
|
||||
|
||||
+18
-1
@@ -84,7 +84,24 @@ see [[open-questions]] `mech+0x190`. The **MessageBoard** feed needs **StatusMes
|
||||
stub) + the per-player status queue. `SeekVoltageGraph`'s 4 Seek* attrs are a cluster-child (not
|
||||
config-called). [T2]
|
||||
|
||||
## Cockpit HUD reticle (main screen, inside view) — LIVE
|
||||
`BTReticleRenderable` (0x358 bytes, ctor @004cc40c) draws over the finished 3D frame in cockpit
|
||||
view only, via the recovered **dpl2d** 2D display-list API (recorders @0x487f34-0x488630; opcode
|
||||
map + coordinate model in `phases/phase-02-dpl2d-reticle.md`; port: `game/reconstructed/dpl2d.cpp`).
|
||||
Geometry is the ctor's hardcoded calibration (originX 0.35, originY 0.25, scaleY 0.5, right range
|
||||
ladder 0..1200 m, bottom heading tape, center cross + dot; tick ladders via FUN_004cd938). The
|
||||
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]].
|
||||
|
||||
## Key Relationships
|
||||
- Full history: `docs/GAUGE_COMPOSITE.md`.
|
||||
- Full history: `docs/GAUGE_COMPOSITE.md`; reticle recovery: `phases/phase-02-dpl2d-reticle.md`.
|
||||
- Uses: [[attribute-pointer]] + [[reconstruction-gotchas]]; reads [[subsystems]] state.
|
||||
- Renders on: [[pod-hardware]] MFD surfaces.
|
||||
|
||||
@@ -51,6 +51,15 @@ authentic path scoped.
|
||||
these are MODE flags — in the basic test mission the inert Myomers / dormant valve is likely AUTHENTIC
|
||||
(advanced damage off); wiring makes them RESPOND when a mission enables the mode, not necessarily change
|
||||
the basic-mission behavior. The MessageBoard feed is separate (StatusMessagePool, below).
|
||||
- **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
|
||||
**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.
|
||||
- **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`
|
||||
|
||||
@@ -7539,6 +7539,13 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
|
||||
BTDrawPfx(mDevice, &viewTransform, (float)dT);
|
||||
}
|
||||
|
||||
// The BT targeting reticle / weapon pips (2D screen space, cockpit view
|
||||
// only -- the dpl2d layer; see game/reconstructed/dpl2d.cpp).
|
||||
{
|
||||
extern void BTDrawReticle(struct IDirect3DDevice9 *device);
|
||||
BTDrawReticle(mDevice);
|
||||
}
|
||||
|
||||
//
|
||||
// And don't forget particles too
|
||||
//
|
||||
|
||||
+390
-24
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -453,75 +453,117 @@ class BTReticleRenderable:
|
||||
public VideoRenderable
|
||||
{
|
||||
public:
|
||||
BTReticleRenderable( // @004cc40c
|
||||
//
|
||||
// PORT ctor. The binary ctor (@004cc40c, 15 args) received the mech's
|
||||
// TargetReticle attribute + 8 HUD-subsystem attributes; its CALIBRATION
|
||||
// however is its own hardcoded constants (0.35/0.25/0.5/0.016/0.008 --
|
||||
// the pip-row origin/scale and the two tick-ladder frames), which this
|
||||
// port ctor reproduces directly. The one live input the glyphs consume
|
||||
// is the target RANGE (drives the range-scale caret); it binds to a
|
||||
// Scalar the targeting step updates. The 3D marker chain + the
|
||||
// PNAME1-8.bgf pip meshes (the floating 3D target designator) are a
|
||||
// separate deferred piece -- see phases/phase-02-dpl2d-reticle.md.
|
||||
//
|
||||
BTReticleRenderable( // @004cc40c (port signature)
|
||||
Entity *entity,
|
||||
int execution_type,
|
||||
Reticle **my_reticle,
|
||||
dpl_VIEW *this_view,
|
||||
void *pip_position_template,
|
||||
void *pip_color_template,
|
||||
void *range_template,
|
||||
void *range_lo_template,
|
||||
void *range_hi_template,
|
||||
Scalar max_range,
|
||||
int flag,
|
||||
void *sim_template_a,
|
||||
void *sim_template_b,
|
||||
void *sim_template_c);
|
||||
Scalar *range_attr);
|
||||
~BTReticleRenderable();
|
||||
|
||||
//
|
||||
// Per-frame draw (the binary Execute @004cdcf0 is in an UN-EXPORTED
|
||||
// gap; the dynamics here are [T3]: draw the master reticle, then each
|
||||
// weapon's lit pip when its within-range flag is up, else the dark
|
||||
// ring). Called from the render loop, cockpit view only.
|
||||
//
|
||||
void
|
||||
Draw(struct IDirect3DDevice9 *device);
|
||||
|
||||
int WeaponCount() const { return weaponCount; }
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
void
|
||||
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,
|
||||
int arg10,
|
||||
StateIndicator *sim_state2,
|
||||
int arg12,
|
||||
int weapon_mode);
|
||||
|
||||
void
|
||||
Execute();
|
||||
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)
|
||||
|
||||
protected:
|
||||
//
|
||||
// Per-weapon parallel arrays (cap 10).
|
||||
// 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].
|
||||
//
|
||||
int weaponCount; // +0x38
|
||||
int weaponMode[10]; // +0x3c
|
||||
Scalar pipColor[10]; // +0x64
|
||||
StateIndicator *withinRangeAttr[10]; // +0x8c
|
||||
StateIndicator *simState[10]; // +0xb4
|
||||
int simState2Value[10]; // +0xdc
|
||||
int arg12Value[10]; // +0x104
|
||||
StateIndicator *simState2Ptr[10]; // +0x130
|
||||
int arg12Ptr[10]; // +0x158
|
||||
Scalar *rangeAttr[10]; // +0x18c
|
||||
Scalar rangeCache[10]; // +0x1b4
|
||||
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
|
||||
dpl2d_DISPLAY *pipDisplayListB[10]; // +0x288
|
||||
dpl2d_DISPLAY *pipDisplayListA[10]; // +0x2b0
|
||||
|
||||
//
|
||||
// Calibrated reticle geometry.
|
||||
// Calibrated reticle geometry (the ctor's authentic constants).
|
||||
//
|
||||
Scalar originX; // +0x1fc
|
||||
Scalar originY; // +0x200
|
||||
Scalar scaleY; // +0x204
|
||||
Scalar biasX; // +0x208
|
||||
Scalar maxRange; // +0x22c
|
||||
Scalar minRange; // +0x230
|
||||
Scalar rangeScale; // +0x234
|
||||
Scalar originX; // +0x1fc [0x7f] = 0.35
|
||||
Scalar originY; // +0x200 [0x80] = 0.25
|
||||
Scalar scaleY; // +0x204 [0x81] = 0.5
|
||||
Scalar biasX; // +0x208 [0x82] = 0.016
|
||||
Scalar maxRange; // +0x22c [0x8b] = 1200
|
||||
Scalar minRange; // +0x230 [0x8c] = 0
|
||||
Scalar rangeScale; // +0x234 [0x8f]
|
||||
|
||||
//
|
||||
// The reticle's own display lists (the ctor's [0x97..0xa1, 0xb6..0xba]
|
||||
// set, as transcribed) + the live range binding.
|
||||
//
|
||||
Scalar *rangeAttr2; // [0x89] the target-range source
|
||||
dpl2d_DISPLAY *masterList; // [0x98] the composed reticle
|
||||
dpl2d_DISPLAY *aimDotList; // [0x9a] centre aim dot
|
||||
dpl2d_DISPLAY *rangeCaretR; // [0x9b] right-ladder caret
|
||||
dpl2d_DISPLAY *rangeCaretB; // [0x9c] bottom-ladder carets
|
||||
dpl2d_DISPLAY *headingList; // [0x9d] heading sub-list
|
||||
dpl2d_DISPLAY *bottomAnchor; // [0x9e] bottom-ladder anchor
|
||||
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;
|
||||
};
|
||||
|
||||
//
|
||||
// The live reticle draw hook (called from the engine render loop after the
|
||||
// 3D scene; draws only in the cockpit view).
|
||||
//
|
||||
extern void BTDrawReticle(struct IDirect3DDevice9 *device);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// BTL4VideoRenderer
|
||||
|
||||
@@ -389,26 +389,8 @@ BTLightConnection::BTLightConnection(
|
||||
Entity *entity, int, dpl_INSTANCE *, int, int *)
|
||||
: BTRenderableBase(entity) {}
|
||||
|
||||
//===========================================================================//
|
||||
// BTReticleRenderable ctor/dtor/Execute (AddWeapon lives in btl4vid.cpp).
|
||||
//---------------------------------------------------------------------------//
|
||||
// TODO(bring-up): real ctor @004cc40c builds the reticle 2D display lists.
|
||||
//===========================================================================//
|
||||
BTReticleRenderable::BTReticleRenderable(
|
||||
Entity *entity, int, Reticle **, dpl_VIEW *,
|
||||
void *, void *, void *, void *, void *, Scalar, int,
|
||||
void *, void *, void *)
|
||||
: VideoRenderable(entity, VideoRenderable::Dynamic), weaponCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
BTReticleRenderable::~BTReticleRenderable()
|
||||
{
|
||||
}
|
||||
|
||||
void BTReticleRenderable::Execute()
|
||||
{
|
||||
}
|
||||
// (BTReticleRenderable ctor/dtor/Draw now live in btl4vid.cpp -- the real
|
||||
// @004cc40c reconstruction replaced the old bring-up stubs here.)
|
||||
|
||||
//===========================================================================//
|
||||
// VideoComponent (legacy renderable base) -- declared in L4VIDRND.h but NOT
|
||||
|
||||
@@ -341,6 +341,16 @@ namespace
|
||||
Mat2x3 mat;
|
||||
};
|
||||
|
||||
//
|
||||
// COORDINATE MODEL (from the reticle's authored constants): centred origin,
|
||||
// +x right, +y down, unit = half the viewport HEIGHT on both axes (keeps
|
||||
// circles round; the reticle content spans roughly +-0.5).
|
||||
//
|
||||
inline float MapX(float x, float ox, float vw, float vh)
|
||||
{ return ox + vw * 0.5f + x * (vh * 0.5f); }
|
||||
inline float MapY(float y, float oy, float vh)
|
||||
{ return oy + vh * 0.5f + y * (vh * 0.5f); }
|
||||
|
||||
void DrawVertexRun(IDirect3DDevice9 *device, const Command &cmd,
|
||||
const ExecState &st, float ox, float oy, float vw, float vh)
|
||||
{
|
||||
@@ -352,7 +362,7 @@ namespace
|
||||
{
|
||||
Vec2 p = st.mat.Apply(cmd.verts[i].x, cmd.verts[i].y);
|
||||
Vertex2D v;
|
||||
v.x = ox + p.x * vw; v.y = oy + p.y * vh;
|
||||
v.x = MapX(p.x, ox, vw, vh); v.y = MapY(p.y, oy, vh);
|
||||
v.z = 0.0f; v.rhw = 1.0f; v.color = st.color;
|
||||
verts.push_back(v);
|
||||
}
|
||||
@@ -389,11 +399,11 @@ namespace
|
||||
}
|
||||
|
||||
void ExecuteListInner(Dpl2dList *self, IDirect3DDevice9 *device,
|
||||
ExecState st, float ox, float oy, float vw, float vh, int depth)
|
||||
ExecState &st, std::vector<ExecState> &stack,
|
||||
float ox, float oy, float vw, float vh, int depth)
|
||||
{
|
||||
if (self == 0 || depth > 8)
|
||||
return;
|
||||
std::vector<ExecState> stack;
|
||||
Vertex2D ring[kCircleSegments + 2];
|
||||
|
||||
for (size_t i = 0; i < self->commands.size(); ++i)
|
||||
@@ -412,8 +422,12 @@ namespace
|
||||
st.mat = Mat2x3::Mul(cmd.mat, st.mat);
|
||||
break;
|
||||
case Command::kCallList:
|
||||
// INLINE include: a called list's state changes (matrices,
|
||||
// colours) PERSIST into the caller -- the reticle's range
|
||||
// caret is a called list holding just a translate that shifts
|
||||
// the master's subsequent geometry.
|
||||
ExecuteListInner((Dpl2dList *)cmd.callee, device,
|
||||
st, ox, oy, vw, vh, depth + 1);
|
||||
st, stack, ox, oy, vw, vh, depth + 1);
|
||||
break;
|
||||
case Command::kPoints:
|
||||
case Command::kLineStrip:
|
||||
@@ -424,9 +438,9 @@ namespace
|
||||
case Command::kCircleOutline:
|
||||
{
|
||||
Vec2 p = st.mat.Apply(cmd.a, cmd.b);
|
||||
const float cx = ox + p.x * vw;
|
||||
const float cy = oy + p.y * vh;
|
||||
const float r = cmd.c * vw; // width-relative -> stays round
|
||||
const float cx = MapX(p.x, ox, vw, vh);
|
||||
const float cy = MapY(p.y, oy, vh);
|
||||
const float r = cmd.c * (vh * 0.5f); // height-unit -> stays round
|
||||
if (cmd.kind == Command::kCircleFill)
|
||||
{
|
||||
ring[0].x = cx; ring[0].y = cy; ring[0].z = 0.0f;
|
||||
@@ -497,8 +511,9 @@ void dpl2d_ExecuteList(dpl2d_DISPLAY *list, IDirect3DDevice9 *device)
|
||||
st.color = 0xFFFFFFFF;
|
||||
st.width = 1.0f;
|
||||
st.mat.Identity();
|
||||
std::vector<ExecState> stack;
|
||||
ExecuteListInner(self, device,
|
||||
st, (float)vp.X, (float)vp.Y, (float)vp.Width, (float)vp.Height, 0);
|
||||
st, stack, (float)vp.X, (float)vp.Y, (float)vp.Width, (float)vp.Height, 0);
|
||||
|
||||
device->SetTexture(0, oldTex);
|
||||
if (oldTex) oldTex->Release();
|
||||
|
||||
@@ -678,7 +678,7 @@ Mech::Mech(
|
||||
|
||||
Wword(0x1e8) = Wword(0x1e9) = Wword(0x1ea) = 0;
|
||||
heatableSubsystems.Construct(0); // FUN_004a4dab(this+0x1eb)
|
||||
poweredSubsystems.Construct(0); // FUN_004a4df6(this+0x1ef)
|
||||
weaponRoster.Construct(0); // FUN_004a4df6(this+0x1ef)
|
||||
damageableSubsystems.Construct(0); // FUN_004a4e41(this+499)
|
||||
|
||||
for (int i = 0; i < 5; ++i) // FUN_0043ad4f x5
|
||||
@@ -1033,12 +1033,12 @@ Mech::Mech(
|
||||
heatableSubsystems.Add((Subsystem *)s);
|
||||
}
|
||||
}
|
||||
for (int id = 2; id < subsystemCount; ++id) // powered roster
|
||||
for (int id = 2; id < subsystemCount; ++id) // weapon roster (0x511830 = MechWeapon)
|
||||
{
|
||||
SubProxy *s = (SubProxy *)subsystemArray[id];
|
||||
if (s != 0 && s->IsDerivedFrom(0x511830))
|
||||
{
|
||||
poweredSubsystems.Add((Subsystem *)s);
|
||||
weaponRoster.Add((Subsystem *)s);
|
||||
stateFlags |= s->capabilityFlags; // |= *(s+0x334) -> Wword(0x104)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +408,12 @@ struct ShotDescriptor
|
||||
ReconChain<Subsystem*> controllableSubsystems; // @0x65c this[0x197] (FUN_00427768)
|
||||
ReconChain<Subsystem*> watchedSubsystems; // @0x6bc this[0x1af] (FUN_00427768)
|
||||
ReconChain<Subsystem*> heatableSubsystems; // @0x7ac this[0x1eb] (class 0x51155c)
|
||||
ReconChain<Subsystem*> poweredSubsystems; // @0x7bc this[0x1ef] (class 0x511830)
|
||||
ReconChain<Subsystem*> weaponRoster; // @0x7bc this[0x1ef] (class 0x511830 =
|
||||
// MechWeapon::ClassDerivations -- the
|
||||
// reticle AddWeapon loop @part_014.c:5386
|
||||
// asserts weapon attrs on every member.
|
||||
// Was mislabeled "poweredSubsystems";
|
||||
// PoweredSubsystem is 0x50f4bc.)
|
||||
ReconChain<Subsystem*> damageableSubsystems; // @0x7cc this[499] (class 0x50e4fc)
|
||||
|
||||
int weaponCount; // @0x448 this[0x112]
|
||||
|
||||
@@ -2331,6 +2331,14 @@ void
|
||||
MECH_TARGET_ENTITY(this) = gEnemyMech; // mech+0x388 (HasActiveTarget gate)
|
||||
MECH_TARGET_SUBIDX(this) = -1; // mech+0x38c whole-mech target
|
||||
|
||||
// feed the HUD's range caret (the reticle's right-ladder pointer)
|
||||
{
|
||||
extern void BTSetHudTargetRange(Scalar range);
|
||||
float hddx = enemyPos.x - localOrigin.linearPosition.x;
|
||||
float hddz = enemyPos.z - localOrigin.linearPosition.z;
|
||||
BTSetHudTargetRange((Scalar)sqrtf(hddx*hddx + hddz*hddz));
|
||||
}
|
||||
|
||||
float ddx = enemyPos.x - localOrigin.linearPosition.x;
|
||||
float ddy = enemyPos.y - localOrigin.linearPosition.y;
|
||||
float ddz = enemyPos.z - localOrigin.linearPosition.z;
|
||||
|
||||
@@ -787,7 +787,7 @@ void
|
||||
{
|
||||
// TODO(look-eyepoint): the commit block below writes the eyepoint pose at
|
||||
// binary offsets whose Mech-member mapping is CONFLICTED (mech+0x360 is
|
||||
// claimed by mechName, +0x410 by stateFlags, +0x7bc by poweredSubsystems --
|
||||
// claimed by mechName, +0x410 by stateFlags, +0x7bc by weaponRoster --
|
||||
// and the old draft even routed them through the TORSO pointer). Arbitrate
|
||||
// those labels against the raw before enabling; the port has no eyepoint
|
||||
// consumer yet. Look-state SELECTION above stays live (this-only).
|
||||
|
||||
@@ -241,6 +241,18 @@ class CockpitHud;
|
||||
void MuzzlePoint(Point3D &p) { GetMuzzlePoint(p); }
|
||||
const RGBColor &PipColor() const { return pipColor; }
|
||||
|
||||
//
|
||||
// Reticle/HUD accessors (the pip registration wiring @part_014.c:5390:
|
||||
// per weapon the 1996 build reads TargetWithinRange / WeaponRange /
|
||||
// PipPosition / PipColor / PipExtendedRange / SimulationState).
|
||||
//
|
||||
Scalar WeaponRange() const { return weaponRange; }
|
||||
int PipPosition() const { return pipPosition; }
|
||||
int PipExtendedRange() const { return pipExtendedRange; }
|
||||
void *WithinRangePtr() { return &targetWithinRange; }
|
||||
void *WeaponAlarmPtr() { return &weaponAlarm; }
|
||||
void *SimulationStatePtr() { return &simulationState; } // attr 1 / 0x1c ("SimulationState")
|
||||
|
||||
protected:
|
||||
|
||||
// @004b9864 -- build an aim orientation (atan2 of a direction vector).
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
# Phase 02 — dpl2d API recovery + BTReticleRenderable reconstruction (task #35)
|
||||
|
||||
**Goal:** the main-view HUD (targeting reticle + up to 10 per-weapon pips).
|
||||
**Status:** API fully recovered [T1]; ctor/Execute transcription + wiring IN PROGRESS.
|
||||
**Status:** COMPLETE (steps 1-5 live, verified 2026-07-08). dpl2d opcode model + CallList
|
||||
inline-include semantics in `game/reconstructed/dpl2d.cpp`; ctor glyph program + FUN_004cd938
|
||||
tick-ladder helper transcribed in `btl4vid.cpp`; per-weapon registration via the binary's real
|
||||
gate `IsDerivedFrom(0x511830 = MechWeapon::ClassDerivations)` (part_014.c:5386) — **7 pips** on
|
||||
the BLH (3 lasers + 2 PPCs + 2 MissileLaunchers), AddWeapon @004cdac0 store map verified
|
||||
(part_014.c:4827-4837; both state attrs are named "SimulationState", strings @51d526/51d577).
|
||||
Draw hook = `BTDrawReticle(device)` after the 3D scene, cockpit view only. The binary Execute
|
||||
@004cdcf0 is an UN-EXPORTED gap → Draw dynamics are best-effort [T3] (caret slide from live
|
||||
target range; pip A lit on TargetWithinRange else ring B). Step 6 (blx_cop canopy shell) and the
|
||||
PNAME1-8.bgf 3D pip meshes remain DEFERRED — tracked in context/open-questions.md.
|
||||
|
||||
## The recovered dpl2d recorder API (libDPL 2D display lists) [T1]
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ def bounds(path):
|
||||
% (path.split('\\')[-1], nverts, lo[0], hi[0], lo[1], hi[1], lo[2], hi[2]))
|
||||
|
||||
base = r'C:\git\bt411\content\VIDEO\GEO\%s'
|
||||
for f in ['ERMLASER.BGF', 'PPC.BGF', 'LLASER.BGF', 'SLASER.BGF', 'MLASER.BGF']:
|
||||
for f in ['BLX_COP.BGF']:
|
||||
try:
|
||||
bounds(base % f)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user