Files
OpenFrontIO/tests/MissileSilo.test.ts
Aotumuri f1d162825e feat: remove spawn timer on singleplayer (#3199)
Resolves #1041 

## Description:

Remove the singleplayer spawn countdown so the game starts when the
player spawns, spawn nations immediately after player spawn, and align
game timer/max-timer timing with the new start point.

Added a singleplayer regression test for spawn-immunity timing
(GameImpl.test.ts) and updated spawn-phase loop tests to use gameType:
GameType.Public where singleplayer behavior is not under test (e.g.
MIRV/AI/Spawn/WinCheck-related suites), eliminating inSpawnPhase()
timeout hangs after the new singleplayer start logic.


https://github.com/user-attachments/assets/c07a585f-1153-490e-88ca-a91fc7ae5756

## 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

## Please put your Discord username so you can be contacted if a bug or
regression is found:
aotumuri
2026-05-11 12:44:44 -07:00

108 lines
3.1 KiB
TypeScript

import { NukeExecution } from "../src/core/execution/NukeExecution";
import { SpawnExecution } from "../src/core/execution/SpawnExecution";
import { UpgradeStructureExecution } from "../src/core/execution/UpgradeStructureExecution";
import {
Game,
Player,
PlayerInfo,
PlayerType,
UnitType,
} from "../src/core/game/Game";
import { TileRef } from "../src/core/game/GameMap";
import { GameID } from "../src/core/Schemas";
import { setup } from "./util/Setup";
import { constructionExecution, executeTicks } from "./util/utils";
const gameID: GameID = "game_id";
let game: Game;
let attacker: Player;
function attackerBuildsNuke(
source: TileRef | null,
target: TileRef,
initialize = true,
) {
game.addExecution(
new NukeExecution(UnitType.AtomBomb, attacker, target, source),
);
if (initialize) {
game.executeNextTick();
game.executeNextTick();
}
}
describe("MissileSilo", () => {
beforeEach(async () => {
game = await setup("plains", { infiniteGold: true, instantBuild: true });
const attacker_info = new PlayerInfo(
"attacker_id",
PlayerType.Human,
null,
"attacker_id",
);
game.addPlayer(attacker_info);
game.addExecution(
new SpawnExecution(
gameID,
game.player(attacker_info.id).info(),
game.ref(1, 1),
),
);
attacker = game.player("attacker_id");
constructionExecution(game, attacker, 1, 1, UnitType.MissileSilo);
});
test("missilesilo should launch nuke", async () => {
attackerBuildsNuke(null, game.ref(7, 7));
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(1);
expect(attacker.units(UnitType.AtomBomb)[0].tile()).not.toBe(
game.map().ref(7, 7),
);
for (let i = 0; i < 5; i++) {
game.executeNextTick();
}
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(0);
});
test("missilesilo should only launch one nuke at a time", async () => {
attackerBuildsNuke(null, game.ref(7, 7));
attackerBuildsNuke(null, game.ref(7, 7));
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(1);
});
test("missilesilo should cooldown as long as configured", async () => {
expect(attacker.units(UnitType.MissileSilo)[0].isInCooldown()).toBeFalsy();
// send the nuke far enough away so it doesn't destroy the silo
attackerBuildsNuke(null, game.ref(50, 50));
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(1);
for (let i = 0; i < game.config().SiloCooldown() - 2; i++) {
game.executeNextTick();
expect(
attacker.units(UnitType.MissileSilo)[0].isInCooldown(),
).toBeTruthy();
}
executeTicks(game, 2);
expect(attacker.units(UnitType.MissileSilo)[0].isInCooldown()).toBeFalsy();
});
test("missilesilo should have increased level after upgrade", async () => {
expect(attacker.units(UnitType.MissileSilo)[0].level()).toEqual(1);
const upgradeStructureExecution = new UpgradeStructureExecution(
attacker,
attacker.units(UnitType.MissileSilo)[0].id(),
);
game.addExecution(upgradeStructureExecution);
executeTicks(game, 2);
expect(attacker.units(UnitType.MissileSilo)[0].level()).toEqual(2);
});
});