Files
BT411/docs/WEAPONS_DRIFT_AUDIT.md
T

385 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Weapons Drift Audit — binary vs port, per weapon lane (2026-08-13)
**What this is:** the AI-facing ledger of the per-weapon-class physics audit
(the #171 batch + this session's raw-byte verifications). The tester-facing
twin is `docs/WEAPONS_FIELD_GUIDE.txt` — keep the two in sync; the field
guide translates every address below into behavior and carries the era
questions to the group.
**Evidence tiers:** T1 = decomp/raw-byte verified · T2 = runtime receipts ·
T3 = port approximation · T4 = guess. Addresses are `@ADDR` in the rebuilt
2026-08-06 export (`reference/decomp/all/part_*.c`).
**HOLD notice:** corrections in the "Corrections owed" section are HELD for
the #171 group decision per Oracle's HOLD — do not sweep them into the KB or
the queued ticket comment until the group signs off.
---
## Verdict summary
| # | Lane | Verdict |
|---|------|---------|
| 1 | Lasers (Emitter 0xBC8, 6 marks) | FAITHFUL |
| 2 | PPC/ERPPC fire chain (0xBD4) | FAITHFUL |
| 3 | PPC rider (a) victim display scramble | KNOWN-DIVERGENCE [T3] (unimplemented; visual OPEN) |
| 4 | PPC rider (b) victim generator short | FAITHFUL (tag-label scope OPEN) |
| 5 | Seek-voltage gears + #21 overcharge brick | KNOWN-DIVERGENCE (deliberate rescue) |
| 6 | Autocannons AFC25/50/100 (0xBCD) | FIXED-THIS-BUILD (#171; T2 field receipts pending) |
| 7 | AC cosmetic tracer visual | KNOWN-DIVERGENCE [T3] (cosmetic; constants recovered — close) |
| 8 | GAUSS (fielded, 0xBCD record) | FAITHFUL (correction sweep owed, HELD) |
| 9 | GaussRifle class 0xBCE (+ shell 0xBD1) | FAITHFUL (dead code both sides) |
| 10 | MissileLauncher core (0xBD0, all racks) | FAITHFUL |
| 11 | Missile presentation (salvo visuals/gains) | KNOWN-DIVERGENCE [T3] (presentation-only) |
| 12 | Missile bystander sweep | KNOWN-DIVERGENCE [T3] (largest remaining gap) |
| 13 | Missile splash (@0042fad0) | FAITHFUL under stock content (gate wiring [T3] on custom eggs) |
| 14 | LRM flight + loft | FAITHFUL |
| 15 | Streak racks (strk model) | FAITHFUL |
| 16 | NRK5 / nrk-fed racks | FAITHFUL (KB model-table wording sweep owed) |
| 17 | Shared reload/ammo/eject chain | FAITHFUL |
| 18 | Jam roll | KNOWN-DIVERGENCE [T3] (extra gate — remove) |
| 19 | Incoming-missile warning | OPEN (consumer unlocated; era testimony wanted) |
---
## Per-lane entries
### 1. Lasers — Emitter 0xBC8 (SLaser/ERSLaser/MLaser/ERMLaser/LLaser/ERLLaser)
One C++ class, data-differentiated; **no pulse variants exist** in shipped
content.
- **Binary [T1]:** trigger-time hitscan beam @004bace8: charge-gear state
machine (4 states, no jam/ammo), lock required (`*(mech+0x388)!=0`),
viewFireEnable look-direction arming, `dist<=effectiveRange` gate AT FIRE.
K byte-confirmed **1.0f @004b9c98** → effRange = (1.0 zoneDamage) ×
WeaponRange. Damage = authored × (charge/seekV)². ONE aimed single-panel
message. Out-of-range: beam draws, damage silently discarded. 0.2s
target-tracking afterglow. Hard heat-FAILURE stop (no gradual
degradation). Heat/gear-toggle vet-expert only.
- **Port [T2]:** `emitter.cpp` live-verified same machine end-to-end; 60Hz
sub-stepped Loading tick (±1% snap-window robustness, port mechanism);
beam from real `ermlaser.bgf` tube via `BTPushBeamKind` ([T3] dpl layer
unported, #164 stuck-beam guards).
- **Verdict:** FAITHFUL.
### 2. PPC / ERPPC — 0xBD4 fire chain
- **Binary [T1]:** one-line tail-call into `Emitter::FireWeapon`
(@004bb878); vtable/ctor/CSS otherwise identical — everything distinctive
is authored data (EnergyDamage(4), 12/16 pts, 900/950 range, 5.0s
recharge, blue pip).
- **Port [T2]:** same tail-call structure; full charge = authored 12/16
verified live; blue beam under its own renderer case.
- **Verdict:** FAITHFUL.
### 3. PPC EnergyDamage rider (a): victim display scramble
- **Binary [T1 call, T4 visual]:** @004a0230 (~0x4a03f3): damageType==4 →
no-arg virtual on app-global +0x4c — identified via the port's own app
decode as THE GAUGE RENDERER (`DAT_004efc94+0x4c` = GetGaugeRenderer,
`btl4mppr.cpp:249`, cf. `btl4galm.cpp:404`) — a cockpit gauge/MFD scramble
per Energy hit. Exact visual/duration unread (which vtable slot +0x4c does
visually: OPEN).
- **Port:** NOT implemented — grep of the port `TakeDamageMessageHandler`
body finds no damageType==4 gauge call. Candidate for the #156/
weapon-state polish list.
- **Verdict:** KNOWN-DIVERGENCE [T3] (unimplemented rider; visual detail
OPEN — era testimony wanted BEFORE building; question shipped in the
field guide).
### 4. PPC EnergyDamage rider (b): victim generator short
- **Binary [T1]:** @0049c690: damageType==4 + critical subsystems in struck
zone → one random critical rolls `ForceShortRecovery` @004b11bc
(brownout, charge stall, cockpit electronics dip). Tag 0x50f4bc
Generator-vs-PoweredSubsystem label conflict still open.
- **Port [T2]:** implemented — `mechdmg.cpp:471-477` (damageType==4 &&
criticalSubsystemCount>0 → ForceShortRecovery).
- **Verdict:** FAITHFUL (tag-label scope OPEN). NOTE: the two riders'
port status differs — never lump them in vet-facing docs.
### 5. Seek-voltage gears + #21 overcharge brick (all energy weapons)
- **Binary [T1]:** `ToggleSeekVoltage` @004ba478 (vet/expert only): wrap
from gear 4→1 skips `ResetFiringState` → charge above snap window →
`ComputeOutputVoltage` overcharge reads ZERO → weapon PERMANENTLY
BRICKED. The arcade shipped this latent brick.
- **Port [T3 deliberate, documented]:** overcharge counts as Loaded (issue
#21, 38 benched rescues) — the port's ONE deliberate energy-lane
divergence; lives in the deviations appendix.
- **Verdict:** KNOWN-DIVERGENCE (deliberate rescue of a shipped latent
brick).
### 6. Autocannons — AFC25/50/100 (ProjectileWeapon 0xBCD)
- **Binary [T1]:** trigger-time hitscan @004bc104: lock required, batched
per TracerInterval (all authored 1), fire-time `dist<=live
effectiveRange` gate, ONE single-panel Ballistic message to the locked
target, out-of-range batch silently discarded (round consumed, tracer
flies), gyro recoil kick dmg/16 dir (0, 0.6, 1.5) (constants
3.0f/0.0625 byte-confirmed), jam p=0.41·T/Tfail clamped [0.05,1.0]
vet/expert-only, EJECT tap=one round / hold 3.0s=dump bay, shell 0xBD1
dead code.
- **Port [T2 bench]:** `projweap.cpp` at bbb3b4e implements the hitscan
line-by-line (13-round batch + DET dmg=0 receipts); closes #112
single-panel and the #168 mis-fix. Field verify armed for Friday (local
rig can't form avatar locks).
- **Verdict:** FIXED-THIS-BUILD (#171 batch; T2 field receipts pending
Friday). The field guide carries the #27 "rounds can genuinely miss"
RETRACTION.
### 7. AC cosmetic tracer visual
- **Binary [T1 weapon-side, T4 renderer-side]:** `launchVelocity.z =
effRange/sqrt(2.0·(1/9.8)·muzzleHeight)` — both constants BYTE-CONFIRMED
this audit: @0x4bc678 = 2.0f (float32); @0x4bc67c = 1/9.8 stored as an
**80-bit x87 EXTENDED** (bytes `8a 6c 37 99 43 c6 fa d0 fb 3f` — does NOT
decode as float32, likely why it stayed "unresolved") → ~1s to cross full
range for a ~5u muzzle. Drawn by the unported 'Tracer' dpl renderable;
renderer feed unrecovered.
- **Port [T3]:** `projweap.cpp` still ships 1.0f stand-ins for both
constants (marked 'unresolved' — now false) and drops the muzzleHeight
term: tracer speed = effRange/1.0. Numerically close for ~5u muzzles by
accident.
- **Verdict:** KNOWN-DIVERGENCE [T3] (cosmetic-only; constants recovered —
CLOSE IT next build; stale 'unresolved' comments must go in the same
change).
### 8. GAUSS — the fielded weapon (lok2 'GAUSS' record)
- **Binary [T1 raw bytes]:** BTL4.RES @0xf16e6: classID **0xBCD
ProjectileWeapon** (bytes `cd 0b` / `d0 01` size 0x1D0, verified this
session) — the pod's Gauss ran the FULL AC hitscan chain WITH damage:
20 pts Ballistic single-panel out to 900, 8s cycle, 16 rounds
(AmmoBinGAUSS 0xBCB), near-cold (1e6 heat), yellow pip, 1.25 gyro kick.
Class histogram across all 18 streams: 78×0xBC8 / 16×0xBCD / 30×0xBD0 /
14×0xBD4, **ZERO 0xBCE**.
- **Port [T1]:** factory dispatches 0xBCD →
`CreateProjectileWeaponSubsystem` (`mech.cpp:1939-1942`) — the port's
fielded GAUSS ALSO deals AC-hitscan damage. The "port damage is an
invention" framing (commit 80037a7, queued #171 comment, projweap
banner) is WRONG at the fielded level — port and binary agree.
- **Verdict:** FAITHFUL (mechanics match). Correction sweep owed — HELD
(see below). Era question reframed and shipped in the field guide
(headline). Chassis attribution (lok2 vs the weapsub scan's nearest-name
'blh') rides the flagged heuristic — the field guide asks the vets which
mech carried it rather than asserting.
### 9. GaussRifle class 0xBCE (+ shell 0xBD1)
- **Binary [T1]:** real code, dead content: `FireWeapon` @004bdca4 = one
store (charge=0), no damage/beam/heat; zero shipped records instantiate
it. Shell 0xBD1 `Make` @004be384 has zero callers; flight body
unreachable.
- **Port [T1]:** same classes exist (GAUSS.CPP shipped source), same
factory case, never constructed by shipped content; equally dead.
- **Verdict:** FAITHFUL (dead code both sides). The CLASS-level "@004bdca4
is a no-op" claim stands even after the fielded-Gauss correction.
### 10. Missile family core — MissileLauncher 0xBD0 (SRM/LRM/Streak/NRK)
One class, data-differentiated.
- **Binary [T1]:** genuinely flying, the ONE ungated family: lock-to-fire
via the shared Loaded case (no dumb-fire trigger path, no Streak-special
code), one entity per salvo, per-missile dmg = authored/count, seeker
homes on the fire-time picked panel point with constant-bearing lead,
4.0u fuze + swept contact (bystanders hittable), lock-drop on target
death (@0049fb54 movementMode 2||9), WeaponRange is fire-control display
only — reach is physics (burn + 10.0s coast, y<1.0 kill plane, expiry
fizzles), impact cluster roll `min(Random(n)+n/4, n)` then zone 1
lottery with per-burst re-roll on the victim.
- **Port [T2]:** full chain live-verified (382-impact night-9 corpus
matches the roll exactly; Streak 277 u/s; fuze/lock-drop benched);
direct Dispatch instead of the SubsystemMessageManager (deliberate,
preserves burstCount, #95).
- **Verdict:** FAITHFUL.
### 11. Missile presentation (salvo visuals + steering gains)
- **Binary [T1]:** ONE Missile entity per salvo (single spawn call
@004bcc60 — the pod drew one flying round + one explosion); body-turn
slerp at authored MaxThrusterRotationRate; velocity slaved to nose while
burning.
- **Port [T3]:** N-round visual ripple (lead round carries damage; N1
damage-0 tracers), ±2.5° deterministic tube spread, steering gains
4.0/8.0 port-tuned (the old 'MissileTurnGain=4.0' attribution was the
proximity fuze), muzzle safety clamp.
- **Verdict:** KNOWN-DIVERGENCE [T3] (presentation-only; damage economy
identical).
### 12. Missile bystander sweep
- **Binary [T1]:** FUN_0042291c swept real world geometry including movers
each tick — a third mech in the flight path could be struck by someone
else's missile.
- **Port [T3]:** pool rounds contact only the locked target + terrain; a
mech between shooter and target is flown through (splash alone can touch
bystanders).
- **Verdict:** KNOWN-DIVERGENCE [T3] — largest remaining missile-physics
gap. Field-guide watch item for Friday ("hit by missiles aimed at
someone else" beyond splash range).
### 13. Missile splash — Explosion::SplashDamage @0042fad0
- **Binary [T1]:** gated on the FIRING player's advancedDamage (+0x264;
every stock egg stamps 1 → splash WAS live on the pods), radius 30
authored on all missiles, baseBurst = FULL rack count (post-roll restore
@part_013.c:18283-18284, read + confirmed this session), falloff bursts
= round(base/dist^1.25) floored 1 (1.25 = shipped double; divide
grounded in T0 EXPLODE.cpp), excludes the explosion + direct victim;
shooter self-exclusion **unprovable from the export**.
- **Port [T2 mechanics / T3 gate]:** same math, full-count baseBurst (now
[T1]-pinned correct), excludes shooter + direct victim; enable =
SplashRadius>0 instead of the advancedDamage gate — coincides under all
shipped eggs, diverges only on custom eggs.
- **Verdict:** FAITHFUL under stock content (gate wiring KNOWN-DIVERGENCE
[T3] on custom eggs — site option; shooter self-splash OPEN — era
question shipped).
### 14. LRM family flight (lrm model) + loft
- **Binary [T1]:** lrm rid=319: turn 60°/s, burn 10s @ 300 u/s², climb 50,
splash 30 — climb>threshold triggers the seeker loft (aim.y +=
0.1×min(range200, 300), up to +30u): the signature climb-then-plunge
arc; soft 30 u/s eject, some mounts +5 up-tilted.
- **Port [T2]:** #84 fixed the thrust resolution (pre-fix 10× slow WAS the
'missiles are slow' field report); loft constants exact
(@004bec18/24/28).
- **Verdict:** FAITHFUL.
### 15. Streak racks (strk model — 'SRM6' bins on at least one chassis)
- **Binary [T1]:** strk rid=323: turn 360°/s (3× SRM — 6× the lrm), burn
3s @ 300, climb 0 — the round that out-turns evasion. NO Streak class or
special lock code exists; the ammo model alone is the identity.
- **Port [T2]:** same records resolved; 277 u/s impact speed verified
live.
- **Verdict:** FAITHFUL.
### 16. NRK5 'Narc' + every nrk-fed rack
Several 'LRM5/10/15' bins author nrk ammo.
- **Binary [T1]:** no beacon mechanism exists anywhere in the binary
(single launcher class, no beacon input on the seeker). nrk rid=327
byte-dumped this session: turn 360°/s, burn 10s @ 300, **CLIMB 50**,
splash 30 — the shipped NRK5 is a light LRM-profile rack WITH
Streak-grade tracking AND the loft (missileB's 'no loft' corrected;
dataA right). Field-visible agility difference between visually
identical 'LRM' racks is authentic.
- **Port [T2]:** same data-driven resolution; nothing Narc-special to
port.
- **Verdict:** FAITHFUL (KB model-table wording needs the nrk climb/turn
correction — sweep owed).
### 17. Shared reload/ammo/eject chain (0xBCD + 0xBD0)
- **Binary [T1]:** recoil countdown only while generator route powered,
heat-scaled (hot generator reloads slower), MinVolt% 0.3 freeze; EJECT
(@004bb9b8, read this session): TAP = one round out + weaponAlarm→3
Loading UNCONDITIONALLY if rounds remain — the in-mission UNJAM (clears
Jammed(5)); HOLD 3.0s = DumpAmmo whole bay → NoAmmo(7) roach motel (only
re-arm/respawn Reset clears); novice locked out of eject.
- **Port [T2]:** same machine (`projweap.cpp:751-968`); tap-unjam
semantics match (the #166 advice rides on it).
- **Verdict:** FAITHFUL.
### 18. Jam roll (ballistic + missile racks)
- **Binary [T1]:** @004bbfcc: rolled after each shot whenever simLive AND
the weapon's own heatAlarm != 0; p = 0.41·T/Tfail (0.41 byte-confirmed)
clamped [0.05, 1.0]. Practically vet/expert-only (std/novice heat model
off → never heat-alarms).
- **Port [T3 marked bring-up gate]:** rolls only at heatAlarm >=
DegradationHeat (`projweap.cpp:692`) — added when temps were static;
now that the heat economy is live it masks authentic warm-weapon jams.
Binary rolls at ANY nonzero heat alarm.
- **Verdict:** KNOWN-DIVERGENCE [T3] (minor; REMOVE the extra gate next
build — announced in the field guide).
### 19. Incoming-missile warning
- **Binary [T1 registration, T4 consumer]:** Missile ctor registers with a
MECH target via a virtual on target+0x418; the consumer (threat lamp?
beeper tempo?) is unlocated in the decomp.
- **Port [T3]:** structurally dead: the authored beeper/tempo attributes
latch (`mech4.cpp:8536`) but the only producer lives in the dormant
Missile TU — the pool never reports; the beeper never fires (#83).
- **Verdict:** OPEN (needs the consumer decomp + era testimony — question
shipped in the field guide).
---
## Contradictions resolved this audit (evidence log)
1. **GAUSS (the headline):** BTL4.RES @0xf16e6 GAUSS record = classID
0xBCD (bytes `cd 0b`, size 0x1D0); class histogram 78×0xBC8 / 16×0xBCD
/ 30×0xBD0 / 14×0xBD4, zero 0xBCE; port factory 0xBCD →
CreateProjectileWeaponSubsystem (`mech.cpp:1939-1942`). The fielded
Gauss is FAITHFUL on both sides; the earlier "port damage is an
invention" audit examined the content-dead 0xBCE class. CLASS-level
claim (@004bdca4 no-op) stands.
2. **K constant:** `_DAT_004b9c98` dumped: CODE file+0xb9a98 = `0000803f`
= 1.0f. [T4] closed → [T1]; undamaged effRange = exactly authored
WeaponRange; port's 1.0 (`mechweap.cpp:558`) correct.
3. **AC launch-speed constants:** @0x4bc678 = 2.0f (float32); @0x4bc67c =
0.10204081 = 1/9.8 as a 10-byte x87 EXTENDED (`8a6c379943c6fad0fb3f`) —
not float32-decodable, hence the stale "unresolved". projweap.cpp 1.0f
stand-ins + dropped muzzleHeight term now closable.
4. **Jam recovery:** @004bb9b8 (part_013.c:15709-15723): eject RELEASE
path calls FeedAmmo and unconditionally SetLevel(weaponAlarm, 3
Loading) when rounds remain — a tap clears Jammed(5). missileB's "jam
permanent until re-arm" refuted; in-sortie unjam = YES (novice locked
out).
5. **nrk flight model:** BTL4.RES type-15 rid=327: turn 360°/s, burn 10s,
accel 300, climb 50, splash 30. dataA right; missileB's "no loft"
corrected. KB sweep owed (combat-damage.md "LRM … the only climber").
6. **SRM6 count-4 variant:** the record authors MissileCount=4, Damage=20
→ 5.0/missile (dataA's 3.33 assumed count 6). ALL per-chassis
attributions ride the nearest-name heuristic — re-verify against the
egg/VehicleTable before any chassis-specific claim ships to the vets
(the field guide deliberately names no chassis in Appendix A).
7. **Splash baseBurst:** part_013.c:18271-18290: cluster roll stored to
+0x338 for direct dispatch, then IF splash enabled the ORIGINAL full
count is RESTORED (+0x338 = uVar3 at :18284) before SplashDamage — the
port's full-missileCount baseBurst exactly faithful.
8. **PPC display-scramble callee:** DAT_004efc94+0x4c = the GAUGE RENDERER
(`btl4mppr.cpp:249`, `btl4galm.cpp:404`) — the Energy-hit rider is a
gauge/MFD-side scramble virtual. Still open: what slot +0x4c does
visually. Port does not implement the call; the generator-short rider
IS ported — statuses differ.
9. **SRM2 flight profile — UNRESOLVED:** dataA says the ava1 SRM2 bin
feeds 'strk'; missileB groups SRM2 under 'srm'. The weapsub scan
printed weapons only (no bin ammoModelFile column). Needs the bins
pass before any SRM2 homing claim ships (field guide explicitly
declines to state it).
10. **@004bb9b8 relabel:** it is the EJECT handler, not fire-consume;
FeedAmmo @004bd4f4 is the consume. Carry into any KB address table
that copied the old tasking label.
---
## Corrections owed — HELD for the #171 group decision (Oracle HOLD)
- Commit 80037a7's "Gauss deals NO damage in the shipped binary / port
damage is an invention" framing.
- `scratchpad/night16/queued_171_comment.md` — Gauss section + the queued
era question (the reframed question now lives in the field guide). The
#112/#168 sections are correct as written.
- `context/decomp-reference.md` §SS5 and `context/combat-damage.md` Gauss
claims.
- Any `projweap.cpp` / gauge banner repeating the no-damage framing.
## Stale banners confirmed present (sweep with the next projweap change)
- `projweap.cpp:990` still reads "the #168 flight-contact model" above the
implemented hitscan body — same failure genus as the stale banner that
caused #168 (gotcha: verify banners when the body changes).
- The "unresolved" comments on `_DAT_004bc678`/`67c` are now false
(constants byte-confirmed, see entry 7).
## Actionable closures queued (next build)
1. AC tracer launch speed: land 2.0f + 1/9.8 + the muzzleHeight term in
`projweap.cpp`; delete the stale comments and the :990 banner. Cosmetic;
era question 14 (round pace) tunes the final feel.
2. Jam roll: remove the `heatAlarm >= DegradationHeat` gate
(`projweap.cpp:692`) — binary rolls at any nonzero heat alarm.
Announced to the vets in the field guide §9.
3. PPC gauge-scramble rider: BLOCKED on era testimony (field guide §3) —
build the visual only after the group answers.
4. Missile bystander sweep: design exists; field guide §6 arms the Friday
watch item.
## Era questions shipped to the vets
The 14 questions live in `docs/WEAPONS_FIELD_GUIDE.txt`, distributed into
the per-section "WE NEED YOUR MEMORY" blocks: Gauss killed + carrier
chassis (headline, §5); PPC hit visuals + generator short (§3); splash
proximity + shooter self-splash (§7); ballistic recoil, AFC25 eaten
trigger, out-of-range round fate, round pace (§4); vet-only jams +
tap-eject unjam, NoAmmo latch with ammo remaining (§9); gear-wrap brick
(§2); incoming-missile warning (§10); generator switching mid-charge,
out-of-range beam (§1); rack-name quirks, homing agility + Narc-did-
nothing (§8).