Combat: MISSILE SPLASH DAMAGE -- Explosion::SplashDamage reconstructed (task #62)

Area-of-effect blast on missile detonation, from the T0 source EXPLODE.cpp:50-254
(@0042fad0) + the arcade decomp. Fires on ANY missile impact (world or mech) --
the +0x360 gate sits in the Missile::Perform collision branch (part_013.c:10097).
Only missiles splash; the AC's tracer is not a Missile.

Radius source CORRECTED: it is the ROUND's own GameModel (type-0xf) +0x50, seeded
from the launcher's linked AmmoBin ammoModelFile @0x1e8 (part_013.c:8778) -- NOT
the launcher's ExplosionModelFile (which resolved to 0). Live-resolved radius = 30
for both MP missile launchers; 0 for AC/Emitters.

Burst falloff = baseBurst / dist^exp floored at 1 (arcade 1.25 = decomp 0x3ff40000;
WinTesla EXPLODE.cpp:209 drifted to 1.2f). damageType/amount pass through unchanged;
only burstCount, radial damageForce, and impactPoint are set. Excludes shooter +
direct victim; dist>radius gated; delivered via msgmgr (cross-pod) or Dispatch.

New: BTResolveSplashRadius / BTApplySplashDamage (mech4.cpp), hooked at both the
world-impact and contact detonation paths; BTAmmoRoundModelResource bridge
(ammobin). Env: BT_SPLASH_LOG, BT_SPLASH_TEST (synthetic near-miss), BT_AF_MISSILE
(missile autofire). Verified: near-miss dist=15 -> 1 burst to a bystander; live
missiles detonate + exclude the direct victim; AC does not splash; no crash.

Deferred (T3): per-player enable sub-gate missile+0x360 = BTPlayer+0x264 (writers
read as a per-frame toggle, not a config flag) -- port treats SplashRadius>0 as the
enable. KB: combat-damage.md + open-questions.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-13 19:06:24 -05:00
co-authored by Claude Opus 4.8
parent 4c54f7ef0c
commit 12fbc023a8
5 changed files with 282 additions and 4 deletions
+38
View File
@@ -439,6 +439,44 @@ the un-exported master-perf writer (0x4a9770-0x4ab188 [T4]); the exact authentic
writer emitted (msg+0x24) is likewise unrecovered, so the killing-blow magnitude is the faithful
stand-in until it is. [T1 mechanism / T4 exact authentic value]
## Missile splash damage — `Explosion::SplashDamage` (task #62, 2026-07-13) [T1/T2]
Area-of-effect blast on **missile** detonation — NOT the vestigial whole-mech `DeathSplashDamage`
above (mech+0x520, never read). The T0 source is `engine/MUNGA/EXPLODE.cpp:50-254` (`@0042fad0`),
called ONLY from the Missile's per-frame `Perform` (`@004bef78`, part_013.c:10097) inside the
collision branch — so it fires on **any** missile impact (mover OR world geometry), gated on
`Missile+0x360`. The AC's tracer is not a Missile and never splashes.
**The model [T0, `EXPLODE.cpp`]:** build a Y-axis cylinder of `SplashRadius` at the blast, collect
every `Mover` in it (+ static world), then for each target that has `damageZones` and is not the
direct victim (`entityHit`), the detonator (`this`), or `excluded`:
- `burstCount = baseBurst / pow(dist, 1.2f)`, `Min_Clamp(…, 1)` — dist = `|target.localOrigin
blastCenter|` (center-to-center). The **arcade** exponent is **1.25** (decomp `0x3ff40000` at
part_004.c:865; the decompiler hid the x87 FDIV + the `FUN_004dcd94`=ROUND that WinTesla writes as
truncation). Point-blank AMPLIFIES (dist<1 → >baseBurst); no distance floor (real victims are never
at dist~0 — the one AT the center is the excluded direct victim).
- `damageType`/`damageAmount` pass through UNCHANGED; only `burstCount`, `damageForce` (radial), and
`impactPoint` (= blastCenter+offset) are set. Delivered via `TakeDamageMessage(inflicting, zone=-1)`.
**The SplashRadius source [T1]:** it is the ROUND's GameModel resource `+0x50`, NOT the launcher's
`ExplosionModelFile`. The launcher seeds each spawned Missile's model from its linked **AmmoBin**
`ammoModelFile @0x1e8` (part_013.c:8778); the Missile ctor `SearchList(that_id, GameModelResourceType)`
→ `+0x50` → `Missile+0x364` (part_013.c:10184). The MissileThruster GameModel parser
`FUN_004bf8ec` writes `"SplashRadius"` to `+0x50` of the type-`0xf`/size-`0x54` record. Live-resolved
radius for both MP missile launchers = **30** (`BT_ROSTER` dump).
**Port impl (`mech4.cpp`):** `BTResolveSplashRadius` gates on `MissileLauncher::ClassDerivations`,
resolves launcher → `BTWeaponAmmoBin` → `BTAmmoRoundModelResource` (+0x1e8) → GameModel +0x50.
`BTApplySplashDamage` walks `BTGetTargetCandidates` (excludes shooter), skips the direct victim +
destroyed/zoneless mechs, applies the `dist>radius` gate + the burst falloff, delivers through the
shooter's msgmgr (cross-pod) or `Dispatch`. Hooked at BOTH detonation paths (world-impact + contact).
**Verified:** radius resolves to 30 for missiles / 0 for AC+Emitters; near-miss dist=15 → 1 burst
delivered to a bystander; live missiles detonate + exclude the direct victim; AC does not splash; no
crash. Env `BT_SPLASH_LOG`, `BT_SPLASH_TEST` (synthetic near-miss), `BT_AF_MISSILE` (missile autofire).
**Deferred [T3]:** the per-player enable sub-gate `Missile+0x360 = BTPlayer+0x264` (part_013.c:8757) —
its decomp writers (4668/10512) read as a per-frame state toggle, not a clean config flag, and the
port's `showDamageInflicted` label is itself a guess; port treats authored `SplashRadius>0` as the
enable. See [[open-questions]].
## Key Relationships
- Weapons/roster: [[subsystems]]. Aim source: [[locomotion]] (drive/facing). Effects: [[rendering]].
- P5 forensics: `docs/HARD_PROBLEMS.md`. Data: [[decomp-reference]] §4-5.