ONE Explosion per salvo -- the port detonated every visual round, amplifying explosion sound + smoke + audio demand by the missile count

The user challenged the 7% clock story ("i think its not just a 7% discrepancy.
did you base your analysis in the decomp") -- correctly.  The stop-path trace
it forced found the real amplifier, and it is byte-grounded:

THE ARCADE (byte-verified in the #95 work): FireWeapon @004bcc60 spawns ONE
Missile entity per trigger -- no loop, missileCount never read at fire.  One
Missile -> one detonation (@004be078) -> ONE Explosion entity: one 4-voice
sound + one authored smoke package per salvo.  The cluster's N-fold nature is
delivered as DAMAGE (burstCount), not as N explosions.

THE PORT: the N-round visual ripple (a deliberate [T3] port visual; its rounds
are damage-0 tracers) called BTSpawnRoundDetonation at EVERY round's contact --
N full Explosion entities per salvo, N x BigExpInt x 4 voices, N x the
explosion PFX, N=2..20.  Measured in a 100s solo missile bench: 559 explosion
sound setups; BigExpInt 208.  On observer nodes the stale-point mirror bug
(fixed in da70bd5) DOUBLED the train again.

One infidelity, three field symptoms:
  * Oracle: "prolonged smoke and explosion including sound" -- a staggered
    train of 6-20 overlapping BigExpInt instances per salvo reads as one long
    explosion+roar (Rajel correctly tied it to #84)
  * Ronin: missile-impact smoke "so much it functioned as a smoke screen"
  * #32: the saturating audio class was 1005 Static3DPatchSource requested=4
    -- each surplus detonation reserved 4 more voices

FIX: BTProjectile carries salvoLead; both detonation sites (target contact +
world/terrain burst) gate on it.  Master salvo: lead = round 0 (the damage
carrier).  Mirror salvo: round 0, so observers see exactly one explosion per
salvo at the live target.  AC/laser single rounds default lead=1 (one round =
one explosion, authentic).  Non-lead rounds keep their small tracer end-puffs.

MEASURED A/B (same bench): 80 of 96 contacting rounds no longer spawn the
package; 16 leads ~= the salvo count.  BigExpInt setups 208 -> 72 (~18
packages for ~16 salvos + deaths = one per salvo).  LaserExplosion unchanged
in kind.  Splash was already lead-only (splashBurst) -- damage totals move
NOWHERE in this change.

Also corrected the rack-tube comment that claimed "the binary's Missile
entities each launch from their own rack tube... their 12 detonations" -- there
are no 12 binary missiles; the ripple is ours.

The 28Hz audio-clock calibration from the previous commit stands, but is
demoted to what it is: a small systemic correction, not the explanation for
the field reports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-08-02 12:00:25 -05:00
co-authored by Claude Opus 5
parent 6dbeb3d266
commit 9b1353bfbf
3 changed files with 34 additions and 13 deletions
+6 -3
View File
@@ -79,7 +79,8 @@ extern void BTPushProjectile(const Point3D &muzzle, void *shooter, void *target,
const Vector3D *launch_velocity = 0, int guided = 1,
int weapon_subsys = -1, int splash_burst = 0, int muzzle_seg = -1,
int damage_type = 2 /*ExplosiveDamageType default*/,
Scalar thrust_accel = 0.0f, Scalar thrust_burn = 0.0f /*#84 thruster*/);
Scalar thrust_accel = 0.0f, Scalar thrust_burn = 0.0f /*#84 thruster*/,
int salvo_lead = 1 /*ONE Explosion per salvo*/);
// #84: resolve this launcher's authored thruster (burn s, accel u/s^2) through
// its connected bin's round-model resource. 0/0 when unresolvable (flies at
// the old constant eject speed -- never worse than before).
@@ -355,7 +356,8 @@ void MissileLauncher::FireWeapon()
(i == 0) ? nmiss : 0 /*salvo-lead: cluster splash baseBurst*/,
GetSegmentIndex() /*task #67: launch through the MOUNT frame (torso twist)*/,
(int)damageData.damageType /*missile = Explosive (authentic)*/,
tAccel, tBurn /*#84: the round ACCELERATES like the binary Missile*/);
tAccel, tBurn /*#84 thruster*/,
(i == 0) /*ONE Explosion per salvo: the lead only*/);
// Salvo replication (missile-visibility wave): bump the fire counter + stamp
// the aim point; the extended update record carries both to peer nodes.
@@ -468,7 +470,8 @@ void
rec->salvoTarget, spd, 0.0f /*VISUAL*/, &launchVelocity, 1,
subsystemID /*per-round detonation resolve on this node*/,
0, GetSegmentIndex() /*task #67 mount frame*/,
2 /*Explosive*/, tAccel, tBurn);
2 /*Explosive*/, tAccel, tBurn,
(i == 0) /*mirror lead: observers see ONE explosion per salvo*/);
if (getenv("BT_PROJ_LOG"))
DEBUG_STREAM << "[projectile] REPLICANT salvo x" << n
<< " at(" << rec->salvoTarget.x << "," << rec->salvoTarget.y