The MadCat torso twists, the view turns with it, and targeting follows. Three reconstruction fronts closed: THE ELECTRICAL WATCHDOG CHAIN (why the torso never powered up): - PowerWatcher::UpdateWatch reconstructed (@004b181c, the REAL registered Performance -- PTR @0050f5fc; Ghidra missed the fn start): the watchdog MIRRORS the watched subsystem's electrical level (+0x278), brownout downgrade when gen output <= minVoltage% x rated. @004b1804 relabeled ResetToInitialState (slot 10) -- the old "Simulation" tag was wrong. - The factory watcher-CONNECT pass reconstructed (vtable slot +0x38, @004aee2c/@004b1a40 byte-identical, recovered from raw exe bytes): watchedLink.Add(roster[watchedSubsystem]) on the master node. Was the SubProxy::Start() no-op -- every watchdog sat at 0 forever. - MinVoltageScale = 0.01 (a 10-byte x87 literal @0x4b1924; was 1.0f = permanent brownout) and PowerWatcher's Derivation chains its REAL base HeatWatcher (the HeatableSubsystem stand-in broke IsDerivedFrom for the whole Torso/Searchlight/ThermalSight family). - KB correction swept: derivation tag 0x50e604 = HEATWATCHER (not "HeatSink"); the btl4gaug heat-widget gate now tests it via the BTIsHeatWatcher bridge. THE CROSSHAIR (task #58 forensics, 6-agent workflow + live probes): - The VIEW is TORSO-MOUNTED: jointtorso -> jointeye -> siteeyepoint in every twist-capable .SKL; the camera + canopy ride the same hinge subtree through HingeRenderable's live matrix-stack compose -- ALREADY WORKING in the port. The crosshair stays screen-centered (center IS the boresight); the twist reads on the tape carets/compass/radar. - The real bug was the port's gBTAimX = tan(twist) slew (the falsified "body-mounted view" model): the camera already carried the twist, so the crosshair counter-slid to hull-forward and the fire ray with it. Deleted; the pick ray inherits the twist from the yawing eye basis. - Two instrumentation traps documented (chase-eye-as-default-camera, BT_FORCE_TORSO clobbering real joints -> the hook now only fills unresolved ones); an over-correcting explicit eye compose was added on those false readings and retired the same day. CONTROLS + REPLICATION: - Q/E spring-center on release (the axis is a twist-RATE demand; the old hold-deflection model drifted forever); X also zeroes the axis and pulses the authentic torso Recenter (@004b6918). M cycles control mode via the real CycleControlMode body. - Torso update-record DIRECTION fixed: engine truth is Write=serialize / Read=apply; @004b6a78 is the READ (was mislabeled Write) and the missing WRITE @004b6a1c recovered from raw disasm (recordLength 0x1C, twist/vel/rate at +0x10/14/18) -- kills the replicant's 0xCDCDCDCD -140-degree ghost twist. - Marching-ghost desync: 4 Standing-case guards zero stale reverse cycleSpeed (negative cadence passed the <= ZeroSpeed stop gate). - Kill credit rerouted to the OBSERVED killer (lastInflictingID -> killer's player link) -- kills count, target K/D populates. KB: subsystems.md (watcher chain), multiplayer.md (record direction), combat-damage.md + gauges-hud.md + cockpit-view.md (torso-mounted view re-correction), decomp-reference.md (new addresses + tag fix), open-questions.md (dead capability-roster loops 2-4, snapshot CD read). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
5.5 KiB
Markdown
83 lines
5.5 KiB
Markdown
# Phase 02 — dpl2d API recovery + BTReticleRenderable reconstruction (task #35)
|
|
|
|
**Goal:** the main-view HUD (targeting reticle + up to 10 per-weapon pips).
|
|
**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). **[SUPERSEDED: Execute @004cdcf0 was
|
|
RECOVERED task #37 (capstone disasm → reference/decomp/reticle_execute_004cdcf0.disasm.txt) —
|
|
Draw dynamics are now [T1]; pip lighting is FIRE-CYCLE state, not TargetWithinRange; crosshair
|
|
positioning re-corrected task #58 — see context/gauges-hud.md.]** 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]
|
|
|
|
Every recorder self-identifies via its `FUN_0049a300(1, s_dpl2d_...)` debug-name string
|
|
(part_010.c). Opcodes are the first word written into the list's command buffer.
|
|
|
|
| FUN @ | dpl2d name | opcode | args |
|
|
|---|---|---|---|
|
|
| 00487f34 | NewDisplayList | — | → list handle |
|
|
| 00487fbc | OpenDisplayList (Begin) | — | (list, mode) |
|
|
| 00488030 | CloseDisplayList (End) | — | (list) |
|
|
| 00488054 | FlushDisplayList (Compile) | — | (list) |
|
|
| 004880e8 | AddOpenPolyline | 4 | closed-loop polyline begin |
|
|
| 0048814c | AddClosePolyline (loop end) | 5 | |
|
|
| 00488278 | AddOpenLines | 6 | open line-strip begin |
|
|
| 004882dc | AddClosePolyline (strip end) | 7 | |
|
|
| 00488340 | AddOpenPolypoint | 2 | point-set begin |
|
|
| 004883a4 | AddClosePolypoint | 3 | |
|
|
| 00488408 | AddPoint | 8 | (x, y) — vertex of the open primitive |
|
|
| 0048847c | AddCircle | 9 | (x, y, r, fill) |
|
|
| 00488500 | AddSetLineWidth | 0x15 | (w) — 1.0/2.0/3.0 used |
|
|
| 0048856c | AddPushState | 0x12 | |
|
|
| 004885cc | AddPopState | 0x13 | |
|
|
| 00488630 | AddSetColor | 0xf | (r, g, b) floats |
|
|
| 004886ac | AddSetColor (packed) | 0x16 | (argb?) |
|
|
| 00488718 | AddSetMatrix | 0x10 | 6 floats (2x3 affine) |
|
|
| 004887b0 | AddConcatMatrix | 0x11 | 6 floats + flag |
|
|
| 00488850 | AddCallDisplayList | (nested) | (list) — glyph reuse |
|
|
| 004888c0 | AddFullScreenClipRegion | | |
|
|
| 00488b80 / 00488bac | 2x3 matrix build: identity / set-translate(x,y) | — | local helpers feeding AddSetMatrix |
|
|
|
|
⚠ CORRECTION to the earlier AddWeapon recon comments: the "PushMatrix/MoveTo/PopMatrix"
|
|
triple is actually **AddOpenPolypoint / AddPoint / AddClosePolypoint** = "draw a POINT" —
|
|
`game/reconstructed/dpl2d.cpp`'s current Push/Move/Pop interpretation must be reworked to
|
|
this opcode model (points, open/closed polylines, circles, width, color, push/pop state,
|
|
set/concat matrix, nested list calls) and its `dpl2d_ExecuteList` renderer extended to draw
|
|
them (XYZRHW screen-space lines/points/fans; state save/restore already present).
|
|
|
|
## BTReticleRenderable — what the ctor builds (@004cc40c, part_014.c:4362) [T1 read]
|
|
|
|
- Calibration from ctor params/templates: pip origin/bias/scale (+0x1fc..0x234 in class
|
|
terms), min/max range from templates, `rangeScale = (clamped - lo) / (hi - lo)`.
|
|
- Loads **PNAME1-8.bgf** (all ship in VIDEO/GEO) — pip/number meshes — plus a 3D
|
|
renderable chain (FUN_00489708/489724/48ab0c/491cc4... — the dpl *3D* overlay object at
|
|
0.12 scale) — the reticle has BOTH a 3D marker component and the 2D display lists.
|
|
- Builds ~15 display lists: the master list (param_1[0x98]) composed of nested glyph lists
|
|
via AddCallDisplayList: crosshair dot cluster (AddPoint xy at ±0.04/±0.1 = the dotted
|
|
cross), heavier ±0.1..0.16 tick cross at width 3, two tick-LADDERS via FUN_004cd938
|
|
(count 0xd=13 / 0x15=21 ticks, spacing/geometry from the calibration constants at
|
|
[0x7f..0x88]: 0.35/0.25/0.5/±0.008/0.016...), side arrow polylines (the 4-vertex
|
|
closed arrows at the ladder ends), an arc segment (AddCircle partial at 0.03?), and the
|
|
heading/aim sub-lists ([0x9a..0xa1, 0xb6..0xba]) positioned by AddSetMatrix translates.
|
|
- FUN_004cd938 = the tick-ladder builder (count, flag, scale, x, y, step, minor/major
|
|
tick half-lengths) — transcribe next.
|
|
|
|
## Remaining steps
|
|
1. Rework dpl2d.cpp to the opcode model + extend ExecuteList (lines/points/polys/matrix/call).
|
|
2. Transcribe the ctor's glyph program into BTReticleRenderable::BTReticleRenderable
|
|
(+ FUN_004cd938 helper) with the authentic constants.
|
|
3. Read + transcribe Execute (vtable PTR_LAB_0051db44 — find the Execute slot; per-frame:
|
|
withinRange attr reads → pip list visibility/colors, aim/heading matrix updates).
|
|
4. Read the 1996 caller wiring (FUN_004cef28's inside path: the ctor's 15 args + the
|
|
AddWeapon per-weapon argument sourcing) and reproduce in MakeMechRenderables.
|
|
5. Draw hook: the engine already calls mReticle->Execute() per frame (L4VIDEO ~6562);
|
|
or execute from the BT renderer after the 3D scene; dpl2d_ExecuteList(device).
|
|
6. The cockpit canopy shell (blx_cop, punch cutout) black-enclosure fix (BT_INSIDE_COCKPIT=1).
|