KB housekeeping sweep: end-of-arc staleness purge (2026-08-06)
Corrections (wrong claims fixed + swept): - open-questions: the DuckState 'no CODE consumer, authentic [T1]' verdict RETRACTED (consumer = the unexported posture block; squat clips shipped in BTL4.RES all along); the searchlight fog swap re-marked DONE (was 'STILL DEFERRED'); crouch index pointer updated. - combat-damage: old task-#60 kill-score section marked SUPERSEDED by the report tail (BTPostKillScore retired). - pod-hardware: crouch census rows -> COMPLETE; gauges-hud: 07-20 note (generators 'unreconstructed') re-swept. New knowledge: - reconstruction-gotchas #20: EXPORT-GAP BLINDNESS (4 incidents, 2 falsely T1; the byte-scan/named-member/RES-TOC/manifest checklist) + the verification-viewpoint bullet in #13 (the crouch capture errors). - decomp-reference: master posture/crouch block offsets (+0x3f8/+0x79c, the myomers cluster) + 8 new bench env gates. - rendering: beam-material decode (btfx brighten) + look-pass acceptance. - experience-levels: crouch novice lockout in the +0x25c consumer list. - build-and-run: the stale-link build ritual; test-harness: bench-script gotchas (both previously oral tradition). - glossary: export-gap-blindness; multiplayer: crouch/searchlight ride existing records; subsystems: searchlight visuals pointer. Status refresh: CLAUDE.md front + project-overview current-state -> core gameplay reconstruction COMPLETE (774->801), polish phase, #60 census next. Ledger addenda: INPUT_PATH row 6 complete; RESPAWN_REARM VehicleDead-sender note. .gitignore: content/ bench artifacts (pngs/logs/eggs/exe) out of status. checkctx CLEAN. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5e17bb2262
commit
f49f35cd43
@@ -53,3 +53,11 @@ scratchpad/night*/*.zip
|
||||
# (deliberate exceptions: git add -f)
|
||||
scratchpad/night*/*notes*.txt
|
||||
scratchpad/night*/lastrun*.txt
|
||||
|
||||
# Bench artifacts in content/ (screenshots, session logs, generated eggs, deployed exe)
|
||||
# -- receipts stay on disk, out of `git status`. A new AUTHORED egg: git add -f.
|
||||
content/*.png
|
||||
content/*.log
|
||||
content/matchlog_*.txt
|
||||
content/btl4.exe
|
||||
content/*.EGG
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
`BTL4OPT.EXE` binary on top of the working WinTesla engine.
|
||||
**Repo of record:** the top-level `CMakeLists.txt` + `README.md` build `btl4.exe`. Layout:
|
||||
`engine/ game/ content/ docs/ reference/ tools/ context/`.
|
||||
**Current front:** `btl4.exe` runs a full single-player loop; the gauge system is complete; the
|
||||
active work is reconstructing each subsystem's authentic behavior from the binary. Details +
|
||||
what's-next: `context/project-overview.md`, `context/open-questions.md`, recent git log.
|
||||
**Current front (2026-08-06):** core gameplay reconstruction is COMPLETE — SP+MP loop, combat/
|
||||
damage/scoring, locomotion (gait/gimp/CROUCH), night kit (searchlight), death/respawn, replication
|
||||
all authentic + benched (builds 4.11.774→801). Active work: the POLISH list + the #60 export
|
||||
gap census. Details + what's-next: `context/project-overview.md`, `context/open-questions.md`,
|
||||
recent git log.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -182,6 +182,18 @@ default (`-DBT_STEAM=ON` is the documented dev-checkout state). `BT_EXPIRE=ON` (
|
||||
14-day tester window; an expire-OFF zip is renamed `-noexpire` and warns. Verify by extracting the
|
||||
zip somewhere clean and booting it with no repo present — that is what catches a missing runtime DLL.
|
||||
|
||||
## Build ritual — the stale-link flake (bit 4+ times; MANDATORY for bench work)
|
||||
MSBuild often does NOT relink `btl4.exe` when only static-lib members changed — the bench then
|
||||
runs a STALE exe and the session burns hours on phantom results. Ritual for every code-change build:
|
||||
1. `rm -f build/Release/btl4.exe` FIRST (force the relink);
|
||||
2. build; a REAL relink prints the 20 known-benign `CreateStreamedSubsystem` LNK2019s (`/FORCE`
|
||||
baseline — see the top of this file); an up-to-date run prints none;
|
||||
3. STRING-VERIFY before benching: `python -c "d=open(r'build/Release/btl4.exe','rb').read();
|
||||
print(b'<A-NEW-LOG-MARKER>' in d)"` — pick a string your change added (NB: release strings
|
||||
carry LOG TEXT, not function names — check the .map for symbols);
|
||||
4. use ABSOLUTE paths in every bench command — `cd` leaks between compound commands (the cwd
|
||||
trap) and has broken builds/appends/copies repeatedly.
|
||||
|
||||
## Key Relationships
|
||||
- Base: [[wintesla-port]] (the engine build recipe).
|
||||
- Verify loop: [[reconstruction-method]]; env gates: [[decomp-reference]] §6.
|
||||
|
||||
@@ -953,7 +953,9 @@ stub let it fire and corrupt `graphicAlarm`. Now `mech->IsDisabled()` — the la
|
||||
defence-in-depth. Sibling fix: `mechdmg.cpp:451` read the phantom `stance` (perma-0) instead of
|
||||
`MovementMode()`, so the leg-shot-out → `graphicAlarm=9` fall/death branch was entirely DEAD; now live.
|
||||
|
||||
## Kill-score damage (task #60)
|
||||
## Kill-score damage (task #60) — ⚠ SUPERSEDED 2026-08-05 by §THE AUTHENTIC SCORE/DEATH REPORT
|
||||
## TAIL above (`BTPostKillScore` is RETIRED; the kill basis is now the applied damage TALLY +
|
||||
## the victim role's killBonus, posted by the victim's own TakeDamage handler). History below.
|
||||
`BTPostKillScore` (btplayer.cpp:1491) feeds the ScoreMessage `damageAmount` into the kill award
|
||||
(`@0x4c02e4` → `(damageAmount + scoreAward) × roleScalar × teamMult × tonnageRatio`, `@0x4c052c`).
|
||||
The port passed a flat `kShotDamage=12` (mech4.cpp:1551), so every kill scored identically regardless
|
||||
|
||||
@@ -176,6 +176,22 @@ rows until sanity fails (`python + struct`, see the session commits `cc2b109`/`2
|
||||
|
||||
---
|
||||
|
||||
### Master posture / crouch block (FUN_004a9b5c region, raw disasm 2026-08-05/06) [T1]
|
||||
- mech+0x3f8 `mapPosture` (0 none / 1 may-duck / 2 holding-squat) — selector @0x4a9f61-0x4aa007:
|
||||
gates = movementMode normal, simLive (player+0x25c, NOVICE lockout), leg alarm 0/4 → 1, leg
|
||||
alarm 1 → 2, myomer factor |x|>1e-4 (const @0x4ab16c).
|
||||
- mech+0x79c `myomerEffectiveness` — MAX over the myomer chain's speedEffect@0x31C, published by
|
||||
the mapper drive-scale block (mechmppr.cpp:990 == @0x4a9cf2-0x4a9da4); scales speedDemand,
|
||||
zeroes turnDemand at ~0 (the seek-4 freeze), and gates the crouch.
|
||||
- DuckRequest consumer @0x4aa011-0x4aa0af: duckState && squatCapable → SetLegAnimation(2 'sqd')
|
||||
/ (3 'squ'), ForceUpdate(8)+(1) (type-3 record ships legState → peers pose it), stability
|
||||
alarm 0/1, request consumed. Clips: animationClips[2]=sqd(i), [3]=squ(i) (BTL4.RES, all 8
|
||||
chassis; loader slots byte-verified vs @0x50d9c8 suffix pool).
|
||||
- Myomers cluster: Performance wrapper @004b8b9c → drive-heat integrator @004b8be3;
|
||||
AvailableOutput @004b8ac0 = gear clamp × QUADRATIC heat degrade (+0x114 vs +0x118/+0x11c band)
|
||||
× (1 − legZoneDamage); speedEffect = output / ownerBaseSpeed(+0x34C) — unclamped (gear-4
|
||||
supercharge ≈1.43).
|
||||
|
||||
## 4. Damage delivery
|
||||
|
||||
- `Entity::TakeDamageMessage(id, size, inflictingEntityID, zone, Damage&)` → `target->Dispatch(&msg)`.
|
||||
@@ -531,6 +547,13 @@ default-ON (`'0'` disables).
|
||||
| Var | Effect |
|
||||
|---|---|
|
||||
| `BT_FORCE_THROTTLE` | auto-walk forward (no key) |
|
||||
| `BT_BTNTEST=addr,on,off` / `BT_BTNTEST2=...` | scripted screen-button press/release cycles at poll counts (the REAL RIO seam; 2nd cycle for e.g. squat→rise) |
|
||||
| `BT_DUCK_LOG` | crouch diagnostics: RIO press mode-mask, SetLegAnimation re-arm tracer (+ra), 1 Hz joint probe, posture/squat events |
|
||||
| `BT_TREE_LOG` | dump the built render-tree topology (segment/joint/parent, SITE rows) at mech build |
|
||||
| `BT_SPOT_SELF` | bench-only: build the searchlight cone on the OWN-cockpit tree so `BT_CAM=face` can eyeball it solo |
|
||||
| `BT_CAM=face` | chase camera in FRONT looking back (the old animation view); `BT_CAM_Y/Z` offsets |
|
||||
| `BT_FOG_LOG` | log SetFogStyle transitions + fog page resolution (searchlight swap forensics) |
|
||||
| `BT_GIMP_SPEED` | override the gimp-gait demand scale (1.0 = authentic) |
|
||||
| `BT_SPAWN_ENEMY` | spawn a target mech 120u ahead along the spawn facing |
|
||||
| `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 |
|
||||
|
||||
@@ -42,7 +42,9 @@ then unconditionally `+0x264 = +0x268 = mission->advancedDamageOn(+0xf0)` (both
|
||||
|
||||
## What each flag gates (binary consumers, all reached via `mech+0x190` → BTPlayer) [T1]
|
||||
|
||||
- **+0x25c — "sim live", off only for novice.** Consumers: ballistic jam roll `CheckForJam`
|
||||
- **+0x25c — "sim live", off only for novice.** Consumers: the CROUCH posture selector
|
||||
(master-perf @0x4a9f70: novice → mapPosture 0 → squat/rise refused — 2026-08-06,
|
||||
[[locomotion]] §CROUCH); ballistic jam roll `CheckForJam`
|
||||
@4bbfcc early-returns NO-JAM when 0 (projweap.cpp calls this `LiveFireEnabled`); **ThermalSight**
|
||||
`ToggleLamp` @4b860c (thermalsight.hpp `ControlsAllowLights`) — ⚠ **CORRECTED 2026-07-25 (#61):
|
||||
this was listed as *Searchlight's* gate. It is not. Raw disassembly of Searchlight's real handler
|
||||
|
||||
@@ -11,7 +11,7 @@ open_questions:
|
||||
- "SeekVoltageGraph RECONSTRUCTED 2026-07-19 (Gitea #11, was #10 finding A): full widget landed (see §SeekVoltageGraph below) -- ghosts gone steady-state (BT_PRESET_HOLD verification); remaining polish: a 1-frame transition artifact when a BT_SHOT lands on the exact page-switch frame (label BecameActive vs the graph's next rated Execute -- same lag class as the binary; self-heals next frame)"
|
||||
- "Secondary-view cycling RESOLVED 2026-07-19 (Gitea #6): the selector is the DISPLAY mode (CycleDisplayMode -> vtbl+0x4C override @4d1ae4), NOT CycleControlMode; desktop 'N' / pad RightThumb wired; pixel-verified dama->crit->heat"
|
||||
- "Upper-MFD PRESET pages RESOLVED 2026-07-19 (Gitea #9): SetPresetMode table @0051dbf0 re-decoded (little-endian -> ModeMFD bits 0-14), per-MFD pod button banks identified from the .CTL dump, desktop J/K/L cycle wired"
|
||||
- "Always-active msg-4 records IDENTIFIED 2026-07-20 (glass input audit): 0x2C = Reservoir InjectCoolant (the flush button), 0x2F/0x2E/0x2D/0x2B/0x2A/0x29 = Condenser1-6 MoveValve, 0x1A-0x1D = GeneratorA-D ToggleGeneratorOnOff (@0050fb90, unreconstructed); plus 0x13 = Mech DuckRequest (crouch), 0x28 = Mech BalanceCoolant, 0x12/0x14 = ThermalSight/Searchlight toggles -- see pod-hardware.md + docs/GLASS_COCKPIT.md 2026-07-20"
|
||||
- "Always-active msg-4 records IDENTIFIED 2026-07-20 (glass input audit): 0x2C = Reservoir InjectCoolant (the flush button), 0x2F/0x2E/0x2D/0x2B/0x2A/0x29 = Condenser1-6 MoveValve, 0x1A-0x1D = GeneratorA-D ToggleGeneratorOnOff (@0050fb90; wired 2026-07-25, powersub.cpp); plus 0x13 = Mech DuckRequest (CROUCH -- COMPLETE 2026-08-06, [[locomotion]]), 0x28 = Mech BalanceCoolant, 0x12/0x14 = ThermalSight/Searchlight toggles (searchlight visuals done 2026-08-05) -- see pod-hardware.md + docs/GLASS_COCKPIT.md; statuses re-swept 2026-08-06"
|
||||
- "MP DEATHS resolved 2026-07-12 (observed-death tally + display clamp); remaining: verify multi-death tallies stay in sync across a long session (GAUGE_COMPOSITE.md)"
|
||||
---
|
||||
|
||||
|
||||
@@ -885,6 +885,9 @@ transition, HUD all landed since P6): console egg → mesh → RunningMission on
|
||||
report tail in the victim's TakeDamage handler — BTMechPostCombatReports, replacing
|
||||
BTPostKillScore; same cross-node reroute, see [[combat-damage]] §report tail). Respawn is
|
||||
reconstructed (task #52 — see item 3b; the VehicleDead sender moved to the same tail).
|
||||
CROUCH and the SEARCHLIGHT replicate with ZERO new wire code (2026-08-05/06): the squat rides
|
||||
the existing type-3 state record (legState) and the lamp rides subsystem record 0x14
|
||||
(lightState) — [[locomotion]] §CROUCH / [[rendering]] §SEARCHLIGHT.
|
||||
|
||||
## Key Relationships
|
||||
- Base: [[wintesla-port]] (L4NET). Depends on: [[locomotion]] (update writer), [[combat-damage]]
|
||||
|
||||
+20
-19
@@ -224,16 +224,22 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR
|
||||
⚠ This entry was stale in BOTH directions for weeks (`docs/INPUT_PATH_AUDIT.md` flagged it) —
|
||||
most of the list had been reconstructed while the prose still called it dead. Check the code,
|
||||
not the census, before reconstructing anything here.
|
||||
- **`DuckState` has no CODE consumer, and that is authentic [T1].** `Mech::DuckRequest` sets
|
||||
`duckState`(attr 0x37, binary `mech+0x398`) to 1 and nothing else: the flag has exactly two
|
||||
writers in the whole binary (that handler and the mech reset) and ZERO readers. Its consumer is
|
||||
a DATABINDING — `content/GAUGE/L4GAUGE.CFG` drives a 3-frame `bduck.pcc` widget off `DuckState`
|
||||
on the map's legend column, verified live (the crouch icon lights grey→orange on a press). So
|
||||
the button is COMPLETE as a request flag + indicator. What is NOT known: whether the 1995 game
|
||||
ever consumed it for posture/collision (no SQUAT clip name survives in the decomp or in
|
||||
`content/`, only `DuckServo01.wav` in AUDIO1.RES). Do not invent a crouch pose to "finish" it.
|
||||
- **Searchlight-driven fog swap — STILL DEFERRED, but the "ORIGINAL 1995 LATENT BUG" premise is
|
||||
❌ RETRACTED (2026-07-25, #61).** The arcade swaps fog between `fog=` (lights on) and
|
||||
- **❌ RETRACTED (2026-08-06): "`DuckState` has no CODE consumer, and that is authentic [T1]".**
|
||||
That verdict — and its corollary "no SQUAT clip survives; do not invent a crouch pose" — was
|
||||
EXPORT-GAP BLINDNESS ([[reconstruction-gotchas]] §20, incident 2), and the [T1] tag was
|
||||
unearned: "ZERO readers" was true of the *export*, not the binary. The reader is the master-perf
|
||||
posture/duck block in the un-exported region (@0x4aa011-0x4aa0af, raw disasm), and the squat
|
||||
clips DID ship — `<pfx>squ/sqd` + interior `squi/sqdi` for all 8 chassis live in **BTL4.RES**
|
||||
(the old claim searched decomp strings + loose `content/` files, never the RES TOC;
|
||||
`DuckServo01.wav` was the tell). CROUCH is now fully reconstructed — request latch → posture
|
||||
arbiter (novice + myomer + mode gates) → squat/hold/rise, MP-replicated on the type-3 record
|
||||
([[locomotion]] §CROUCH). The `bduck.pcc` legend databinding observation stands and still works.
|
||||
- **✅ Searchlight-driven fog swap — DONE 2026-08-05 (412053d/b75bb4a; [[rendering]] §SEARCHLIGHT).**
|
||||
The port's `TickSearchlight` (btl4vid) is the `PullFogRenderable` equivalent: LightOn →
|
||||
`SetFogStyle(searchLightOn/OffFogStyle)` with the binary's inverted-cache seed (authentic DARK
|
||||
night start), plus the external spot.bgf beam cone (btfx `brighten` additive veil) on the
|
||||
searchlight site joint, MP-replicated. Historical attribution notes kept below — the
|
||||
"ORIGINAL 1995 LATENT BUG" premise was ❌ RETRACTED 2026-07-25 (#61).** The arcade swaps fog between `fog=` (lights on) and
|
||||
`nosearchlightfog=` (off) via `PullFogRenderable` watching the Searchlight's `lightState`.
|
||||
**The old entry claimed the 1995 binary itself could never light the lamp. That was wrong** — it
|
||||
compared Searchlight's Performance (@004b841c, reads `requestedOn`@0x1E0) against **ThermalSight's**
|
||||
@@ -243,15 +249,10 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR
|
||||
un-pooled `"ToggleLamp"` strings adjacent to their own class names. Body: @004b838c sits in a Ghidra
|
||||
export gap (#60) but was **recovered by raw disasm** (`scratchpad/dis838c.py`) — it toggles 0x1E0 and
|
||||
carries no novice gate. All T1.]
|
||||
Searchlight's handler set is now WIRED and **verified live**: pad `0x14` → `requestedOn 0→1` →
|
||||
`lightState 0→1`. So the sim needs **no repair** and the previous "DECISION: faithful to the buggy
|
||||
original" is void — a working fog swap is now plain FAITHFUL reconstruction. Remaining work is a
|
||||
single item: construct `PullFogRenderable` at btl4vid.cpp `MakeMechRenderables` reticle-build/inside
|
||||
pass (== arcade part_014.c:5173, Dynamic, bound per Searchlight `lightState` via a new
|
||||
`LightStatePtr()` accessor). `ControlsAllowLights()` is WIRED since issue #2 to the `player+0x25c`
|
||||
not-novice experience flag via the BTPlayerExperienceSimLive bridge ([[experience-levels]]).
|
||||
See [[rendering]] fog section. (The pre-#61 "verified inert live: BT_FOG_LOG zero `SetFogStyle(2/3)`"
|
||||
observation still holds — reason (1), the un-constructed renderable, remains.)
|
||||
Searchlight's handler set is WIRED and **verified live**: pad `0x14` → `requestedOn 0→1` →
|
||||
`lightState 0→1`; the fog watcher + beam cone landed 2026-08-05 (see the header above), so no
|
||||
remaining work rides this entry. (NB the 1995 searchlight carries NO novice gate — that
|
||||
lockout is ThermalSight's; raw disasm @004b838c.)
|
||||
- **`HandleMessage` is vtable slot 8/9 in the binary but NON-virtual across the reconstruction --
|
||||
FILED AS GITEA #65
|
||||
(2026-07-25, found via #46).** Ten classes declare it (ammobin/heat×2/hud/mechsub/myomers/
|
||||
|
||||
@@ -54,9 +54,10 @@ button** (Reservoir InjectCoolant, hold-to-flush — works), **0x2F/0x2E/0x2D/0x
|
||||
the per-condenser VALVE buttons** (MoveValve, Cond1-6 — work), **0x1A-0x1D = Generator A-D
|
||||
ON/OFF** (`ToggleGeneratorOnOff` id 4, binary table @0050fb90 fn @004b1ed0 — ✅ **WIRED**,
|
||||
`powersub.cpp`). Newly decoded from the binary message tables: **0x13 → Mech `DuckRequest`
|
||||
(0x1a @0049fa00 — the manual's CROUCH button)** — ✅ **WIRED 2026-07-26**, the last handler in
|
||||
this census; press-only, sets `duckState` and the map legend's `bduck.pcc` widget lights (see
|
||||
[[open-questions]] for why it has no code consumer) —, **0x28 → Mech `BalanceCoolant` (0x16
|
||||
(0x1a @0049fa00 — the manual's CROUCH button)** — ✅ **COMPLETE 2026-08-06**: handler (07-26) +
|
||||
the master-perf posture CONSUMER + squat/rise clips + MP replication ([[locomotion]] §CROUCH;
|
||||
glass key F4); the map legend's `bduck.pcc` widget still lights. (The old "no code consumer"
|
||||
verdict was export-gap blindness — [[reconstruction-gotchas]] §20) —, **0x28 → Mech `BalanceCoolant` (0x16
|
||||
@0049f728)** ✅ **WIRED 2026-07-21 (#20)**, **0x12 → ThermalSight `ToggleLamp` (id 3, table @0x51120C fn @004b860c)** and **0x14 →
|
||||
Searchlight + Searchlight2 `ToggleLamp` (id 3, table @0x51117C fn @004b838c)** — ✅ **BOTH WIRED
|
||||
2026-07-25 (#61)**, previously default-constructed blackholes; verified live (0x14 →
|
||||
@@ -231,8 +232,8 @@ every checked control behavior:
|
||||
"Top Speed Gimped 40" also confirms the limp-gait speed cap as an authored spec. The port
|
||||
follows the BINARY (house rule); resurrecting manual-4.0 supercharge would be an opt-in
|
||||
deviation for the operator to decide.
|
||||
NEW LEADS (manual describes, port lacks input/UI): **CROUCH** (button by the secondary screen;
|
||||
`Mech::duckState` attr 0x37 + SQUAT clips exist, nothing drives them), ~~EJECT~~ **EJECT WIRED
|
||||
NEW LEADS (manual describes, port lacks input/UI): ~~CROUCH~~ ✅ **CROUCH COMPLETE 2026-08-06**
|
||||
([[locomotion]] §CROUCH), ~~EJECT~~ **EJECT WIRED
|
||||
2026-08-02** (core: `Mech::EjectPilotMessageHandler` id 0x19 @0049f854 + the crippled-mech
|
||||
permission evaluator @0049fa1c; input = binding-engine "Eject" action, Backspace / pad
|
||||
LeftThumb; the punch-out kills via graphicAlarm 10 ≥ 9 — KillBonus authors 0 in ALL shipped
|
||||
|
||||
+15
-13
@@ -39,19 +39,21 @@ sections in `docs/PROGRESS_LOG.md` cite old `C:/git/nick-games/...` paths — tr
|
||||
they map into this repo (reconstructed BT → `game/reconstructed/`, engine → `engine/MUNGA{,_L4}/`,
|
||||
content → `content/`, raw decomp → `reference/decomp/`). [T2]
|
||||
|
||||
## Current state (2026-07)
|
||||
`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 are done (WinTesla); AUDIO backend is now REAL
|
||||
(the repo's OpenAL32.dll + libsndfile-1.dll were both no-op STUBS — replaced with real OpenAL Soft +
|
||||
an in-tree WAV loader, 2026-07-15) and the 241-sample soundbank is cracked from AUDIO1/2.RES + loading;
|
||||
the ONLY remaining audio gap is game-triggering (no AudioEntities fire PlayNote yet), so gameplay is
|
||||
still silent apart from a proof-of-life hook (see [[wintesla-port]] Audio). 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]
|
||||
## Current state (2026-08-06) — core gameplay reconstruction COMPLETE; polish phase
|
||||
Field-tested nightly at 4-8 pods over Steam (builds 4.11.622→801). Authentic + benched: the full
|
||||
SP+MP loop; per-panel mesh-true targeting/damage (+ crits, cylinder lottery, armour darkening);
|
||||
the 1995 SCORING model (kill awards + received penalties + panic cost — the id-0x16 report tail);
|
||||
death (blast/splash, wreck, burial) + respawn (full re-arm audit); locomotion (two-channel gait,
|
||||
gimp limp, knockdowns, CROUCH F4); the night kit (fog-swap SEARCHLIGHT F5 + beam cone); heat/
|
||||
power/myomers (incl. the seek-4 freeze); replication (masters/replicants, kill/death columns,
|
||||
ghost+skate field detectors); the Steam wire seam + build gate; the operator console/relay
|
||||
lifecycle; gauges/HUD; audio (real OpenAL backend + the AUDIO_FIDELITY trigger waves — footsteps/
|
||||
gait/alarms/impacts live in the field). [T2]
|
||||
**Remaining (polish + infra):** the #60 export gap census (NEXT — export-gap blindness has caused
|
||||
4 wrong conclusions, [[reconstruction-gotchas]] §20); VehicleDead killed-by consumption (operator
|
||||
"X killed by Y"); burning-wreck handler (mech-0x17 label mismatch); the id-0x16 type-0 curiosity;
|
||||
MechRIOMapper Keypress @004d2514; marker beacon (LoadObject wrapper stub); DIV firmware
|
||||
intersection routine (deep-cut); deferred ledger items in [[open-questions]]. [T2]
|
||||
|
||||
## Key Relationships
|
||||
- Full detail: `docs/PROGRESS_LOG.md`.
|
||||
|
||||
@@ -278,6 +278,12 @@ Suspect ANY reconstructed per-frame code with narrow equality/window tests or `x
|
||||
state transitions: charge/seek loops, snap comparisons, timers compared with `==`.
|
||||
|
||||
## 13. Verification gotchas (don't fool yourself)
|
||||
- **Capture the viewpoint that can SEE the change.** The crouch "pose does not hold" and the
|
||||
false "eye does not drop" residual (2026-08-05/06) were BOTH capture errors: cockpit-view
|
||||
screenshots cannot show your own legs, and canopy-dominant diff crops mask eye-relative motion
|
||||
(the canopy drops WITH the eye — only the through-window ground shifts). Anchor pixel-diff
|
||||
crops on a region the effect MUST change, pair them with a state probe (joint values), and
|
||||
READ one frame with your own eyes before declaring a visual regression.
|
||||
|
||||
- **Lazy gauge build:** `GaugeRenderer::BuildConfigurationFile` runs LAZILY. A too-early process
|
||||
kill shows `[gskip]=0` / "not built" even though the widget is fine — **wait for the gauge
|
||||
@@ -543,6 +549,30 @@ segment tables) — the same class of latent overflow.
|
||||
0xFEEEFEEE=freed).
|
||||
5. For exhaustive multi-function analysis: a read-only Workflow (understand), then implement hands-on.
|
||||
|
||||
## 20. Export-gap blindness — absence in the EXPORT is not absence in the BINARY (4 incidents)
|
||||
The Ghidra export (`reference/decomp/`) has coverage gaps (#60), and BTL4.RES content never
|
||||
appears in it at all — so "no readers", "no caller", "no such clip/asset", and "unreconstructed"
|
||||
claims made by grepping the export or the port ALONE are structurally unsound. This class has
|
||||
produced four wrong conclusions, two of them [T1]-tagged at the time:
|
||||
1. **2026-07-25 searchlight "1995 latent bug"** — compared Searchlight's Performance against
|
||||
THERMALSIGHT's handler (wrong class) and invented a missing bridge; retracted (#61).
|
||||
2. **2026-07-2x `DuckState` "has no CODE consumer, authentic [T1]" + "no SQUAT clip survives"** —
|
||||
the consumer was the un-exported master-perf posture block (@0x4aa011), and the clips lived in
|
||||
BTL4.RES (`squ/sqd/squi/sqdi` × 8 chassis); the search covered decomp strings + loose files,
|
||||
never the RES TOC. Disproven by the CROUCH reconstruction (2026-08-06).
|
||||
3. **2026-07-31 morning: the myomer drive-scale DELETION** ("the binary has NO dynamic
|
||||
myomer→speed coupling") — the consumer was un-exported; restored same day by raw capstone.
|
||||
4. **2026-08-05 "the myomer factor FEEDER (@004b8be3) is unreconstructed"** — it had been fully
|
||||
reconstructed since 07-31 under NAMED members (`speedEffect`, `AvailableOutput`); the grep
|
||||
searched raw offsets. Cost: a duplicate multiply + a dead crouch gate until 08-06.
|
||||
**The rule — before claiming "X does not exist / is not reconstructed":**
|
||||
- byte-scan `content/BTL4OPT.EXE` for the offset/immediate (disp32 patterns), never just the export;
|
||||
- grep the port for NAMED members (check the .hpp for the offset's name) — offsets rot after promotion;
|
||||
- for content claims, walk the **BTL4.RES TOC** (`tools/resscan.py` pattern), not the loose tree;
|
||||
- check `reference/BT410_SOURCE_MANIFEST.md` + `game/reconstructed/CLASSMAP.md`;
|
||||
- and tag the claim's tier by the WEAKEST source consulted — an export-only sweep caps at [T4].
|
||||
The #60 gap census (dark-region inventory + a fresh Ghidra re-export) is the systemic fix.
|
||||
|
||||
## Key Relationships
|
||||
- Applies to: every topic that reconstructs a class ([[subsystems]], [[combat-damage]], [[gauges-hud]], [[locomotion]]).
|
||||
- Uses: [[decomp-reference]] (offsets/ClassIDs), [[reconstruction-method]] (the loop).
|
||||
|
||||
@@ -413,8 +413,15 @@ the VISUALS were the gap, decoded from MakeMechRenderables @004cef28 `case 0xbd8
|
||||
enables future site-hung effects). Wrecks go dark through the authentic gate (host shutdown
|
||||
forces lightState 0). Cross-node verified: B presses, A logs `[spot] cone SHOWN (seg 20)`.
|
||||
- Benches: `scratchpad/night12/searchfog.sh` (solo cockpit chain + red-fog end-to-end probe),
|
||||
`spotcone.sh` (2-node cone + replication). OPEN items in [[open-questions]] (cone look pass,
|
||||
the stubbed LoadObject wrapper + dormant marker case, the subtle authored deltas).
|
||||
`spotcone.sh` (2-node cone + replication). BEAM MATERIAL decoded 2026-08-06 (b75bb4a) [T1]:
|
||||
SPOT.BGF is a 7-vert cone ~50u forward and ~35° DOWN (a ground-pool lamp, not an air beam),
|
||||
verts tinted cyan-white; its material class `btfx:brighten.25` smuggles the ADDITIVE factor
|
||||
in DIFFUSE.r (0.25) with a warm emissive on the night page — the loader draws brighten*
|
||||
batches as an additive veil (dest += vertexRGB × factor, blend pass, unlit); tint compose
|
||||
(vertex cyan vs night emissive warm) [T3], flagged in the L4D3D draw branch. Look pass
|
||||
ACCEPTED by field eyeball 2026-08-06 (translucent beam reads correctly; solo rig
|
||||
BT_SPOT_SELF=1 + BT_CAM=face). Still open ([[open-questions]]): the stubbed LoadObject
|
||||
wrapper + dormant marker case, the subtle authored deltas.
|
||||
|
||||
## Key Relationships
|
||||
- Geometry/LOD: [[bgf-format]]. Base: [[wintesla-port]] (L4D3D). Shadow/visual-conform: [[locomotion]].
|
||||
|
||||
@@ -41,6 +41,9 @@ Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER
|
||||
- **WAVE 3** — power bus (Generator/PoweredSubsystem) + Emitter/PPC fire-path (end-to-end fire; heat
|
||||
conducts to the central sink via the linked-sink roster).
|
||||
- **WAVE 4** — standalone readouts: Sensor/Searchlight/ThermalSight/AmmoBin (de-shim, gate fixes).
|
||||
- ✅ **Searchlight VISUALS complete 2026-08-05** — cockpit fog swap + external spot.bgf beam
|
||||
cone, both MP-replicated; mountSegment@0x1DC identified (= resource segmentIndex, the cone's
|
||||
mount joint — was "commandedOn, role unidentified"). Full story: [[rendering]] §SEARCHLIGHT.
|
||||
- ✅ **Searchlight + ThermalSight buttons WIRED 2026-07-25 (#61)** — both classes' handler sets were
|
||||
default-constructed blackholes (systemic cause #1, `docs/INPUT_PATH_AUDIT.md`). Each now chains
|
||||
`PowerWatcher::GetMessageHandlers()` with its own id-3 `ToggleLamp`. **Verified live**: pad `0x14`
|
||||
|
||||
@@ -189,6 +189,21 @@ sleep <duration>; kill $relay; bt_kill_ours
|
||||
(one line, ungated) answer questions retroactively; per-frame traces stay
|
||||
gated.
|
||||
|
||||
## Bench-script gotchas (each has burned a session)
|
||||
- **Write benches CLEAN, never sed-derive a chain** — sed-derived copies silently dropped envs
|
||||
twice (the [replgimp] silence); export envs INSIDE the per-node subshell.
|
||||
- **Absolute paths everywhere; `cd /c/git/bt411` first** — the cwd trap (heredoc python + `cd
|
||||
scratchpad/nightN`) has broken later builds/appends with `fatal: pathspec`/`cannot stat`.
|
||||
- **No apostrophes/backticks through bash heredocs** — write a `.py` file and run it (quoting has
|
||||
mangled posted tracker comments and killed scripts mid-parse).
|
||||
- **Drain stale watchdogs before relaunching** — a leftover waiter's `taskkill` has killed a live
|
||||
bench mid-run.
|
||||
- **Scripted input goes through the REAL seam**: `BT_BTNTEST`/`BT_BTNTEST2` (RIO queue → mode-mask
|
||||
drain). In 2-node runs press at poll ≥900 — round-start jitter can eat earlier presses.
|
||||
- **Capture the viewpoint that can see the change** (gotcha #13 bullet): cockpit view can't show
|
||||
your own legs; pair screenshots with a state probe and READ a frame before concluding.
|
||||
- **Build ritual first** — see [[build-and-run]] §Build ritual (stale exe = phantom results).
|
||||
|
||||
## Key Relationships
|
||||
- Uses: [[build-and-run]] (parity, env gates, BT_SHOT capture) · [[experience-levels]] (expert vs novice gating)
|
||||
- Informs: [[reconstruction-method]] (step 4 "verify honestly" — this file is the how)
|
||||
|
||||
@@ -74,7 +74,7 @@ Ranked by impact. **Reverse thrust `0x3F` (key LALT / pad B) is FIXED** and conf
|
||||
| 3 | **pad LT / RT (turn pedals)** — and every pad-only session | Steer the legs | Not a wiring gap: the pedals are live (`L4PADRIO.cpp:710-715` → `L4CTRL.cpp:1438-1446` → `btl4mppr.cpp:1337-1340` → `:1399 pedalsPosition`) but only *Standard/Veteran* read them (`mechmppr.cpp:1034`, `:1061`); the mapper boots in **Basic** (`mechmppr.cpp:361`) — and **the pad has no binding for `0x18` CycleControlMode**. Verified: `content/bindings.txt:74-89` binds only `0x40 0x3F 0x47 0x42 0x44 0x43 0x41`; no `0x18`, no `0x15` | **S** | `BT_MPPR_TRACE=1`, squeeze LT on a pad-only launch: `pedals=` moves, `turn=0`. Add `pad BACK button 0x18`, press it → `[mode] control mode -> 1`, then LT yaws |
|
||||
| 4 | **Fire dropout: releasing one alias of a held fire button** (`1`+`SPACE` on `0x40`; `3`+`LCTRL`+`RCTRL` on `0x47`; pad A/X on the same) | Two keys on one address = two wires to one button | Edge state is per **binding**, not per **address**: `previousKeyHeld[k]` is indexed by binding index and `EmitButton(addr, held)` fires on every per-binding edge (`engine/MUNGA_L4/L4PADRIO.cpp:580-595`; same shape in the pad loop `:665-685`). The release writes `-(addr+1)` (`L4CTRL.cpp:2635-2647`) and `ControlsUpdateManager::Update` then suppresses the redundant press, so fire stays **off** until the held key is released and re-pressed | **M** (shared plumbing — **defer**) | `BT_FIRE_LOG=1`: hold LCTRL (missiles firing), tap `3`, release `3` → fire stops with LCTRL still down |
|
||||
| 5 | **`0x1A`-`0x1D`** Generator A-D ON/OFF (4 radar-rail buttons) | Take a generator off line (heat/power management) | `Generator : HeatSink` (`powersub.hpp:320-322`) and **no `Generator::GetMessageHandlers()` exists** — `powersub.cpp:1014` resolves to HeatSink's set `{id 3 ToggleCooling}`, so streamed `msg 4` (`ctrlmap.log:44-47`) finds nothing. `ToggleGeneratorOnOff` @`004b1ed0` is unreconstructed (0 hits in `game/`) | **M** | Click `0x1A`: today zero log. After: generator output voltage → 0 and its ENG-page bar follows |
|
||||
| 6 | **`0x13`** CROUCH (manual p8, a full section) | Duck to present a smaller target | Streamed `subsys -1 msg 0x1a` (`ctrlmap.log:40`) resolves to the Mech (`ENTITY.cpp:608-615`), but `Mech::MessageHandlerEntries` has exactly three entries — `TakeDamage`, `PlayerLink`, `BalanceCoolant` (`mech.cpp:463-467`). No id `0x1a`. Assets exist (`Mech::duckState` attr 0x37 `mech.cpp:861`, SQUAT clips, `DuckServo01.wav`) | **M** (reconstruct @`0049fa00`) | Click `0x13` on a stationary mech: expect the squat clip + `duckState 0→1` |
|
||||
| 6 | **`0x13`** CROUCH (manual p8, a full section) | Duck to present a smaller target | Streamed `subsys -1 msg 0x1a` (`ctrlmap.log:40`) resolves to the Mech (`ENTITY.cpp:608-615`), but `Mech::MessageHandlerEntries` has exactly three entries — `TakeDamage`, `PlayerLink`, `BalanceCoolant` (`mech.cpp:463-467`). No id `0x1a`. Assets exist (`Mech::duckState` attr 0x37 `mech.cpp:861`, SQUAT clips, `DuckServo01.wav`) | ~~M~~ ✅ **COMPLETE** (handler 07-26; consumer + clips + MP 2026-08-06 — [[locomotion]] §CROUCH) | Verified: squat clip plays, holds, rises; peers replicate it |
|
||||
| 7 | **Torso recenter from the keyboard** (`0x42` is pad/mouse only) | Center a twisted torso — the manual's anti-disorientation control (p9) | `0x42` itself is **LIVE** (streamed `subsys 17 attr 14 -> +0x208` = `Torso::centerCommand`, consumed `torso.cpp:636-640`) but `content/bindings.txt:83,86` bind it only on `pad Y`/`DPAD_UP`. The port's keyboard path (`gBTTorsoRecenter`, `mech4.cpp:2875`) is fed from the zeroed `gBTInput` and its consumer sits inside the stood-down bridge (`mechmppr.cpp:854-863`, gate `:655-660`) | **S** | Twist with `E`, press `X`: `currentTwist` does not return to 0. Add `key X button 0x42` (two rows on one VK both fire — `L4PADRIO.cpp:580-629` iterates all bindings) |
|
||||
| 8 | **Any clickable button latches if the mouse-up is lost** (worst on `0x3F` reverse, `0x40` trigger) | A momentary click always releases | Neither WndProc handles `WM_CAPTURECHANGED` or `WM_KILLFOCUS` — grep over `engine/` + `game/` returns **zero** hits. Alt-tab or a system dialog between press and release leaves `gCkPressed` set and `SetScreenButton(a,0)` never runs | **M** — trap: `btl4main.cpp:137` calls `ReleaseCapture()` itself, which re-enters `WM_CAPTURECHANGED`; guard on `gCkPressed != -1` (it is cleared before `ReleaseCapture`) | Click-hold `0x3F`, alt-tab, release outside the window: `BT_PAD_LOG=1` shows no release line and `rev` stays 1 |
|
||||
| 9 | **Every HOTAS / flight stick / rudder** (whole `joydev/joyaxis/joybutton/joyhat` grammar) | Drive the pod channels from a DirectInput device | `content/bindings.txt` is 89 lines and has **zero** joy rows; the joy poll is gated on `joyAxisBindingCount>0 \|\| …` (`L4PADRIO.cpp:775-777`) so `BTJoyInit/BTJoyPoll` are never called. `PadBindingProfile::Load` writes the default only when the file is **absent** (`L4PADBINDINGS.cpp:708-713`), and the file is gitignored (`.gitignore:23`) — so the zip ships **this machine's stale file** | **S** now (ship without it / refresh it) · **M** later (version marker + additive merge) | `content/jstest.log`: `bindings loaded: 40 keys, 10 pad buttons, 5 pad axes, 0 joy axes…`; no `[joy]` line exists in any log in `content/` |
|
||||
|
||||
@@ -271,3 +271,10 @@ Four operator/field reports were then grounded and dispositioned:
|
||||
|
||||
Bench: `scratchpad/night11/respawnreset.sh` (BT_VALVE_TEST press -> BT_MP_FORCE_DMG
|
||||
kill -> read `[valve]`/`[respawn]` on A, plume/un-wreck ordering on B).
|
||||
|
||||
## Addendum 2026-08-05 (#45 report tail): the VehicleDead SENDER moved
|
||||
The death-transition dispatch site (mech4) is retired: the decoded sender is the TakeDamage
|
||||
handler's death tail (@0x4a07d4-0x4a0890), now BTMechPostVehicleDead (btplayer.cpp) with the
|
||||
BT 0x38-byte extension {killed-by player, kill zone}. The #55 NULL-playerLink fallback + the
|
||||
DEAD_NOTIFY forensics moved into it intact; the #81 single-dispatch rule is unchanged. The
|
||||
flow diagram above predates this -- read BTPostKillScore rows as historical (retired 08-05).
|
||||
|
||||
@@ -431,3 +431,9 @@ RootTranslation:
|
||||
definition: The .ANI [RootTranslation] section — per-keyframe forward root SPEED (.z, units/s). Locomotion integrates movement += dt*rootTrans.z; feet plant by construction (no separate stride constant).
|
||||
related_terms: [ANI, locomotion, SequenceController]
|
||||
related_topics: [locomotion]
|
||||
|
||||
export-gap-blindness:
|
||||
kind: failure-mode
|
||||
definition: Concluding "the binary/content lacks X" from the Ghidra EXPORT or the port alone. The export has coverage gaps (#60) and RES-archive content never appears in it, so absence there proves nothing. Four incidents (two falsely [T1]); rule + checklist in reconstruction-gotchas §20 — byte-scan the exe, grep NAMED members, walk the BTL4.RES TOC, consult the manifest.
|
||||
related_terms: [decomp, RES]
|
||||
related_topics: [reconstruction-gotchas, source-completeness]
|
||||
|
||||
Reference in New Issue
Block a user