From 932d4f3be2cded197e5ed4712a9e9e35963b86a6 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Fri, 25 Jul 2025 15:44:32 -0700 Subject: [PATCH] Have MIRV damage asymptote to 5% of total population (#1570) ## Description: Decreasing MIRV warhead blast radius nerfed them too much, so have them destroy 95% of population. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I have read and accepted the CLA aggreement (only required once). ## Please put your Discord username so you can be contacted if a bug or regression is found: evan --- src/core/configuration/Config.ts | 10 ++++++-- src/core/configuration/DefaultConfig.ts | 18 ++++++++++++-- src/core/execution/NukeExecution.ts | 32 +++++++++++++++++++++---- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 5f975031c..a964595e3 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -1,6 +1,5 @@ import { Colord } from "colord"; import { JWK } from "jose"; -import { GameConfig, GameID, TeamCountConfig } from "../Schemas"; import { Difficulty, Game, @@ -18,6 +17,8 @@ import { import { GameMap, TileRef } from "../game/GameMap"; import { PlayerView } from "../game/GameView"; import { UserSettings } from "../game/UserSettings"; +import { GameConfig, GameID, TeamCountConfig } from "../Schemas"; +import { NukeType } from "../StatsSchemas"; export enum GameEnv { Dev, @@ -157,7 +158,12 @@ export interface Config { defaultNukeSpeed(): number; defaultNukeTargetableRange(): number; defaultSamRange(): number; - nukeDeathFactor(humans: number, tilesOwned: number): number; + nukeDeathFactor( + nukeType: NukeType, + humans: number, + tilesOwned: number, + maxPop: number, + ): number; structureMinDist(): number; isReplay(): boolean; allianceExtensionPromptOffset(): number; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 03a34236d..d1139198f 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -23,6 +23,7 @@ import { TileRef } from "../game/GameMap"; import { PlayerView } from "../game/GameView"; import { UserSettings } from "../game/UserSettings"; import { GameConfig, GameID, TeamCountConfig } from "../Schemas"; +import { NukeType } from "../StatsSchemas"; import { assertNever, simpleHash, within } from "../Util"; import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config"; import { PastelTheme } from "./PastelTheme"; @@ -822,8 +823,21 @@ export class DefaultConfig implements Config { } // Humans can be population, soldiers attacking, soldiers in boat etc. - nukeDeathFactor(humans: number, tilesOwned: number): number { - return (5 * humans) / Math.max(1, tilesOwned); + nukeDeathFactor( + nukeType: NukeType, + humans: number, + tilesOwned: number, + maxPop: number, + ): number { + if (nukeType !== UnitType.MIRVWarhead) { + return (5 * humans) / Math.max(1, tilesOwned); + } + + const targetPop = 0.05 * maxPop; + const excessPop = Math.max(0, humans - targetPop); + const scalingFactor = 20000; + + return (scalingFactor * excessPop * excessPop) / (maxPop * maxPop); } structureMinDist(): number { diff --git a/src/core/execution/NukeExecution.ts b/src/core/execution/NukeExecution.ts index 82c32f400..494e6fc12 100644 --- a/src/core/execution/NukeExecution.ts +++ b/src/core/execution/NukeExecution.ts @@ -203,6 +203,10 @@ export class NukeExecution implements Execution { const toDestroy = this.tilesToDestroy(); this.maybeBreakAlliances(toDestroy); + const maxPop = this.target().isPlayer() + ? this.mg.config().maxPopulation(this.target() as Player) + : 1; + for (const tile of toDestroy) { const owner = this.mg.owner(tile); if (owner.isPlayer()) { @@ -210,25 +214,45 @@ export class NukeExecution implements Execution { owner.removeTroops( this.mg .config() - .nukeDeathFactor(owner.troops(), owner.numTilesOwned()), + .nukeDeathFactor( + this.nukeType, + owner.troops(), + owner.numTilesOwned(), + maxPop, + ), ); owner.removeWorkers( this.mg .config() - .nukeDeathFactor(owner.workers(), owner.numTilesOwned()), + .nukeDeathFactor( + this.nukeType, + owner.workers(), + owner.numTilesOwned(), + maxPop, + ), ); owner.outgoingAttacks().forEach((attack) => { const deaths = this.mg ?.config() - .nukeDeathFactor(attack.troops(), owner.numTilesOwned()) ?? 0; + .nukeDeathFactor( + this.nukeType, + attack.troops(), + owner.numTilesOwned(), + maxPop, + ) ?? 0; attack.setTroops(attack.troops() - deaths); }); owner.units(UnitType.TransportShip).forEach((attack) => { const deaths = this.mg ?.config() - .nukeDeathFactor(attack.troops(), owner.numTilesOwned()) ?? 0; + .nukeDeathFactor( + this.nukeType, + attack.troops(), + owner.numTilesOwned(), + maxPop, + ) ?? 0; attack.setTroops(attack.troops() - deaths); }); }