diff --git a/context/combat-damage.md b/context/combat-damage.md index 5f278ff..b89176c 100644 --- a/context/combat-damage.md +++ b/context/combat-damage.md @@ -199,14 +199,21 @@ Fixed-torso mechs (the BLH: `TorsoHorizontalEnabled=0` — no jointtorso in the boresight dead-ahead. Once locked, `Emitter::FireWeapon` converges with NO aim/arc test (part_013.c:7758). `MechWeapon::UpdateTargetState` (`FUN_004b9bdc` [T1]): `targetWithinRange = dist < (1 − hostZoneDamage) × weaponRange`. The 0x388 WRITER is in the same un-exported gap. -⚠ `weaponRange` is **fire-control data only** (#168 audit [T1]): projectile FLIGHT is never -range-capped — a plain shell (AFC) lives exactly 5.0 s (@4bddec) and a Missile lives -BurnTime+10 s with a y<−1 kill-plane (@4bef78); FireWeapon @4bcc60 has no range gate, so an -AFC50 (authored range 750) landing hits at 2000+ u is AUTHENTIC arcade behavior. Constants + -the whole flight/expiry chain: [[decomp-reference]] §5 "Projectile/Missile flight & expiry". -The same audit killed the pool's stale-point auto-hit (unguided contact was tested against -the fire-time pick, so a locked AC shell could not miss a moving target — fixed 2026-08-11: -the contact test now tracks the target's live position on every round kind). +⚠ ~~`weaponRange` is fire-control data only~~ **RETRACTED 2026-08-13 (the #171 hunt): that +was MISSILE-only truth mis-generalized to ballistics.** The AC's own FireWeapon @004bc104 +(recovered by the 2026-08-06 re-export; #168 worked from a stale "not recovered" banner and +generalized from the missile launcher) is TRIGGER-TIME HITSCAN: batched rounds, dist vs LIVE +effectiveRange at fire time (the same gate energy uses), ONE single-panel damage message to +the locked target, nothing spawned (the shell class 0xBD1 is dead code; the visible round is +the 0xBCD renderer tracer). So: an AFC50 landing hits beyond its authored range is a PORT +DEVIATION, not arcade behavior; "a locked AC could not miss a mover" IS the arcade behavior +(#168's "stale-point auto-hit" kill removed an authentic design while fixing its stale-point +detail). Missiles: genuinely flying + seeker-led (the machine leads for the player), 4.0u +proximity fuze, swept contact, salvo randomization — the #168 flight direction stands THERE. +Gauss: NO damage in the shipped binary (charge dump only; port damage is an invention). +#112 (AFC multi-zone shotgun) is the port's per-round scatter vs the binary's one batched +message. Full chain + evidence: [[decomp-reference]] §5 (corrected); port fix pending the +#171 group decision (Oracle's HOLD). ⚠ **`hostZoneDamage` is `Subsystem::damageZone->damageLevel` (weapon `@0xE0 → +0x158`), NOT heatLoad** — the port originally computed `effectiveRange = (1 − heatLoad) × weaponRange` (mechweap.cpp), the SAME `@0xE0`-DamageZone-vs-heat misattribution corrected in diff --git a/context/decomp-reference.md b/context/decomp-reference.md index 53ec35d..8f26b12 100644 --- a/context/decomp-reference.md +++ b/context/decomp-reference.md @@ -253,14 +253,32 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a @004b0b18) + @004b0abc (auto/manual, modeAlarm@0x2CC); attach/detach @004b0dd8/@004b0e30 (generator client capacity @0x1e4/count @0x1e8). -### Projectile/Missile flight & expiry (#168 audit, 2026-08-11) [T1] -- **No range cap exists anywhere in the flight chain.** WeaponRange feeds fire-control only - (effectiveRange @0x328 = (1−hostZoneDamage)×WeaponRange — targeting/reticle "in range" call). - FireWeapon @4bcc60 reads no +0x328: the trigger fires at any target distance. -- **Plain Projectile (AFC shell) live integrator @4bddec** (master); expiry: age since +### Projectile/Missile flight & expiry (#168 audit 2026-08-11; ⚠ CORRECTED by the #171 hunt 2026-08-13) [T1] +- ⚠ **#168's "no range cap / WeaponRange is fire-control only" was MISSILE-ONLY truth, + mis-generalized.** The AC's own FireWeapon @004bc104 (recovered by the 2026-08-06 re-export; + the old projweap.cpp "body NOT recovered" banner was stale) IS range-gated AND hitscan: + batch counter +0x408, every TracerInterval-th (+0x438) round → dist = |aimPoint(+0x37c) − + muzzle| vs LIVE effectiveRange +0x328 (dynamic: WeaponRange × (K − owningZone structure), + recomputed @004b9bdc; K @_DAT_004b9c98 presumed 1.0 [T4-unread]) → ONE single-panel Damage + (batchedCount × DamageAmount) to the LOCKED target *(mech+0x388) via @004b9728 AT FIRE TIME. + Out of range = batch silently discarded (tracer still renders). The visible AC round is the + 0xBCD renderer TRACER (tracerModel+0x41C, TOF @004bc06c) — cosmetic. MissileLauncher + @004bcc60 (no fire-time damage, no range read) is the genuinely-flying family. + **Gauss @004bdca4 deals NO damage in the shipped binary** (charge dump only — projectile + launch designed, never implemented; port damage is an invention pending the #171 decision). + **Every FireWeapon (all classes) requires a LOCKED target**: both sims gate the trigger on + *(mech+0x388) != 0 — no lock, no fire [T1]. +- **Plain Projectile (shell) class 0xBD1 is DEAD CODE in the shipped binary**: 0xbd1 appears + exactly once in the whole export (the DefaultData lookup @004c1590); Make @004be384 has zero + code callers; the fully-recovered AC fire path spawns nothing. Its flight body @4bddec + (below) is a complete never-instantiated Model-B implementation — its 5.0s clock is real + code that nothing shipped ever runs. [T1; 6.5% export-gap caveat] +- **Plain Projectile (dead class) integrator @4bddec** (master); expiry: age since spawn(+0x19c) > `_DAT_004be054` = **5.0 s** → FUN_0042061c kill. Dead-reckon divergence² vs predicted pos(+0x260, predictor FUN_00422060 via +0x254) > `_DAT_004be050` = 0.1 → - updateModel(+0x18)|=1 (ForceUpdate, replication-dirty — NOT a kill). + updateModel(+0x18)|=1 (ForceUpdate, replication-dirty — NOT a kill). Contact: swept test + @0042291c per tick; on hit → explosion msg 0x5C at live pos + direct TakeDamage (zone −1) + to the struck entity via @004be078; at EXPIRY silent removal, no damage. - **Missile live integrator @4bef78** (master); expiry: age > burnTime(+0x340, model rec +0x44) + `_DAT_004bf5ac` = **10.0 s** coast margin, OR pos.y < `_DAT_004bf5b0` = **−1.0** kill-plane → FUN_0042061c. Keepalive: no update for `_DAT_004bf594`=2.0 s → dirty bit. diff --git a/game/reconstructed/projweap.cpp b/game/reconstructed/projweap.cpp index d8249df..15757e6 100644 --- a/game/reconstructed/projweap.cpp +++ b/game/reconstructed/projweap.cpp @@ -969,10 +969,26 @@ void // // @004bc104 -- slot 18 FireWeapon (overrides MechWeapon's pure-virtual trap -// @004ba45c). Body NOT recovered by the decompiler (prologue confirmed at -// 0x4bc104, immediately following the 1.0f literal @0x4bc100). Spawns the -// generic projectile/tracer. MissileLauncher overrides this again at @004bcc60 -// to launch Missile entities. +// @004ba45c). +// +// STALE-BANNER CORRECTION (2026-08-13, the #171 hunt): this comment said +// "Body NOT recovered by the decompiler" -- TRUE of the 2025 export, FALSE +// since the 2026-08-06 re-export (part_013.c:16023-16125), and the stale +// banner is the ROOT CAUSE of the #168 mis-fix: the ballistic model was +// generalized from the MISSILE launcher @004bcc60 instead of this body. +// The RECOVERED truth [T1]: the AC FireWeapon is TRIGGER-TIME HITSCAN -- +// it spawns NOTHING (no shell entity; class 0xBD1 is dead code; the visible +// round is the 0xBCD renderer tracer). It batches the round counter +// (+0x408) and every TracerInterval-th round (+0x438) computes +// dist = |mech aimPoint(+0x37c) - muzzle| and, gated dist <= LIVE +// effectiveRange (+0x328, the same gate shape as the Emitter), sends ONE +// single-panel Damage (amount = batchedCount x DamageAmount) to the LOCKED +// target *(mech+0x388) via SendDamageMessage @004b9728 -- at fire time. +// A locked AC inside range cannot miss; out of range the batch silently +// discards (tracer still flies). Reconstruction pending the #171 group +// decision (Oracle's HOLD) -- the current port body below still carries +// the #168 flight-contact model. MissileLauncher (@004bcc60) is the +// genuinely-flying family (Model B) and overrides this slot. // // Gitea #12: the view/target gate, the ammo pull and the recoil set are // STRIPPED from this body -- they belong to the CALLER (the recovered