Combat: FIX missile splash over-application -- once per salvo, not per round (task #62)

Live regression: a clustered bystander died in ~2 missile salvos ("suddenly
lethal"). Root cause, not authentic: the arcade fires ONE cluster Missile per
trigger (burstCount=missileCount) doing ONE SplashDamage event with baseBurst =
missileCount, floored at 1 ONCE. The port re-expresses that cluster as N flying
BTProjectile rounds, and task #62 fired splash PER ROUND -- each baseBurst=1,
each floored at 1 -- so the distance floor was applied N times = ~missileCount x
too much splash.

Fix: BTProjectile.splashBurst tags ONLY the salvo-lead round. MissileLauncher::
FireWeapon passes nmiss (the cluster count) on i==0 and 0 on every other round;
the contact + world-impact splash hooks fire only when splashBurst>0, using it as
baseBurst. One splash event per salvo with baseBurst=missileCount -- matches the
single arcade cluster missile. Replicant mirror rounds carry 0 (damage 0 too) so
the master's cross-pod splash isn't doubled. AC unaffected (not a MissileLauncher;
splash_burst defaults 0).

Verified headless (BT_SPAWN_ENEMY=2 clustered rig, 45s): bystander now takes 6
splash events (1/salvo, baseBurst=6) and SURVIVES (was ~2 shots); primary dies to
direct hits in ~3 trigger pulls (36 missiles == single-enemy TTK); AC does not
splash; no crash. Also: BT_SPAWN_ENEMY read as a COUNT (=2 clusters a bystander
within SplashRadius) for eyeballing splash; entity-id logging on [enemy]/[splash].
KB: combat-damage.md documents the N-round cluster trap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-13 19:43:50 -05:00
co-authored by Claude Opus 4.8
parent 12fbc023a8
commit 145a69f865
5 changed files with 139 additions and 70 deletions
+18 -3
View File
@@ -469,9 +469,24 @@ resolves launcher → `BTWeaponAmmoBin` → `BTAmmoRoundModelResource` (+0x1e8)
`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).
**⚠ THE SALVO-LEAD FIX (task #62 bug, found 2026-07-13 by live regression) — the N-round trap.**
The arcade fires ONE cluster Missile per trigger (burstCount=missileCount) → ONE SplashDamage event
with `baseBurst = missileCount`, floored at 1 ONCE. The port re-expresses that cluster as N flying
`BTProjectile` rounds (visual tracers). Firing splash PER ROUND (each `baseBurst=1`, each floored at
1) applied the floor N times = **~`missileCount`× too much splash** — a clustered bystander died in
~2 salvos (user-reported "missiles suddenly lethal"; direct hits were fine — a single enemy survived
24). Fix: `BTProjectile.splashBurst` tags ONLY the salvo-lead round (`FireWeapon` passes `nmiss` on
`i==0`, `0` on the rest); splash fires once per salvo with `baseBurst = missileCount`. Replicant
mirror rounds carry `0`/damage-0 (master delivers splash cross-pod — no double). **Lesson:** a cluster
weapon re-expressed as N rounds must apply per-CLUSTER effects (splash, and note the direct hit's
zone-concentration is likewise SPREAD in the port vs the arcade's single-zone cluster — a separate,
pre-existing under-concentration) once per salvo, not once per round.
**Verified:** radius resolves to 30 for missiles / 0 for AC+Emitters; near-miss dist=15 → 1 burst;
live rig (2 clustered mechs, 45s): bystander takes 6 splash events (1/salvo, `baseBurst=6`) and
SURVIVES (was ~2 shots pre-fix); primary dies to direct hits in ~3 trigger pulls (36 missiles, ==
single-enemy TTK); AC does not splash; no crash. Env `BT_SPLASH_LOG`, `BT_SPLASH_TEST` (synthetic
near-miss), `BT_AF_MISSILE` (missile autofire), `BT_SPAWN_ENEMY=2` (clustered bystander rig).
**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