KB: full staleness audit + sweep -- 35 verified corrections across 16 topics, 15 new glossary terms

Adversarially-verified audit (18 agents) against the task #46-#56 landings:
MP/current-state understatements, superseded punch readings, retired env
gates, dead paths, line-cite drift; env-gate hub table completed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-11 10:56:51 -05:00
co-authored by Claude Fable 5
parent 2abfd51350
commit 3c34ae6de6
18 changed files with 253 additions and 103 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ spec is `docs/ASSET_PIPELINE.md`. Full list: `docs/PROGRESS_LOG.md §5`.
(`DIV-BSL2` — a BIT-SLICED container: w*h 32-bit texel words holding SIX independent 4-bit
GRAYSCALE sub-images (nibble pair-swapped, byte 0 pad) OR RGB444/RGBA4444 (sliceType 7/8). The BMF
`0x18 BITSLICE` tag picks the slice, absent=0). Decoding BSL as RGB = the "rainbow/graffiti mech"
bug. Reference reader: `DivLoader/VGCDivLoader.cpp:323-410`. [T2]
bug. Reference reader: `DivLoader/VGCDivLoader.cpp:323-410`; archive-only — in-repo reimplementation: `engine/MUNGA_L4/image.cpp`. [T2]
- **`.VMF`** — `DIV-VIZ2` material SOURCE. Carries the `SCROLL` animation + `MAP{}` image binding for
effects (beams/muzzle/smoke/sky). THE key effects asset ([[rendering]]). [T2]
- **[[material-ramp]]** — the IG board colours no-normal geometry by a 2-endpoint RAMP; details in
+2 -1
View File
@@ -15,7 +15,8 @@ open_questions:
spec (verified against all 1275 content `.BGF` files) lives in **`docs/BGF_FORMAT.md`** — this
topic captures the structure + the hard-won gotchas that cost real debugging. Loader of record:
`engine/MUNGA_L4/bgfload.cpp` (game-callable via `LoadBgfFile`); standalone reference reader
`DivLoader/VGCDivLoader.cpp`. Tag names: `dsys/PFBIZTAG.H`.
`DivLoader/VGCDivLoader.cpp` (archive-only; in-repo reimplementation: `engine/MUNGA_L4/image.cpp`).
Tag names: `dsys/PFBIZTAG.H`.
## Structure
`OBJECT → LOD → PATCH (geogroup) → PMESH`. Interleaved float32 verts; **QUAD faces (must
+27 -18
View File
@@ -5,13 +5,13 @@ status: living
source_sections: "task #55 (2026-07-10); reference/decomp part_007.c/part_013.c/part_014.c/part_011.c; engine/MUNGA_L4/bgfload.cpp, L4VIDRND.cpp; game/reconstructed/btl4vid.cpp"
related_topics: [rendering, bgf-format, gauges-hud, subsystems, decomp-reference]
key_terms: [material-ramp, siteeyepoint]
open_questions: ["gyro tail: gait jolt/rumble [T3 gates], sway model, torso-pitch eye writer, damageForce fill", "exact unlit ramp index for the frame colour (BT_COP_RAMP_L=0.08 is T3 tuned-to-footage)"]
open_questions: ["gyro tail: gait jolt/rumble [T3 gates], sway model, torso-pitch eye writer, damageForce fill", "exact authored frame colour: the unlit constant (0.13,0.12,0.15) is T3 tuned-to-footage (BT_COP_FRAME overrides; the ramp-index approach + BT_COP_RAMP_L are RETIRED)"]
---
# First-Person Cockpit: the *_cop Canopy + the Authentic Eyepoint
The pod's only view was the cockpit eyepoint looking out through a per-mech canopy shell.
Task #55 reconstructed both halves — the CANOPY rendering and the EYE camera — from the binary.
Verified in-game on the Madcat against gameplay footage. [T2]
Verified in-game on all 8 mechs (Thor vs gameplay footage). [T2]
## The canopy shell (*_COP.BGF)
@@ -29,22 +29,27 @@ Verified in-game on the Madcat against gameplay footage. [T2]
38-59% across all 12). The struts ARE the frame; the openings ARE the windows. Beware the
edge-counting trap: counting edges across patches (patch-local indices collide) undercounts
boundaries catastrophically (BLX "7%" wrong → 59% right). [T1]
- **Rendering (bgfload.cpp):** keyed by filename (`meshIsCop`, stem contains `_cop`):
1. **Single-sided** (emitTri): double-siding draws far struts through the near openings → the
"black box". `BT_COP_DOUBLE=1` restores (diag).
2. **Per-face INWARD winding orientation** (finish()): the l/r torso patches are MIRRORED
copies, so no global winding works — orient each face's kept winding toward the mesh
interior (where the pilot sits). `BT_COP_FLIP=1` flips (diag).
3. **Dark `softer`-ramp frame colour**: texture-less ramp + no normals = unlit; evaluate the
ramp at a flat index, default `BT_COP_RAMP_L=0.08` (near the dark end — [T3] tuned to the
near-black frame in footage; the board's exact unlit index is not in the code).
- **Rendering (FINAL treatment 2026-07-11; bgfload.cpp keyed by filename `meshIsCop`):**
1. **The PUNCH STENCIL-CUT kit** (see §FINAL below + [[bgf-format]]): the TAG_PATCH scan
hash-pairs the punch geogroup's chunks into mask / hull / twin roles; L4D3D executes the
3-step stencil cut (D24S8). `BT_COP_PLATES=1` disables (diag).
2. **Double-sided** like all no-normal geometry — the earlier single-siding + per-face inward
winding existed solely to fight the solid-box look, which the un-cut PLATES caused, not the
winding; both are REMOVED (bgfload.cpp:533 comment). `BT_COP_SINGLE=1` restores
single-siding, `BT_COP_FLIP=1` flips its winding (diag only).
3. **UNLIT constant frame colour** (the map-lighting bug fix, 2026-07-11): the frame batch is
routed through the renderer's pure-emissive branch (black diffuse + emissive
`(0.13,0.12,0.15)` [T3 tuned-to-footage]; `BT_COP_FRAME="r g b"` overrides). The previous
lit path multiplied the colour by the MAP light — cavern-night `ambient=0.8` washed the
frame gray (user-reported). `BT_COP_RAMP_L` + the flat-ramp-index approach are RETIRED.
- The shell SHOWS by default in the inside view (btl4vid.cpp ApplyViewSkeleton);
`BT_HIDE_COCKPIT=1` hides it (diag).
- **Wrong turns, recorded so they stay dead:** (a) "windows are punch texels" — no texel exists;
(b) "the punch patch IS the visible frame / drop-punch removes the cockpit" — an artifact of
testing with the broken pre-inverse eye; with the authentic eye the punch patches are plainly
the WINDOWS (see §resolved below) and dropping them is correct; (c) a view-direction face-cut
heuristic — not data-derivable, superseded by the lattice + winding + punch-drop treatment.
testing with the broken pre-inverse eye; (c) a view-direction face-cut heuristic — not
data-derivable; (d) "drop the whole punch geogroup = the windows" and (e) "duplicate pairs =
plates, hide both" — both superseded by the firmware-decoded STENCIL-CUT kit (§FINAL below),
which is the authentic mechanism.
## The authentic eyepoint (decomp-verified [T1])
@@ -141,7 +146,8 @@ Some mechs have laterally OFFSET cockpits, and the data + our port agree exactly
center +1.13 (exact)**; loki 0.25/0.15; owens +0.19/+0.16; the other five ≈0/≈0. Our in-game
measured eye (`[EYECHK]`) reproduces the Thor offset to the hundredth (lateral 1.13 from the mech
origin). The eye chain (baseOffset + parent-segment DCS composition) is therefore CORRECT per-mech;
the still-blocked cockpit views are a canopy-RENDER question, not a camera one.
the canopy-render question was subsequently resolved by the stencil-cut kit — all 8 render
authentically (see the FINAL paragraph above).
## The playable roster is 8, not 12 [T1]
@@ -169,9 +175,12 @@ user-community report ("frames were never this revealed") + the resolution/FOV h
## Diagnostics / env gates
`BT_FORCE_MODEL=<name>` (force the player mech: madcat/owens/sunder/…; btl4mssn.cpp),
`BT_HIDE_COCKPIT`, `BT_COP_DOUBLE`, `BT_COP_FLIP`, `BT_COP_RAMP_L`, `BT_COP_DEBUG` (paint the
shell green), `BT_COP_DUMP` (per-batch punch/bbox), `BT_EYE_FWD=<f>` (push the eye forward — the
position probe), `[EYECHK]` log (eye world pos + look dir + entity pos, first frames).
`BT_HIDE_COCKPIT`, `BT_COP_PLATES` (disable the stencil kit), `BT_COP_FRAME="r g b"` (frame
colour override), `BT_COP_SINGLE` / `BT_COP_FLIP` (single-siding diag), `BT_COP_DEBUG` (paint
the shell green), `BT_COP_DUMP` (per-batch punch/bbox), `BT_EYE_FWD=<f>` (eye position probe),
`BT_GYRO_LOG` / `BT_GYRO_TRACE` (gyro bring-up log / per-frame integrator trace, gyro.cpp),
`[EYECHK]` log (eye world pos + look dir + entity pos, first frames).
(RETIRED: `BT_COP_DOUBLE`, `BT_COP_RAMP_L` — removed with the stencil-cut/unlit-frame landing.)
## Key Relationships
- Uses: [[bgf-format]] (PUNCH, vertex tags) · [[rendering]] (ramp model, skeleton branch)
+21 -15
View File
@@ -3,7 +3,7 @@ id: combat-damage
title: "Combat & Damage — targeting, firing, damage zones, death"
status: established
source_sections: "PROGRESS_LOG.md §10c; docs/HARD_PROBLEMS.md (P5)"
related_topics: [subsystems, decomp-reference, reconstruction-gotchas, locomotion, rendering]
related_topics: [subsystems, decomp-reference, reconstruction-gotchas, locomotion, rendering, translocation-warp, multiplayer]
key_terms: [damage-zone, TakeDamage, viewpoint-entity, master, replicant]
open_questions:
- "Per-impact aim (cylinder lookup); collision damage application; DeathShutdown + collapse anim"
@@ -200,8 +200,10 @@ Built + runtime-verified. Changes:
the absorber bank — which stores NOTHING, so the whole unaimed path was inert until 2026-07-08 (the
"can't kill the enemy" bug). See [[reconstruction-gotchas]] §2. [T2]
- **`Mech::TakeDamageMessageHandler`** override registered (`MESSAGE_ENTRY(Mech, TakeDamage)`, overlays
Entity's by ID): on `invalidDamageZone`, `msg->damageZone = table->ResolveHit(msg->damageData.impactPoint)`,
clear the flag, then base-route. Aimed hits pass straight through.
Entity's by ID): the handler FIRST feeds the raw `Damage` to the Gyroscope hit-bounce (`GyroApplyDamage`,
@0x4a0264 hub, task #56) before zone resolution; then on `invalidDamageZone`, `msg->damageZone =
table->ResolveHit(msg->damageData.impactPoint)`, clear the flag, then base-route. Aimed hits pass
straight through.
- **Named accessors (no databinding-trap raw reads):** `Mech::WorldToLocal` (`localToWorld.MultiplyByInverse`),
`CylinderReferenceHeight` (`standingTemplateMaxY` = `collisionTemplate->maxY` = the binary `mech+0x2ec[+0xc]`),
`TorsoHeading` (via the `BTGetTorsoTwist` bridge in `torso.cpp` → `Torso::CurrentTwist` = torso+0x1d8;
@@ -220,7 +222,8 @@ Built + runtime-verified. Changes:
invisible 1-shot kills (internal zones are soft; the binary never aims them — they die only via the
destruction cascade `RecurseSegmentTable`/`SendSubsystemDamage`). On kill the wreck STAYS TARGETED
(`gEnemyMech` is NOT nulled) so beams keep terminating on it — nulling it made every later beam a
"free" ray that visibly passed through the wreck; scoring latches off via `gEnemyDestroyed`. [T2]
"free" ray that visibly passed through the wreck; scoring latches off via `gEnemyDestroyed` — until
the burial transition drops the lock (`gEnemyMech = 0` when the buried wreck goes INERT, mech4.cpp). [T2]
`Mech__DamageZone::TakeDamage` → engine armor model (`damageLevel
+= damageAmount*damageScale[type]`) → 1.0 = zone destroyed → death. The Mech ctor populates the
inherited `damageZones[]` from the DamageZoneStream (type 0x14) resource. `class Damage{ damageType
@@ -286,8 +289,9 @@ Full decomp map of the death path. Two tiers:
(mech+0x120[mech+0x11c]); as a subsystem's damage crosses a descriptor-table threshold (`FUN_0042a5f4`
detects the crossing, `FUN_0042a664` looks up the state) it applies the `GraphicState` ("Destroyed" = enum 1,
parsed @part_003.c:9099) to the subsystem graphic (`sub+0xd4`) AND dispatches an effect-creation message
(`FUN_0043663c`→`FUN_004364e4` to the effect mgr `app+0x38`). **Fully exported but a no-op STUB** in the
recon (mech.cpp:221). Gamedata `DeathSplashDamage`/`Radius`/`Effect` load into a descriptor @ +0x74/78/7c
(`FUN_0043663c`→`FUN_004364e4` to the effect mgr `app+0x38`). Fully exported; was a no-op stub
(mech.cpp:221) — now fully RECONSTRUCTED (mechdmg.cpp — ctor @0042a984 / Tick @0042aa2c; wired in the
Mech ctor, mech.cpp:1469) [T2]. Gamedata `DeathSplashDamage`/`Radius`/`Effect` load into a descriptor @ +0x74/78/7c
(`FUN_004a2da8`). Destroyed-skin variant set (`destroyed`, `destroyeds`…`destroyedd`) @part_003.c:5720.
**NOT EXPORTED (master-perf gap 0x4a9770-0x4ab188 + the un-exported TakeDamage) [T4]:**
- The `movementMode` WRITER (sets 5-8 then 2/9 on death) + the fall-direction (5/6/7/8) selection.
@@ -302,19 +306,21 @@ the RP analog and wired into the active path (mech4.cpp `PerformAndWatch`).
### ✅ Death-STATE core DONE (2026-07-08) [T2]
`Mech::UpdateDeathState()` + `IsMechDestroyed()` (mech4.cpp) reconstruct the un-exported death branch from
its exported consumers + the RP analog, called for EVERY mech early in `PerformAndWatch`: on `graphicAlarm
>= 9` it (1) sets `movementMode = 5` → the collapse clip's latch in `AdvanceBodyAnimation` (fall direction
5-8 un-exported → 5 [T3]); (2) loops the roster calling `Subsystem::DeathShutdown(1)` (RP `VTV::DeathShutdown`
analog; base is a no-op virtual, a SHUTDOWN not teardown → safe, wreck STAYS); (3) next frame settles to
`movementMode = 9` → `IsDisabled` → locomotion frozen. The drive's lone `movementMode = 1` write (mech4.cpp
>= 9` it (1) settles the death mode STRAIGHT to `movementMode = 9` (mech4.cpp:1183) — there is NO
`movementMode = 5` collapse write (the only other writes are the drive's `= 1`, mech4.cpp:1044/1986); the
fall latch 5-8 is never fired (task #32: no collapse anim exists in BT 4.11, see the VESTIGE proof above)
[T1]; (2) loops the roster calling `Subsystem::DeathShutdown(1)` (RP `VTV::DeathShutdown`
analog; base is a no-op virtual, a SHUTDOWN not teardown → safe, wreck STAYS); (3) `movementMode` 9 →
`IsDisabled` → locomotion frozen. The drive's lone `movementMode = 1` write (mech4.cpp
~1309) is guarded on `!IsMechDestroyed` so the death state isn't clobbered. **Runtime-verified** (forced kill):
`[death] … collapse + subsystem shutdown` → `[death] … settled -> disabled (IsDisabled=1, frozen wreck)`,
mech frozen in place (pos unchanged), subsystem tick + renderer keep running, **no crash, wreck stays**.
Env gate `BT_DEATH_LOG=1`.
**Deferred (honest):** (a) the visible **collapse ANIMATION** may not latch through the active path — the
gait SM showed `state=0` post-death (the clip trigger via `AdvanceBodyAnimation` needs the death `movementMode`
to reach its latch; and spawned NON-player mechs don't advance their body anim at all). (b) **`MechDeathHandler`**
(the exported per-subsystem destroyed-skin + explosion engine, `FUN_0042a984`/`FUN_0042aa2c`) is still the
no-op stub → no death explosions/skin-swaps yet. (c) the whole-mech **DeathSplash** radius damage (un-exported).
**Deferred (honest):** (a) ✅ RESOLVED (task #32) — there IS no collapse animation to latch: the fall
latch is a VESTIGE (see the proof above); the authentic death visual is the wreck-hulk swap, and the death
mode settles straight to 9. (b) ✅ RESOLVED — **`MechDeathHandler`** is fully RECONSTRUCTED (mechdmg.cpp —
ctor @0042a984 / Tick @0042aa2c; wired in the Mech ctor, mech.cpp:1469) [T2]; destroyed-skin swaps + zone
explosions verified live (see STEP 6 above). (c) the whole-mech **DeathSplash** radius damage (un-exported).
### ⚠ Death-latch correction (2026-07-09, task #52) [T1→T2]
`IsMechDestroyed()` originally tested **`graphicAlarm >= 9` alone** — a bring-up shortcut that made a
+1 -1
View File
@@ -25,7 +25,7 @@ across archives. The bt411 `content/` dir is the assembled runtime tree. Full de
the source tree's `CONTENT/BT/ANIMS/`. All assembled into bt411 `content/`. [T2]
## The runtime archive
`content/BTL4.RES` (3.23 MB, byte-identical to `reference/decomp/BTL4.RES`) contains the built
`content/BTL4.RES` (3.23 MB; byte-identical to the pod-tree and decomp-extraction copies — provenance in `docs/PROGRESS_LOG.md §4`) contains the built
subset: models, materials, skeletons, animation clips, damage-zone/subsystem records, and **8
loadable maps**. Resolved by name/ID via `FindResourceDescription` (a missing map name null-derefs
at `MISSION.cpp:355`). [T1]
+14 -4
View File
@@ -141,14 +141,17 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a
## 6. Env-var gates (runtime; default OFF unless noted)
The authentic stack is DEFAULT-ON (`BTEnvOn`, mechrecon.hpp): `BT_GAIT_CUTOVER`, `BT_GAIT_SM`,
`BT_COLLISION`, `BT_REAL_CONTROLS` default ON — set `=0` to fall back. ⚠ Gates check
`getenv(name) != NULL`, so `BT_X=""` (empty) still ENABLES; use `Remove-Item Env:\BT_X`.
`BT_COLLISION`, `BT_REAL_CONTROLS` default ON — set `=0` to fall back. ⚠ Most BOOLEAN gates check
`getenv(name) != NULL` or first-char `!= '0'`, so `BT_X=""` (empty) still ENABLES; use
`Remove-Item Env:\BT_X`. Value-parsing gates are NOT empty-enabled: `BT_AUTOFIRE` and
`BT_KEY_NOFOCUS` require `'1'`, `BT_GOTO` requires a non-empty value, and `BT_WARP_MIP` is
default-ON (`'0'` disables).
| Var | Effect |
|---|---|
| `BT_FORCE_THROTTLE` | auto-walk forward (no key) |
| `BT_SPAWN_ENEMY` | spawn a target mech 120u ahead along the spawn facing |
| `BT_FORCE_FIRE` | auto-fire on cooldown |
| `BT_AUTOFIRE=1` | hold the trigger (headless walk→fire→death harness; supersedes the dead `BT_FORCE_FIRE` — btl4main.cpp:310 set `fireForced` once at startup, but mech4.cpp:1567 unconditionally overwrites it every frame) |
| `BT_ASSERT_TO_DEBUGGER` | route CRT asserts to the debugger, not a modal box |
| `BT_HEAPCHECK` | whole-heap validation on every alloc/free (O(n²) at mission load — SLOW) |
| `BT_PROBE_BGF=<name\|ALL>` | boot-time BGF loader probe, exits after |
@@ -165,11 +168,18 @@ The authentic stack is DEFAULT-ON (`BTEnvOn`, mechrecon.hpp): `BT_GAIT_CUTOVER`,
| `BT_START_INSIDE` | begin in the cockpit view (V toggles) |
| `BT_FORCE_MODEL=<name>` | force the player mech (`madcat`/`owens`/…; btl4mssn.cpp — per-mech cockpit bring-up) |
| `BT_HIDE_COCKPIT` | hide the `*_cop` canopy shell (SHOWS by default — see [[cockpit-view]]) |
| `BT_COP_*` / `BT_EYE_FWD=<f>` | cockpit canopy + eye diagnostics (`DOUBLE`/`FLIP`/`RAMP_L`/`DEBUG`/`DUMP`; eye forward-push probe) — [[cockpit-view]] |
| `BT_COP_*` / `BT_EYE_FWD=<f>` | cockpit canopy + eye diagnostics (`FRAME` (unlit frame colour override, default 0.13,0.12,0.15) / `SINGLE` (single-sided diag; double-sided is default) / `PLATES=1` (disable the punch stencil-cut kit) / `FLIP` / `DEBUG` / `DUMP`; eye forward-push probe) — [[cockpit-view]] |
| `BT_SHOT=<path.png>` | periodic backbuffer dump (every 90 frames) — non-disruptive visual verification |
| `BT_TLOC_LOG` | translocation-warp lifecycle log (`[tloc] warp COLLAPSE/EXPAND/phase=…`) |
| `BT_WARP_*` | translocation-warp visual tuning — all default to the verified values (see [[translocation-warp]]): `EYE_UP=8.25` (on-axis), `SPIN=4`, `TWIST=0`, `CONTRAST=1.0`, `LO_*/HI_*` (lavender ramp), `BLUR=0`, `TESS=3`, `MIP=1`, `CULL=cw`, `ANISO=0` |
| `BT_WARP_SELFTEST` / `BT_WARP_SELFSHOT=<prefix>` | DIAG (off by default): force a held warp in a solo game / dump backbuffer frames to disk (visual-verification harness) |
| `BT_GYRO_LOG` | gyro bring-up log (gyro.cpp) |
| `BT_GYRO_TRACE` | per-frame hit-bounce integrator trace |
| `BT_REPL_LOG` | replicant/MP replication log (mech4.cpp) |
| `BT_GOTO="enemy"\|"x z"` / `BT_GOTO_LOG` | self-driving beeline (to the enemy or a map coordinate) + its log |
| `BT_MP_LOG` / `BT_MP_NET` / `BT_MP_FORCE_DMG` | MP diagnostics / forced cross-pod damage |
| `BT_AUTODRIVE=<0..1>` | headless throttle |
| `BT_KEY_NOFOCUS=1` | accept key input without window focus |
Full render/locomotion gates (BT_RAMP, BT_MATPRI, BT_CULL, BT_SHADOW_*, BT_LODSEL, BT_ADDLOD,
BT_PUNCH, …) are catalogued in [[rendering]]. Warp visuals: [[translocation-warp]].
+2 -3
View File
@@ -142,9 +142,8 @@ and every instrument is now live [T2]:**
the aim group translates by `reticlePosition` = the TORSO BORESIGHT (SetMatrix; NOT a free
cursor — see [[combat-damage]] Targeting: the stick twists the torso, no mouse-aim exists).
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]].
(hud.hpp had flagged them uncertain). Deferred: PNAME1-8.bgf 3D marker chain. (The canopy shell
is now authentic and shows by default — see [[cockpit-view]]; `BT_HIDE_COCKPIT=1` hides it.)
## Key Relationships
- Full history: `docs/GAUGE_COMPOSITE.md`; reticle recovery: `phases/phase-02-dpl2d-reticle.md`.
+6 -2
View File
@@ -7,7 +7,7 @@ related_topics: [asset-formats, decomp-reference, reconstruction-gotchas, combat
key_terms: [RootTranslation, ground-model, SequenceController, MechControlsMapper, SLD, joint]
open_questions:
- "Authentic per-mech turn-rate constant (currently bring-up constant)"
- "Body-callback gimp handlers (states 16-19); airborne callbacks"
- "Airborne clip callbacks (FUN_004a7970) deferred"
---
# Locomotion
@@ -33,7 +33,9 @@ raw disasm** (both Advance fns had NO static decomp caller; found by byte-scanni
- **BODY channel = the replication PROJECTOR.** `IntegrateMotion` (0x4ab1c8; body advance @0x4ab312,
from `FUN_004ab430`, the projected-origin/dead-reckon updater) stores `-dist/dt` into
**projectedVelocity (+0x2a0)** — the dead-reckoning feed for update records. Its phase drift is
locally INVISIBLE in the binary. (The recon does not yet wire this store — follow-up.) [T1]
locally INVISIBLE in the binary. The literal projectedVelocity store is unwired, but the
update-record velocity feed is live via the localVelocity/worldLinearVelocity maintenance
(mech4.cpp VELOCITY STORAGE block) — replicant gait (task #50) runs off it. [T1]
**v5 (current): display == travel BY CONSTRUCTION through the LEG** (body advances first, leg last).
History: v3 (travel=leg, display=body) foot-slipped — two channels, different consumers; v4 unified
BOTH on the body (mis-reading 0x4ab430 as the travel source) — feet planted, but whenever the two
@@ -51,6 +53,8 @@ through walk/run caps (`LoadLocomotionClips`, mech3.cpp:326 — measures `walkSt
**Aliasing bug class (namedClip):** `namedClip[]`@0x5e0 == `animationClips[]`@0x5cc+0x14 (ONE array);
`gimpBaseClip`@0x64c == `animationClips[0x20]`; `bodyAnimationState`@0x728 == `bodyStateAlarm.level`.
Declared-separate members don't see each other's writes — alias them.
Replicants (task #50): travel = engine DeadReckon; leg channel driven joints-only from the replicated
worldLinearVelocity -> speedDemand (mech4.cpp REPLICANT MOTION) — detail in [[multiplayer]]. [T2]
## The AUTHENTIC 1995 ground model (default-on, `BT_GROUND_REAL`)
Decoded by the 10-agent `ground-model-decode` workflow (binary `FUN_004a9b5c` = the master perf).
+22 -19
View File
@@ -6,20 +6,22 @@ source_sections: "PROGRESS_LOG.md §7 (Phase 7), §8 (P6); docs/HARD_PROBLEMS.md
related_topics: [wintesla-port, combat-damage, locomotion, decomp-reference]
key_terms: [master, replicant, EGG, dead-reckon]
open_questions:
- "Cross-pod combat (damage to a replicant's master); replicant gait; pod-LAN config"
- "pod-LAN config (real IPs); update-record velocity from the body-channel projection; peer SimulationState/DropZoneLocation replication (authentic peer warp spheres); Mech-level update records @0x4a0c2c (in progress)"
---
# Multiplayer
Pod-to-pod networked play. The hard infrastructure exists (WinTesla replaced DOS `NETNUB` with a
WinSock2 TCP stack); the work is integrating + smoke-testing it for BT. Full detail:
`docs/PROGRESS_LOG.md §7, §8`; the problem writeup: `docs/HARD_PROBLEMS.md` (P6).
WinSock2 TCP stack), and MP is now **substantially reconstructed + 2-node verified** — replication,
cross-pod combat, beam visuals, replicant gait, respawn (tasks #46-#52). Remaining: pod-LAN config,
fidelity nuances, and the Mech-level update records. Full detail: `docs/PROGRESS_LOG.md §7, §8`;
the problem writeup: `docs/HARD_PROBLEMS.md` (P6).
## The stack (already reconstructed)
- WinTesla replaced `NETNUB` with a **3446-line WinSock2 TCP** reimplementation (`MUNGA_L4/L4NET.CPP`,
- WinTesla replaced `NETNUB` with a **~3.5k-line WinSock2 TCP** reimplementation (`MUNGA_L4/L4NET.CPP`,
`SOCK_STREAM`/ReliableMode, zero SOCK_DGRAM); the master/replicant distributed-sim core
(InterestManager/Entity/HostManager) is complete. So MP = integrate + smoke-test that stack (gated
on P3 locomotion + P5 entity lifecycle + subsystem WAVEs). The rumored `L4NET.CPP:1853` send-size
(InterestManager/Entity/HostManager) is complete. MP was integrated on that stack and verified
end-to-end 2-node. The rumored `L4NET.CPP:1853` send-size
bug is DEAD CODE (`#if MESSAGE_BUFFERING`==0). [T2]
## Master / replicant model
@@ -49,7 +51,7 @@ emulator** (⚠ `NotationFile::ReadText` expects NUL-SEPARATED lines). [T2]
via `MakeEntityRenderables`, which runs a TWO-STEP view sequence — inside skeleton + `COCKPIT
eyepoint`, then outside skeleton + `external chase` (`[view] …` logs, btl4vid.cpp:2158) — and the
external chase camera is NOT re-installed (only ONE `[BTrender] external debug chase camera
installed`, btl4vid.cpp:648, for the whole session despite multiple respawns). Plausible failure:
installed`, btl4vid.cpp:647, for the whole session despite multiple respawns). Plausible failure:
if that two-step completes only to the COCKPIT step (raced/interrupted on the `-net` ladder), the
node is left stuck INSIDE — body hidden by the inside skeleton, shadow/reticle flagging with it =
the reported bundle. Camera + reticle + view + `tshd` shadow are all gated on `buildDebugChaseCamera
@@ -109,7 +111,7 @@ the change only ADDS `ValidFlag`).
2. Wire: `NetworkManager::Send` (L4NET.CPP:1062) → `send()` to host 3 (online).
3. B: `ReceiveNetworkPacket` (NTTMGR.cpp:107) → `GetEntityPointer(3:22)` = `entityIndexSocket.Find`
(HOSTMGR.h:270) → resolves to B's **master mech (classID 3001=0xBB9)**`Post(EntityManagerEventPriority)`.
4. B: event drains → `Entity::Receive` (now `valid=1`) → `Mech::TakeDamageMessageHandler` (mech.cpp:548)
4. B: event drains → `Entity::Receive` (now `valid=1`) → `Mech::TakeDamageMessageHandler` (mech.cpp:584)
→ invalidZone (1) cylinder lookup → zone damage. ~36 hits × 12 → B's mech DESTROYED + death transition.
**Two prior mis-diagnoses corrected — both were the WRONG messageID.** `Entity::TakeDamageMessageID` is
@@ -124,7 +126,7 @@ on the wire" claim. Both are FALSE. The id is correct end-to-end (`3:22` = B's m
is corrupted. Lesson: when a message probe seems to see a wrong id, first confirm the numeric messageID.
Retained gated instrument: `[mp-force]` (BT_MP_FORCE_DMG + BT_MP_LOG) — the 2-node cross-pod reproducer,
off in solo. `[mp-hdlr]` (BT_MP_NET, mech.cpp:554) logs each applied cross-pod hit. 2-node launch recipe +
off in solo. `[mp-hdlr]` (BT_MP_NET, mech.cpp:591) logs each applied cross-pod hit. 2-node launch recipe +
`scratchpad/mp_test.sh`: A `-egg MP.EGG -net 1501`, B `-net 1601`, then `python tools/btconsole.py MP.EGG
127.0.0.1:1501 127.0.0.1:1601` (MP.EGG `[pilots]` = game ports 1502/1602).
@@ -195,13 +197,13 @@ Re-ran the one-box smoke test on the current build (world-pick targeting, weapon
transition, HUD all landed since P6): console egg → mesh → RunningMission on BOTH instances, 174+
×144-byte update records EACH side (msgID=6), 2 mech trees per instance, 0 crashes over ~6 min.
**Gap map to a first playable LAN fight** (in wiring order):
1. **Enemy selection**: the world-pick tests only `gEnemyMech` (the solo dummy, btplayer.cpp:791).
MP: the pick must test every OTHER mech (the peer replicants) — generalize to a candidate walk
(mech entities != viewpoint). Small.
2. **Cross-pod damage**: dispatch TakeDamage at the REPLICANT → verify the engine reroute carries
it to the owning master (the KB claims Dispatch reroutes; not yet exercised with our STEP-6
handler). The master takes real zone damage + runs its own death transition (task #42 placed the
death dispatch victim-side — MP-correct by construction).
1. **Enemy selection — DONE (task #46).** The world-pick used to test only `gEnemyMech` (the solo
dummy, btplayer.cpp:791); now generalized to the live-mech registry candidate walk
(`BTGetTargetCandidates`, all mechs != shooter) — see "Cross-pod combat step 1+2" above.
2. **Cross-pod damage — DONE (task #47).** TakeDamage dispatched at the REPLICANT reroutes to the
owning master (verified end-to-end: full cross-pod kill; the master takes real zone damage + runs
its own death transition; task #42 placed the death dispatch victim-side — MP-correct by
construction). See "Cross-pod combat step 1+2" above.
3. **Victim visuals on the shooter's screen** — PARTIALLY resolved (user-observed 2026-07-09): the
wreck SWAP does appear on the killer's screen, but the death SINK/burial does not (the collapse+
burial runs only in the owning pod's death transition). Fix path: transcribe the recovered
@@ -313,9 +315,10 @@ transition, HUD all landed since P6): console egg → mesh → RunningMission on
variants, 2 alarm, 3 leg-state+heat with body-channel write-through re-sync, 5 knockdown
(SetBodyAnimation(0x20) write-through), 6 death, 7 impact, 8 movementMode) — transcribing it
would replicate remote KNOCKDOWN/death/heat states; beams did not need it.
5. **2-window driving**: input gates on window focus (alternate windows; `BT_KEY_NOFOCUS` for
harness). DEATHS scoring should light via the existing BTPostKillScore MP branch. Respawn =
the P5 teardown debt (deferred until needed).
5. **2-window driving — RESOLVED (tasks #48/#51).** Keyboard MP turning was restored at the root
(the `pilotArray[1]` shrunk-span overrun fix, task #51); `BT_KEY_NOFOCUS` exists for automated
harnesses. DEATHS scoring lights via the existing BTPostKillScore MP branch. Respawn is
reconstructed (task #52 — see item 3b).
## Key Relationships
- Base: [[wintesla-port]] (L4NET). Depends on: [[locomotion]] (update writer), [[combat-damage]]
+18 -15
View File
@@ -60,25 +60,27 @@ authentic path scoped.
on mech+0x388, but 0x388 has 11 reads / 0 direct stores across CODE → set indirectly). Residue:
(a) the binary's own per-frame reticle→mech copy + the AUTO-TARGET SELECTOR are still in an
un-exported gap (a message/selector; our port auto-targets the single living enemy — for MP,
reconstruct the real multi-target selection: nearest/most-aligned/cycle); (b) our cockpit view is body-mounted, so on a
TWIST-CAPABLE mech the crosshair should deflect with the torso twist (`BTTwistToReticleX` is
wired for it) AND the torso should visibly lead the legs — verify on a twist mech when one is
in play (the BLH is fixed-torso, boresight always centred); (c) pre-burial, the pick still
reconstruct the real multi-target selection: nearest/most-aligned/cycle); (b) the eyepoint now
rides the parent DCS (task #55 authentic eye), so torso pose reaches the view; on a
TWIST-CAPABLE mech (selectable via `BT_FORCE_MODEL`) the crosshair should deflect with the
torso twist (`BTTwistToReticleX` is wired for it) AND the torso should visibly lead the legs —
verify the reticle deflection on a twist mech (the BLH is fixed-torso, boresight always
centred); (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 (tasks #35-#38 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; the authentic Lock producer + the simple-X mode landed in #38 — see
[[gauges-hud]] for the full instrument map). Remaining: (a) the **`*_cop` canopy shell** —
SOLVED for the Madcat (task #55, see [[cockpit-view]]): authentic eye (baseOffset + parent-DCS +
inverse view) + single-sided inward-wound lattice + dark `softer` ramp = the dark frame with the
world through the openings, verified vs gameplay footage; shows by DEFAULT now
(`BT_HIDE_COCKPIT=1` hides). NOTE the old claim here ("canopy windows are punch texels") was
WRONG — the shell has no texture; the openings are geometry. ✅ RESOLVED (2026-07-11): the
PUNCH patches are the transparent WINDOWS (dropped by the loader); the frame = the non-punch
geometry only. All 8 mechs render their authentic canopy — madcat's dome, thor/owens/sunder's
brace/dash remnants, and the authentically FRAMELESS bhk1/loki/vulture/avatar. Eye placement
verified exact per-mech incl. Thor's +1.13 offset cockpit ([[cockpit-view]]). (b) the **3D marker
SOLVED for all 8 (task #55): authentic eye + the PUNCH stencil-cut kit + double-sided cop
geometry + unlit constant frame colour (`BT_COP_FRAME`) — see [[cockpit-view]] Rendering/FINAL;
shows by DEFAULT now (`BT_HIDE_COCKPIT=1` hides). NOTE the old claim here ("canopy windows are
punch texels") was WRONG — the shell has no texture; the openings are geometry. ✅ RESOLVED
(2026-07-11): PUNCH = a 3-chunk STENCIL-CUT kit (mask/hull/twin, i860-firmware-decoded)
executed as a D24S8 stencil cut; visible canopy = hull-minus-apertures ([[cockpit-view]]
§FINAL). All 8 mechs render connected dark frames with clear viewports (the intermediate
"frameless bhk1/loki/vulture/avatar" reading was wrong); thor matches the pod footage exactly.
Eye placement verified exact per-mech incl. Thor's +1.13 offset cockpit ([[cockpit-view]]). (b) the **3D marker
chain** ([0x31c]/[0x320] dpl objects + the PNAME1-8.bgf player-name meshes over the locked
target — matters for MP). (c) the compass rotation SIGN is derived-not-verified [T3] (turn
left → the stem should swing right; flip the sin sign in Draw if footage disagrees).
@@ -198,8 +200,9 @@ authentic path scoped.
movementMode states don't reach the other pod (the wreck SWAP does appear). The writer's disasm is
recovered + 9 record types decoded (`reference/decomp/mech_writeupdate_004a0c2c.disasm.txt`);
the read-side leg-state branches at mech.cpp:1511/:1613 are the dead Wword comparisons.
- The pod-LAN config (real IPs, bare-IP pilot entries). The WinSock2 TCP stack (L4NET.CPP) is
reconstructed + smoke-tested on one box (two instances share a world, movement replicates).
- The WinSock2 stack (L4NET.CPP) is 2-node verified end-to-end (cross-pod kills, beams,
replicant gait — tasks #46-#51); remaining = the real pod-LAN config (real IPs, bare-IP
pilot entries).
## Rendering follow-ups (non-blocking)
- **.PFX effect-layer polish (deferred 2026-07-08; the layer itself is LIVE + verified).** The
+8 -4
View File
@@ -37,10 +37,14 @@ they map into this repo (reconstructed BT → `game/reconstructed/`, engine →
content → `content/`, raw decomp → `reference/decomp/`). [T2]
## Current state (2026-07)
`btl4.exe` boots, renders the world + a skinned mech, and runs a **drive → animate → target → fire
→ damage → destroy** single-player loop. The engine/renderer/HAL/audio are done (WinTesla); the
active work is reconstructing each BT subsystem's authentic behavior from the binary. The **gauge
system is complete** ([[gauges-hud]]). [T2]
`btl4.exe` boots, renders the world + a skinned mech, and runs a full **drive → animate → target →
fire → damage → destroy → respawn** single-player loop (task #52). **2-node MP is verified
end-to-end** — replication, cross-pod targeting/damage/kill, beam visuals, replicant gait (tasks
#46-#51, [[multiplayer]]). All 8 cockpit canopies are authentic + the horizontal-FOV fix (task #55,
[[cockpit-view]]); the Gyroscope is live byte-exact with hit-bounce (task #56); the **gauge system
is complete** ([[gauges-hud]]). The engine/renderer/HAL/audio are done (WinTesla); the active work
is reconstructing each BT subsystem's authentic behavior from the binary. Remaining: pod-LAN
config, Mech-level update records, per-subsystem waves. [T2]
## Key Relationships
- Full detail: `docs/PROGRESS_LOG.md`.
+6 -4
View File
@@ -20,8 +20,9 @@ usually here, not in the logic.
> **The core rule (RULE: no stand-ins):** the full game logic IS in the pseudocode — a "gap"
> is a reconstruction stub not yet filled, never a hole in the original. Never write
> placeholder logic for an apparent gap; read the decomp. (User: "there are no gaps, just
> work to be done.") Bring-up scaffolding (env-var paths, explosion-for-beam) is clearly
> marked and meant to be REPLACED, never to substitute for reading the decomp. [T2]
> work to be done.") Bring-up scaffolding (the `BT_AUTOFIRE`/`BT_AUTODRIVE`/`BT_GOTO` env
> harness; historically explosion-for-beam, since replaced by real per-weapon beams) is
> clearly marked and meant to be REPLACED, never to substitute for reading the decomp. [T2]
---
@@ -52,8 +53,9 @@ and `BTVal` is the recon **absorber** type: `operator=` stores NOTHING, every re
check `mech.hpp`'s offset map first (the slot may already exist under a best-effort
mislabel; 0x111 was mislabeled `ammoExpended`).
- `if (Wword(a) != Wword(b))` and `if (Wword(a) == 2)` are BOTH always-false → the guarded
branch is dead code. Known dead sites: `mech.cpp:1511` + `mech.cpp:1613` (replicant
leg-state / stability-alarm sync in ReadUpdateRecord — multiplayer-only, deferred).
branch is dead code. Known dead sites: `mech.cpp:1673` + `mech.cpp:1775` (replicant
state sync in ReadUpdateRecord — MP is live; these are being revived by the in-progress
Mech Write/ReadUpdateRecord reconstruction, task #1).
- Any `Wword(N)` used for OBJECT access reads a shared global, not `this+i*4`; e.g.
`(Mech*)Wword(3)` for a zone's owner → garbage; use `GetOwningSimulation()`.
+11 -6
View File
@@ -24,13 +24,18 @@ Full detail: `docs/PROGRESS_LOG.md §10b`; the systemic bug classes are [[recons
The full game logic IS in the pseudocode (the binary ran the game); a "gap" is a reconstruction
stub not yet filled, not a hole in the original. **Never write stand-in/placeholder logic for an
apparent gap — read the decomp.** (User: "there are no gaps, just work to be done.") Bring-up
scaffolding (env-var paths, explosion-for-beam, a player-gated drive) is clearly MARKED and meant
to be REPLACED by the real reconstructed system, never to substitute for reading the decomp. [T2]
scaffolding (the `BT_AUTOFIRE`/`BT_AUTODRIVE`/`BT_GOTO` env harness; historically
explosion-for-beam and a player-gated drive, since replaced by the real reconstructions) is
clearly MARKED and meant to be REPLACED by the real reconstructed system, never to substitute
for reading the decomp. [T2]
## Decompilation stack (Plan B — in progress)
- **Ghidra 12.1.2 + JDK21** installed under `tools/` (portable). `ExportBTSource.java` run headless
on `BTL4OPT.EXE` → real decompiled C in `reference/decomp/recovered/`. The pass is
**assert-anchored** (few funcs/file — anchored on the asserts that carry source paths). [T2]
## Decompilation stack (established)
- **Ghidra 12.1.2 + JDK21** are an EXTERNAL install (`tools/` holds only python utilities); the
headless scripts live in `reference/ghidra_scripts/` (`ExportBTSource.java`, `DecompVSS.java`,
`ExportAll.java`). `ExportBTSource.java` run headless on `BTL4OPT.EXE` → decompiled C in
`reference/decomp/` (`bt_<src>.cpp.c` assert-anchored files; bulk pseudocode in
`reference/decomp/all/part_*.c`). The pass is **assert-anchored** (few funcs/file — anchored
on the asserts that carry source paths). [T2]
- For a `FUN_` the assert-anchored exporter skipped: **`tools/disas2.py <VA> [len]`** (capstone
disassembly of `BTL4OPT.EXE` — recovers x87 math Ghidra drops, folds known calls + float
constants). Or `DecompVSS.java` (headless address-list decompiler). [T2]
+11 -2
View File
@@ -3,7 +3,7 @@ id: rendering
title: "Rendering — the D3D9 pipeline, materials, sky, beams, performance"
status: established
source_sections: "PROGRESS_LOG.md §10 (render-fidelity notes); docs/BGF_FORMAT.md"
related_topics: [bgf-format, asset-formats, wintesla-port, locomotion, pod-hardware]
related_topics: [bgf-format, asset-formats, wintesla-port, locomotion, pod-hardware, cockpit-view]
key_terms: [L4D3D, material-ramp, BSL, LOD, DPL, beam]
open_questions:
- "Replacement-LOD for NON-additive multi-LOD models; day/night material path priority (BT_MATPRI)"
@@ -22,6 +22,9 @@ mech — WHITE baked verts) is UNLIT, colorized by the material's 2-endpoint **[
(`dpl_SetMaterialRamp` — texture luminance indexes a low→high gradient); **normal-bearing geometry**
(~150 vehicle/missile files) is LIT by the map light. Applying the ramp to lit geometry = the "dusty
white blobs" bug (gate on `hasNormals`). [T2]
Exception: cockpit-frame (`*_cop`) batches are pure-emissive constant colour (0.13,0.12,0.15),
`BT_COP_FRAME` overrides — see [[cockpit-view]] (task #55; the old `BT_COP_RAMP_L` ramp-index
approach is retired). [T2]
## The big render-fidelity fixes (dbase/desert matched to pod footage)
- **Geometry:** CONN is a flat trilist (not fan); CONN+PCONN coexist — see [[bgf-format]]. Malformed
@@ -37,10 +40,16 @@ white blobs" bug (gate on `hasNormals`). [T2]
`L4DPLCFG`): resolves map+time → clip/fog/ambient + a directional sun. Bring-up stubs
(EnsureValidProjection / per-frame ambient / uninit cloud colours) had clobbered it — now they
RE-ASSERT the env values. Debug: copy BTDPL.INI → *DBG.INI with `debug=True`. [T2]
- **Projection / stencil (task #55):** the BTDPL.INI `viewangle=60` is the **HORIZONTAL** FOV —
authentic 60×47 frustum at 4:3; fovY derived via `BTFovYFromHorizontal` (L4VIDEO.cpp), applied at
all 5 projection sites (it was mis-applied as vertical). The device depth-stencil is now
**`D3DFMT_D24S8`** for the cockpit punch stencil-cut (L4VIDEO.cpp:2799; sequence
L4D3D.cpp:1199-1248) — see [[cockpit-view]]. [T2]
## Backface culling (default CW, `BT_CULL`)
The bring-up `D3DCULL_NONE` drew interior/back faces (the "dark wedge" shapes = mesh insides). CW is
correct for the BGF quad triangulation `[a b c][a c d]`. [T2]
correct for the BGF quad triangulation `[a b c][a c d]`. [T2] Exception: cockpit `*_cop` batches
default DOUBLE-SIDED (`BT_COP_SINGLE=1` restores single-siding, diag) — see [[cockpit-view]]. [T2]
## LODs (default: shipping-engine first-LOD + ADDITIVE_LODS; `BT_ADDLOD`/`BT_LOD_SCALE`/`BT_PUNCH`)
See [[bgf-format]] for the √3 range decode, the ADDITIVE_LODS cumulative rule, PUNCH cutouts, and the
+6 -3
View File
@@ -6,7 +6,7 @@ source_sections: "PROGRESS_LOG.md §10d; docs/SUBSYS_PLAN.md; docs/VEHICLE_SUBSY
related_topics: [decomp-reference, reconstruction-gotchas, combat-damage, gauges-hud]
key_terms: [subsystem, factory, ClassID, subsystem-roster, PoweredSubsystem, bridge]
open_questions:
- "Gyroscope (0xBC4) integrator; Myomers coupling (gates on owning-player @mech+0x190); 0xBD3 damage-hub untangle (WAVE 8)"
- "Myomers coupling (gates on owning-player @mech+0x190); 0xBD3 damage-hub untangle (WAVE 8)"
---
# Subsystems
@@ -35,7 +35,7 @@ Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER
`static_assert`-locked (the P7 alarm-unification: HeatSink 0x1D0 → PoweredSubsystem 0x31C → MechWeapon
0x3F0 → Emitter 0x478). [T2]
## Reconstruction waves (state: ~18-19 of 20 factory cases wired to real classes)
## Reconstruction waves (state: 19 of 20 factory cases wired to real classes — only 0xBD3 remains, the MechControlsMapper squatter)
- **WAVE 1** — HeatableSubsystem re-based onto MechSubsystem (de-shadow cascade).
- **WAVE 2** — heat family (Condenser/AggregateHeatSink/Reservoir) + HUD + MechTech (un-swap).
- **WAVE 3** — power bus (Generator/PoweredSubsystem) + Emitter/PPC fire-path (end-to-end fire; heat
@@ -46,7 +46,10 @@ Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER
- **WAVE 6** — Myomers (mover-coupled; structural un-stub is INERT — no live mover/heat coupling).
- **WAVE 7** — projectile/missile weapons (byte-exact; flying projectiles are a PORT reconstruction —
the 2007 Entity is 0x1BC vs the binary's 0x300, so raw base-offset reads fail).
- **DEFERRED** — Gyroscope 0xBC4 (NaN integrator); SubsystemMessageManager 0xBD3 (WAVE 8 — a
- **TASK #56 (2026-07-11)** — Gyroscope LIVE byte-exact (ctor @004b3778, sizeof 0x3D0 locked;
integrators + `FUN_004b2980` damage fan-out; joint dispatch from the Mech performance tail
@0x4aaf74/83; `BT_GYRO_LOG`/`BT_GYRO_TRACE`) — see [[cockpit-view]].
- **DEFERRED** — SubsystemMessageManager 0xBD3 (WAVE 8 — a
**damage/explosion consolidation hub** cached to `Mech[0x10d]`=0x434; recon MISLABELS the slot
`controlsMapper` + the drive squats there while the real mapper is roster slot 0, so wiring it needs
an untangle. NOT the valve/advanced-damage gate — that's the owning **BTPlayer** @mech+0x190, see
+5 -2
View File
@@ -138,8 +138,11 @@ the world-anchored `BTStartWarpEffect(x,y,z)` (mechdmg.cpp). The peer path is a
## Env gates (all default to the verified values)
`BT_WARP_EYE_UP=8.25` · `BT_WARP_SPIN=4` · `BT_WARP_TWIST=0` · `BT_WARP_CONTRAST=1.0` ·
`BT_WARP_LO_R/G/B=0.15/0.12/0.35` · `BT_WARP_HI_R/G/B=0.98/0.94/1.0` · `BT_WARP_BLUR=0` ·
`BT_WARP_TESS=3` · `BT_WARP_MIP=1` · `BT_WARP_TILE_U=1` · `BT_WARP_RADFREQ=1` · `BT_WARP_ANISO=0` ·
`BT_WARP_CULL=cw` · `BT_WARP_TINT=0` · `BT_WARP_TILT=0` · `BT_TLOC_LOG` (lifecycle log) ·
`BT_WARP_TESS=3` · `BT_WARP_MIP=1` · `BT_WARP_TILE_U=1` · `BT_WARP_RADFREQ=1` (`BT_WARP_TILE_V` is an
accepted alias — bgfload.cpp:665) · `BT_WARP_ANISO=0` · `BT_WARP_CULL=cw` · `BT_WARP_TEXMOD=0`
(diag: =1 re-enables the TEXTURE×DIFFUSE modulate — double-tints; btl4vid.cpp:2640) · `BT_WARP_TILT=0` ·
`BT_WARP_SCALE` (btl4vid.cpp:2263-2266, collapse =v / expand =1.5·v) · `BT_WARP_NOSCROLL` (diag,
btl4vid.cpp:2373) · `BT_TLOC_LOG` (lifecycle log) ·
`BT_WARP_SELFTEST` / `BT_WARP_SELFSHOT` (DIAG capture harness — off by default; retained for visual verification of the open peer/collapse work).
## Key files
+2 -3
View File
@@ -15,12 +15,11 @@ on top of it. Full detail: `docs/PROGRESS_LOG.md §5b, §8`.
## What WinTesla already did (do NOT rebuild)
- **Renderer bypass: DONE**`MUNGA_L4/L4D3D.cpp` + `DXUtils` replace libDPL / the IG board with
Direct3D9. No `libdpl.lib` in the tree. (Our from-scratch `port/` D3D9 viewer is now a
reference/sandbox, not the path.) [T1]
Direct3D9. No `libdpl.lib` in the tree. (The early from-scratch D3D9 viewer was retired and removed.) [T1]
- **Audio: DONE** — OpenAL + libsndfile replacing the HMI "SOS" engine. [T1]
- **L4 HAL on Windows + the VS build system: DONE.** [T1]
- **Red Planet game logic: COMPLETE & buildable**`VTV/VTVMPPR/WEAPSYS/...`. RP is at/near playable. [T1]
- **Multiplayer: the DOS `NETNUB` driver is already replaced by a 3446-line WinSock2 TCP
- **Multiplayer: the DOS `NETNUB` driver is already replaced by a ~3.5k-line WinSock2 TCP
reimplementation** (`MUNGA_L4/L4NET.CPP`) + the master/replicant distributed-sim core. See
[[multiplayer]]. [T1]
+90
View File
@@ -126,6 +126,36 @@ DIV-BIZ2:
related_terms: [BGF, BMF, PFBIZTAG]
related_topics: [bgf-format, asset-formats]
PMESH:
kind: format
definition: The BGF mesh-container chunk inside a PATCH (geogroup) — interleaved float32 vertices plus faces carried in CONN and/or PCONN lists.
related_terms: [BGF, CONN, PCONN]
related_topics: [bgf-format]
CONN:
kind: format
definition: BGF CONNECTION_LIST (0x0047) — a FLAT TRIANGLE LIST (3 indices/face; corpus-verified n%3==0), NOT "one polygon, fan-triangulate". COEXISTS with PCONN in one pmesh (370/841 GEO models mix them — dropping the CONN triangles was a real bug).
related_terms: [BGF, PMESH, PCONN]
related_topics: [bgf-format]
PCONN:
kind: format
definition: BGF PCONN_LIST — packed quad/hex faces of a PMESH (quads must be triangulated). Can coexist with loose CONN triangles in the same pmesh.
related_terms: [BGF, PMESH, CONN]
related_topics: [bgf-format]
LOD:
kind: concept
definition: Level-of-detail band selection in BGF models. Each LOD chunk's 0x2046 header = a [near..far) viewing band; the board drew the LOD whose band contains the camera distance. ADDITIVE_LODS models instead draw EVERY LOD with d < OutDist (near detail adds onto coarser massing).
related_terms: [BGF, SV_SPECIAL, DIV-BIZ2]
related_topics: [bgf-format]
SV_SPECIAL:
kind: format
definition: The BGF name-driven special-behavior token chunk (0x2037) — dz_* damage/articulation tokens, object-level ADDITIVE_LODS (draw-all-LODs arena structures), patch-level PUNCH (dpl_Punchize stencil-cut, e.g. the cockpit canopy windows).
related_terms: [BGF, DIV-BIZ2, LOD]
related_topics: [bgf-format]
BMF:
kind: format
definition: DIV-BIZ2 (FILETYPE=1) material+texture LIBRARY (no pixels). Material -> DIFFUSE color + MATERIAL_TEXTURE -> texture -> TEXTURE_MAP image basename. Tag 0x18 BITSLICE selects a BSL sub-image; 0x28 ramp ref; 0x26 emissive.
@@ -138,6 +168,12 @@ BSL:
related_terms: [BMF, VTX, TGA, material-ramp]
related_topics: [asset-formats, rendering]
VTX:
kind: format
definition: DIV-VTX2 — a small texture container with trailing raw RGB pixels (e.g. VIDEO/TEX/BINTA.VTX, 64x64 RGB, pixels at offset 57 — the translocation-warp gradient). One of the three texture-pixel sources alongside .tga and .bsl.
related_terms: [BSL, BMF]
related_topics: [asset-formats, translocation-warp]
SKL:
kind: format
definition: INI joint skeleton. [joint] parent=, Type=(hingex/y/z|ball|balltranslate), Object=part.bgf, dzone=, tranx/y/z, pitch/yaw/roll. Build a DCS tree; neutral pose ~ translations.
@@ -186,6 +222,12 @@ VMF:
related_terms: [BMF, BSL, beam]
related_topics: [rendering]
beam:
kind: concept
definition: The weapon discharge visual drawn from the Emitter sim state (Emitter::FireWeapon builds a muzzle->target beam). The dpl_* beam renderable was never ported — the port draws it via BTPushBeam/BTDrawBeams (L4VIDEO), the ermlaser.bgf tube with the scrolling bexp grit (.VMF SCROLL); ER lasers red, PPC blue, standard yellow.
related_terms: [VMF, DPL, BGF]
related_topics: [rendering, combat-damage]
material-ramp:
kind: concept
definition: The IG board colours no-normal geometry (terrain/mesas/sky/buildings/mech) by a material's 2-endpoint RAMP (dpl_SetMaterialRamp) — texture luminance indexes a low->high gradient. Normal-bearing geometry (vehicles) is LIT instead. Applying the ramp to lit geometry = the "white blobs" bug.
@@ -213,6 +255,12 @@ EntitySegment:
related_terms: [joint, subsystem-roster, GetSegment]
related_topics: [locomotion, combat-damage]
siteeyepoint:
kind: concept
definition: The skeleton SITE segment naming the cockpit eye camera mount — a child of jointeye (WriteMechJoint drives jointeye - translation = the eye-position spring, rotation = the body-tip spring). The eye ctor caller (part_014.c:5525-66, BTL4VID.CPP) gates on this segment name and mounts the eye via the segment's GetBaseOffset() under its PARENT draw component.
related_terms: [EntitySegment, joint, SKL]
related_topics: [cockpit-view]
subsystem-roster:
kind: concept
definition: subsystemArray @mech+0x128 (count @0x124) — the inherited Entity table of the mech's Subsystem* instances (heat/weapons/gyro/sensor/...). A +0x128-style owner offset in subsystem code is the ROSTER, NOT the segment table (a classic bug source).
@@ -225,6 +273,12 @@ subsystem:
related_terms: [subsystem-roster, factory, ClassID, PoweredSubsystem]
related_topics: [subsystems, decomp-reference]
PoweredSubsystem:
kind: class
definition: Subsystem base class drawing power from the generator chain (ClassID 0xBC2, ctor @004b0f74, sizeof 0x31C). The heat leaf chains PoweredSubsystem:HeatSink:HeatableSubsystem:MechSubsystem — Emitter/PPC/Sensor/Myomers/weapons descend here. Publishes InputVoltage to the gauge system.
related_terms: [subsystem, ClassID, gauge]
related_topics: [subsystems, decomp-reference]
factory:
kind: concept
definition: The Mech-ctor per-class subsystem factory (switch on resource ClassID). The case LABELS are systematically MISLABELED — the real class is the ctor-address comment reconciled via CLASSMAP.md, not the case name. Each real class wired via a Create<Class>Subsystem bridge.
@@ -237,6 +291,18 @@ ClassID:
related_terms: [factory, subsystem, CLASSMAP]
related_topics: [subsystems, decomp-reference]
SequenceController:
kind: class
definition: The BT animation clip player driving a joint channel (seqctl.cpp; SelectSequence@004277a8, Advance@0042790c). Two embedded on the Mech, 0x60 bytes each — legAnimation@0x65c (the LOCAL sim - travel + the displayed pose) and bodyAnimation@0x6bc (the replication projector feeding update records).
related_terms: [ANI, RootTranslation, joint]
related_topics: [locomotion]
MechControlsMapper:
kind: class
definition: The roster slot 0 subsystem mapping pilot controls -> demands (mechmppr.cpp @004afbe0; btl4mppr.cpp mappers) — interprets input into speedDemand/turnDemand, the LEG channel's live source and the update-record speedDemand feed. NOTE recon MISLABELS Mech[0x10d] (+0x434) as controlsMapper; that cache is the SubsystemMessageManager — the real mapper is roster slot 0.
related_terms: [subsystem-roster, RIO, SequenceController]
related_topics: [locomotion, pod-hardware]
master:
kind: concept
definition: A locally-simulated entity (the pod's own mech + spawned dummies). Ticks its full drive/subsystems and EMITS network update records. Contrast replicant.
@@ -249,6 +315,12 @@ replicant:
related_terms: [master, dead-reckon, multiplayer]
related_topics: [multiplayer]
dead-reckon:
kind: concept
definition: The replicant motion model — extrapolate from the last network update record and converge toward the master's replicated origin (engine DeadReckon; velocity feed = projectedVelocity/worldLinearVelocity). Replicant TRAVEL stays DeadReckon; the leg channel animates joints only (task #50).
related_terms: [replicant, master, SequenceController]
related_topics: [multiplayer, locomotion]
viewpoint-entity:
kind: concept
definition: application->GetViewpointEntity() — the local pod's own mech (the cockpit view + player controls). Gate player-only logic on `this == GetViewpointEntity()` (Mech::PerformAndWatch runs for EVERY mech).
@@ -312,6 +384,12 @@ methodDescription:
related_terms: [gauge, attribute-pointer, GaugeRenderer]
related_topics: [gauges-hud]
gauge:
kind: concept
definition: A cockpit instrument (widget) rendered by the GaugeRenderer onto the pod's MFD surfaces. Built from gauge\l4gauge.cfg keywords (each resolving to a methodDescription) and bound to game state by NAME via the attribute-pointer system.
related_terms: [GaugeRenderer, methodDescription, attribute-pointer, MFD]
related_topics: [gauges-hud]
GaugeRenderer:
kind: class
definition: The engine gauge system. BuildConfigurationFile parses gauge\l4gauge.cfg via GaugeInterpreter (Initialize->GetProcedureBody->ParsePrimitive). Builds LAZILY -> a too-early process kill shows a false "not built". Composites onto the pod's SVGA16 bit-plane surfaces.
@@ -324,12 +402,24 @@ MFD:
related_terms: [gauge, GaugeRenderer, SVGA16, pod]
related_topics: [gauges-hud, pod-hardware]
SVGA16:
kind: class
definition: The pod's 16-color SVGA gauge surface — ONE shared pixelBuffer the MFD bit-plane masks overlay. SVGA16::BuildWindows makes a fullscreen device + window per gauge/MFD surface on the pod's multi-adapter hardware; SVGA16::DrawDevSurface renders the off-pod dev composite (BT_DEV_GAUGES).
related_terms: [MFD, gauge, GaugeRenderer, pod]
related_topics: [pod-hardware, gauges-hud]
damage-zone:
kind: class
definition: Mech__DamageZone (engine DamageZone base). Armor model: damageLevel += damageAmount*damageScale[type], accumulates to 1.0 (destroyed). Built by the Mech ctor from the DamageZoneStreamResourceType (0x14) resource. Aimed hits route via Entity::TakeDamageMessage.
related_terms: [DZM, TakeDamage, DZ]
related_topics: [combat-damage]
TakeDamage:
kind: concept
definition: The Entity damage message — Entity::TakeDamageMessageID = 18 (NOT 21; runtime-confirmed). Entity::TakeDamageMessage(id, size, inflictingEntityID, zone, Damage&) -> target->Dispatch; the base handler IGNORES zone==-1, the Mech override (@0x4a037a) cylinder-maps unaimed hits and routes damageZones[zone]->TakeDamage.
related_terms: [damage-zone, replicant, master]
related_topics: [combat-damage, multiplayer]
ground-model:
kind: concept
definition: The authentic 1995 vertical-position model (FUN_004a9b5c). NO GRAVITY — an ABSOLUTE per-frame ground SNAP via the zone BoundingBoxTree (FindBoundingBoxUnder), lift = 5% of volume X-width. Walls block by FULL FRAME REJECTION; crushable props pass with a crunch.