This commit is contained in:
Ryan Barlow
2026-05-27 00:38:49 +01:00
parent 8323a1ec2e
commit f10c78158d
3 changed files with 60 additions and 15 deletions
+10 -13
View File
@@ -1,5 +1,5 @@
import { EventBus } from "../core/EventBus";
import { GameView, PlayerView } from "../core/game/GameView";
import { GameView } from "../core/game/GameView";
import { ClientID } from "../core/Schemas";
import { SkinTestWinModal } from "./hud/layers/SkinTestWinModal";
import { SendAttackIntentEvent } from "./Transport";
@@ -7,6 +7,7 @@ import { SendAttackIntentEvent } from "./Transport";
const INITIAL_ATTACK_DELAY_MS = 100;
const MAX_PLAYER_LOOKUP_RETRIES = 50;
const MODAL_TIMEOUT_MS = 120_000;
const INITIAL_ATTACK_TROOPS = 1_000_000;
/**
* Client-side controller for the "preview a skin" singleplayer game.
@@ -17,7 +18,6 @@ const MODAL_TIMEOUT_MS = 120_000;
* the EventBus + DOM — neither of which belong in src/core.
*/
export class SkinTestController {
private myPlayer: PlayerView | null = null;
private attackTimer: ReturnType<typeof setTimeout> | null = null;
private modalTimer: ReturnType<typeof setTimeout> | null = null;
private lookupRetries = 0;
@@ -70,19 +70,16 @@ export class SkinTestController {
private runAttack(): void {
if (!this.active) return;
if (this.myPlayer === null) {
const found = this.gameView.playerByClientID(this.clientID);
if (found === null) {
if (++this.lookupRetries >= MAX_PLAYER_LOOKUP_RETRIES) {
console.error("Skin test: gave up finding player");
return;
}
this.scheduleAttack();
// Wait for the player to exist in the GameView before firing — gives the
// worker time to addPlayer() once the spawn execution runs.
if (this.gameView.playerByClientID(this.clientID) === null) {
if (++this.lookupRetries >= MAX_PLAYER_LOOKUP_RETRIES) {
console.error("Skin test: gave up finding player");
return;
}
this.myPlayer = found;
this.scheduleAttack();
return;
}
const troops = Math.floor(this.myPlayer.troops() / 2);
this.eventBus.emit(new SendAttackIntentEvent(null, troops));
this.eventBus.emit(new SendAttackIntentEvent(null, INITIAL_ATTACK_TROOPS));
}
}
+2 -2
View File
@@ -40,14 +40,14 @@ describe("SkinTestController", () => {
return { controller, onAttack, modal, onPreviewEnded };
}
it("schedules an initial attack with half the player's troops", () => {
it("schedules an initial attack with a fixed troop count", () => {
const { controller, onAttack } = makeController();
controller.start();
vi.advanceTimersByTime(200);
expect(onAttack).toHaveBeenCalledTimes(1);
expect(onAttack.mock.calls[0][0]).toMatchObject({
targetID: null,
troops: 50,
troops: 1_000_000,
});
});
@@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest";
import { Config } from "../../../src/core/configuration/Config";
import {
Difficulty,
GameMapSize,
GameMapType,
GameMode,
GameType,
PlayerInfo,
PlayerType,
} from "../../../src/core/game/Game";
import { UserSettings } from "../../../src/core/game/UserSettings";
import { GameConfig } from "../../../src/core/Schemas";
function makeConfig(overrides: Partial<GameConfig> = {}): Config {
const gameConfig: GameConfig = {
gameMap: GameMapType.Iceland,
gameMapSize: GameMapSize.Compact,
gameMode: GameMode.FFA,
gameType: GameType.Singleplayer,
difficulty: Difficulty.Easy,
nations: "disabled",
donateGold: false,
donateTroops: false,
bots: 0,
infiniteGold: true,
infiniteTroops: true,
instantBuild: false,
randomSpawn: true,
...overrides,
};
return new Config(gameConfig, new UserSettings(), false);
}
const humanInfo = (): PlayerInfo =>
new PlayerInfo("test", PlayerType.Human, "client1", "p1", false, null, []);
describe("Config.startManpower with startingTroops override", () => {
it("uses startingTroops when set", () => {
const config = makeConfig({ startingTroops: 10_000_000 });
expect(config.startManpower(humanInfo())).toBe(10_000_000);
});
it("falls back to 1_000_000 for infinite-troops human when override is absent", () => {
const config = makeConfig({ infiniteTroops: true });
expect(config.startManpower(humanInfo())).toBe(1_000_000);
});
});