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
+8 -2
View File
@@ -73,7 +73,7 @@
extern void BTPushProjectile(const Point3D &muzzle, void *shooter, void *target,
const Point3D &targetPos, Scalar speed, Scalar damage,
const Vector3D *launch_velocity = 0, int guided = 1,
int weapon_subsys = -1);
int weapon_subsys = -1, int splash_burst = 0);
//###########################################################################
// Port-side salvo replication state (missile-visibility wave).
@@ -305,9 +305,15 @@ void MissileLauncher::FireWeapon()
// on the authored up-tilt, then the seeker loft + steering arc it onto the target.
int nmiss = (missileCount > 0 && missileCount < 40) ? missileCount : 1;
for (int i = 0; i < nmiss; ++i)
// SPLASH (task #62 fix): the whole salvo re-expresses ONE arcade cluster
// missile, which does ONE SplashDamage event with baseBurst = the cluster
// count. Tag ONLY the first round as the salvo lead (splash_burst = nmiss);
// the rest carry 0 so splash fires once per salvo, not once per visual round
// (which floored-at-1 N times = ~missileCount-x over-splash).
BTPushProjectile(muzzle, o, target, targetPos, speed, damageData.damageAmount,
&launchVelocity, 1,
subsystemID /*messmgr explosion bundling at impact (task #7)*/);
subsystemID /*messmgr explosion bundling at impact (task #7)*/,
(i == 0) ? nmiss : 0 /*salvo-lead cluster splash*/);
// Salvo replication (missile-visibility wave): bump the fire counter + stamp
// the aim point; the extended update record carries both to peer nodes.