Files
OpenFrontIO/resources
ca5ed2416c Make MIRV warheads standard nukes: real flight, normal SAM interception, per-type speeds (#4706)
fixes #4508

## Summary

MIRV warheads previously had no real flight in the simulation: they
spawned directly **on their target tile** (the separation-point `src`
passed by `MirvExecution` was silently discarded by `NukeExecution`) and
detonated 0–15 ticks later. Because there was nothing a SAM missile
could ever intercept, `SAMLauncherExecution` carried a special batch
path that instantly deleted every warhead aimed within 50 tiles of the
SAM in a single launch.

This PR makes warheads just another nuke type and removes that
special-casing:

### Core simulation
- `NukeExecution` now honors a caller-provided launch tile (`src ??=
spawn`), so warheads fly a real parabola from the MIRV separation point
down to their targets, with a recorded trajectory and motion plan. The
`distanceBasedHeight` special case is gone.
- The SAM batch-interception block (search/protection radii, batch
delete, special message + stats) is deleted. `MIRVWarhead` joins the
standard preshot targeting list and the `SAMMissileExecution` whitelist:
SAMs fire **one missile per warhead**, respecting range and cooldown.
The MIRV carrier itself remains un-interceptable.
- Nuke speeds move from a flat `defaultNukeSpeed()` to a per-type map
`Config.nukeSpeed(unitType)`, with MIRV (10 → 15) and warheads (base 15
→ 22, plus the existing 0–4 stagger) **50% faster**; atom/hydro
unchanged at 10.
- Intentionally unchanged: the MIRV-specific population-reduction
formula (drives troops toward 3% of max), blast radii, alliance
semantics (warheads still don't break alliances; the MIRV betrays at
launch), and event messages (no per-warhead inbound/detonated spam).

### Balance notes
- MIRVs are substantially stronger against SAM-heavy defense: a SAM now
downs individual warheads on its normal cooldown instead of vaporizing
every warhead targeting its protection radius in one shot.
- Warheads now abort at launch if their trajectory crosses impassable
terrain, like other nukes.
- Impacts spread over real flight time instead of all landing within
~1.6s.

### Client fallout from warheads spending real time in the air
- **TrailManager**: death cleanup was quadratic (every dead warhead
rescanned every surviving trail per tile) and hit ~50% CPU during
multi-MIRV strikes. Trail tiles now carry a live-trail refcount; a texel
clears when its last claimant dies. Benchmarked at two-MIRVs-in-flight
scale: **81.2ms → 1.2ms per tick (~68×)**. Tradeoff (documented in the
header): where trails with *different* texel values cross, the crossing
pixels can keep the dead trail's value until survivors die too;
same-owner warhead fans clear pixel-perfectly.
- **Sounds**: warhead detonations are rate-limited to one `atom-hit` per
5 ticks so a 350-warhead rain doesn't churn a `howl.play()` per impact
(`SoundManager` caps concurrency at 8, but each play still cost CPU).
- Removed the now-unused `mirv_warheads_intercepted` message key from
`en.json`.

## Tests
- New: SAM takes down a stationary warhead; SAM intercepts an in-flight
warhead end-to-end via the normal pipeline.
- New: `SoundEffectController` boom rate-limit +
intercepted-warheads-stay-silent.
- New: `nukeSpeed` config map values.
- Full suite passes (2189 + 230 server), lint + tsc clean. TrailManager
overlap-repaint and SAM/Nuke/Nation MIRV tests all pass unchanged
(TestConfig keeps a flat test speed so existing tick counts stay valid).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:16:24 -07:00
..
2026-01-09 20:26:34 -08:00
2025-09-30 13:18:03 -07:00
2025-12-09 16:12:00 -08:00
2025-09-28 17:47:05 -07:00
2025-05-29 11:07:04 -07:00
2026-05-31 15:09:08 +01:00
2026-01-09 20:26:34 -08:00