mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-25 06:28:11 +00:00
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>