Skip to content

Commit f3d0fab

Browse files
committed
comment
1 parent 3101b73 commit f3d0fab

3 files changed

Lines changed: 53 additions & 48 deletions

File tree

src/client/ClientGameRunner.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { createPartialGameRecord, replacer } from "../core/Util";
1414
import { ServerConfig } from "../core/configuration/Config";
1515
import { getConfig } from "../core/configuration/ConfigLoader";
16+
import { TestSkinExecution } from "../core/execution/TestSkinExecution";
1617
import { PlayerActions, UnitType } from "../core/game/Game";
1718
import { TileRef } from "../core/game/GameMap";
1819
import { GameMapLoader } from "../core/game/GameMapLoader";
@@ -40,7 +41,6 @@ import {
4041
} from "./InputHandler";
4142
import { endGame, startGame, startTime } from "./LocalPersistantStats";
4243
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
43-
import { TestSkinExecution } from "./TestSkinExecution";
4444
import {
4545
SendAttackIntentEvent,
4646
SendBoatAttackIntentEvent,
@@ -52,6 +52,7 @@ import {
5252
import { createCanvas } from "./Utils";
5353
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
5454
import { GoToPlayerEvent } from "./graphics/layers/Leaderboard";
55+
import { ShowSkinTestModalEvent } from "./graphics/layers/SkinTestWinModal";
5556
import SoundManager from "./sound/SoundManager";
5657
import { ReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier";
5758

@@ -364,14 +365,19 @@ export class ClientGameRunner {
364365
// Start a fresh TestSkinExecution which manages its own modal timeout
365366
this.testSkinExecution = new TestSkinExecution(
366367
this.gameView,
367-
this.eventBus,
368368
this.lobby.clientID,
369369
() => this.isActive,
370370
() => {
371371
// Called when execution requests the modal be shown — stop the game and
372372
// clean up resources first.
373373
this.stop();
374374
},
375+
(targetID, troops) =>
376+
this.eventBus.emit(new SendAttackIntentEvent(targetID, troops)),
377+
(patternName, colorPalette) =>
378+
this.eventBus.emit(
379+
new ShowSkinTestModalEvent(patternName, colorPalette),
380+
),
375381
);
376382
this.testSkinExecution.start();
377383
}
Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
1-
import { EventBus } from "../core/EventBus";
2-
import { GameView, PlayerView } from "../core/game/GameView";
3-
import { ClientID } from "../core/Schemas";
4-
import { ShowSkinTestModalEvent } from "./graphics/layers/SkinTestWinModal";
5-
import { SendAttackIntentEvent } from "./Transport";
1+
import { ColorPalette } from "../CosmeticSchemas";
2+
import { Execution, Game, PlayerID } from "../game/Game";
3+
import { GameView, PlayerView } from "../game/GameView";
4+
import { ClientID } from "../Schemas";
65

7-
export class TestSkinExecution {
6+
export class TestSkinExecution implements Execution {
87
private myPlayer: PlayerView | null = null;
98
private initialAttackTimeoutId: ReturnType<typeof setTimeout> | null = null;
109
private modalTimeoutId: ReturnType<typeof setTimeout> | null = null;
10+
private active = true;
1111

1212
constructor(
1313
private gameView: GameView,
14-
private eventBus: EventBus,
1514
private clientID: ClientID,
16-
17-
private isActive: () => boolean,
18-
// callback to request the runner stop the game before showing the modal
15+
private isRunnerActive: () => boolean,
1916
private onShowModalRequested: () => void,
17+
private onAttackIntent: (targetID: PlayerID | null, troops: number) => void,
18+
private onShowModal: (
19+
patternName: string,
20+
colorPalette: ColorPalette | null,
21+
) => void,
2022
) {}
2123

24+
isActive(): boolean {
25+
return this.active;
26+
}
27+
28+
activeDuringSpawnPhase(): boolean {
29+
return false;
30+
}
31+
32+
init(_mg: Game, _ticks: number): void {}
33+
34+
tick(_ticks: number): void {}
35+
2236
public start() {
2337
// schedule the initial attack
2438
this.scheduleInitialAttack(100);
@@ -30,12 +44,13 @@ export class TestSkinExecution {
3044
}
3145
this.modalTimeoutId = setTimeout(() => {
3246
this.modalTimeoutId = null;
33-
if (!this.isActive()) return;
47+
if (!this.isRunnerActive()) return;
3448
this.showModal();
3549
}, 120000);
3650
}
3751

3852
public stop() {
53+
this.active = false;
3954
if (this.initialAttackTimeoutId !== null) {
4055
clearTimeout(this.initialAttackTimeoutId);
4156
this.initialAttackTimeoutId = null;
@@ -74,7 +89,7 @@ export class TestSkinExecution {
7489
const patternName = myPlayer.cosmetics.pattern.name;
7590
const colorPalette = myPlayer.cosmetics.pattern.colorPalette ?? null;
7691

77-
this.eventBus.emit(new ShowSkinTestModalEvent(patternName, colorPalette));
92+
this.onShowModal(patternName, colorPalette);
7893
}
7994

8095
private scheduleInitialAttack(delayMs: number) {
@@ -84,13 +99,13 @@ export class TestSkinExecution {
8499
}
85100
this.initialAttackTimeoutId = setTimeout(() => {
86101
this.initialAttackTimeoutId = null;
87-
if (!this.isActive()) return;
102+
if (!this.isRunnerActive()) return;
88103
this.initialAttack();
89104
}, delayMs);
90105
}
91106

92107
private initialAttack() {
93-
if (!this.isActive()) return;
108+
if (!this.isRunnerActive()) return;
94109

95110
if (this.myPlayer === null) {
96111
const myPlayer = this.gameView.playerByClientID(this.clientID);
@@ -103,8 +118,6 @@ export class TestSkinExecution {
103118
}
104119

105120
const troopCount = this.myPlayer.troops() ?? 1000000;
106-
this.eventBus.emit(
107-
new SendAttackIntentEvent(null, Math.floor(troopCount / 2)),
108-
);
121+
this.onAttackIntent(null, Math.floor(troopCount / 2));
109122
}
110123
}
Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2-
import { ShowSkinTestModalEvent } from "../../src/client/graphics/layers/SkinTestWinModal";
3-
import { TestSkinExecution } from "../../src/client/TestSkinExecution";
4-
import { SendAttackIntentEvent } from "../../src/client/Transport";
5-
import { EventBus } from "../../src/core/EventBus";
2+
import { TestSkinExecution } from "../../src/core/execution/TestSkinExecution";
63

74
describe("TestSkinExecution", () => {
8-
let eventBus: EventBus;
9-
105
beforeEach(() => {
11-
eventBus = new EventBus();
126
vi.useFakeTimers();
137
});
148

@@ -17,7 +11,7 @@ describe("TestSkinExecution", () => {
1711
vi.restoreAllMocks();
1812
});
1913

20-
it("showModal emits ShowSkinTestModalEvent and prevents scheduled initial attack", () => {
14+
it("showModal calls onShowModal and prevents scheduled initial attack", () => {
2115
const fakePlayer = {
2216
cosmetics: { pattern: { name: "pattern1", colorPalette: { name: "p" } } },
2317
troops: () => 100,
@@ -27,15 +21,17 @@ describe("TestSkinExecution", () => {
2721
playerByClientID: (_: any) => fakePlayer,
2822
} as any;
2923

30-
const spyEmit = vi.spyOn(eventBus, "emit");
3124
const onShowModalRequested = vi.fn();
25+
const onAttackIntent = vi.fn();
26+
const onShowModal = vi.fn();
3227

3328
const exec = new TestSkinExecution(
3429
gameView,
35-
eventBus,
3630
"client1" as any,
3731
() => true,
3832
onShowModalRequested,
33+
onAttackIntent,
34+
onShowModal,
3935
);
4036

4137
exec.start();
@@ -46,21 +42,12 @@ describe("TestSkinExecution", () => {
4642
// Should have requested runner to stop
4743
expect(onShowModalRequested).toHaveBeenCalled();
4844

49-
// Should have emitted the ShowSkinTestModalEvent once with the right payload
50-
const emitted = spyEmit.mock.calls.map((c) => c[0]);
51-
expect(emitted.some((e) => e instanceof ShowSkinTestModalEvent)).toBe(true);
52-
const modalEvent = emitted.find(
53-
(e) => e instanceof ShowSkinTestModalEvent,
54-
) as ShowSkinTestModalEvent;
55-
expect(modalEvent).toBeDefined();
56-
expect(modalEvent.patternName).toBe("pattern1");
45+
// Should have called onShowModal with the right payload
46+
expect(onShowModal).toHaveBeenCalledWith("pattern1", { name: "p" });
5747

58-
// Advance timers past the initial attack delay; since showModal cleared timeouts, no SendAttackIntentEvent should be emitted
48+
// Advance timers past the initial attack delay; since showModal cleared timeouts, no attack should fire
5949
vi.advanceTimersByTime(500);
60-
const emittedAfter = spyEmit.mock.calls.map((c) => c[0]);
61-
expect(emittedAfter.some((e) => e instanceof SendAttackIntentEvent)).toBe(
62-
false,
63-
);
50+
expect(onAttackIntent).not.toHaveBeenCalled();
6451
});
6552

6653
it("start schedules initial attack if not cancelled", () => {
@@ -73,24 +60,23 @@ describe("TestSkinExecution", () => {
7360
playerByClientID: (_: any) => fakePlayer,
7461
} as any;
7562

76-
const spyEmit = vi.spyOn(eventBus, "emit");
63+
const onAttackIntent = vi.fn();
7764

7865
const exec = new TestSkinExecution(
7966
gameView,
80-
eventBus,
8167
"client1" as any,
8268
() => true,
8369
() => {},
70+
onAttackIntent,
71+
() => {},
8472
);
8573

8674
exec.start();
8775

8876
// advance past initial attack delay
8977
vi.advanceTimersByTime(200);
9078

91-
// initial attack should have emitted a SendAttackIntentEvent
92-
expect(
93-
spyEmit.mock.calls.some((c) => c[0] instanceof SendAttackIntentEvent),
94-
).toBe(true);
79+
// initial attack should have called the onAttackIntent callback
80+
expect(onAttackIntent).toHaveBeenCalledWith(null, 50);
9581
});
9682
});

0 commit comments

Comments
 (0)