Skip to content

Commit 1cad36f

Browse files
committed
Extract client screen planning helpers
1 parent 05786f0 commit 1cad36f

File tree

3 files changed

+139
-16
lines changed

3 files changed

+139
-16
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import type { ClientState } from './game-client-phase';
4+
import { deriveClientScreenPlan } from './game-client-screen';
5+
6+
describe('game-client-screen', () => {
7+
it('maps menu, connecting, fleet building, and game over states', () => {
8+
expect(deriveClientScreenPlan('menu', null, null, null, 'https://delta-v.example')).toEqual({
9+
kind: 'menu',
10+
});
11+
expect(deriveClientScreenPlan('connecting', null, null, null, 'https://delta-v.example')).toEqual({
12+
kind: 'connecting',
13+
});
14+
expect(deriveClientScreenPlan('playing_fleetBuilding', null, null, null, 'https://delta-v.example')).toEqual({
15+
kind: 'fleetBuilding',
16+
});
17+
expect(deriveClientScreenPlan('gameOver', 'ABCDE', null, 'invite', 'https://delta-v.example')).toEqual({
18+
kind: 'none',
19+
});
20+
});
21+
22+
it('maps all active play states to the HUD screen', () => {
23+
const states: ClientState[] = [
24+
'playing_astrogation',
25+
'playing_ordnance',
26+
'playing_combat',
27+
'playing_movementAnim',
28+
'playing_opponentTurn',
29+
];
30+
31+
for (const state of states) {
32+
expect(deriveClientScreenPlan(state, null, null, null, 'https://delta-v.example')).toEqual({
33+
kind: 'hud',
34+
});
35+
}
36+
});
37+
38+
it('recovers or preserves invite links on the waiting screen', () => {
39+
expect(deriveClientScreenPlan(
40+
'waitingForOpponent',
41+
'ABCDE',
42+
null,
43+
'invite-token',
44+
'https://delta-v.example',
45+
)).toEqual({
46+
kind: 'waiting',
47+
code: 'ABCDE',
48+
inviteLink: 'https://delta-v.example/?code=ABCDE&playerToken=invite-token',
49+
});
50+
51+
expect(deriveClientScreenPlan(
52+
'waitingForOpponent',
53+
'ABCDE',
54+
'https://saved.example/invite',
55+
'invite-token',
56+
'https://delta-v.example',
57+
)).toEqual({
58+
kind: 'waiting',
59+
code: 'ABCDE',
60+
inviteLink: 'https://saved.example/invite',
61+
});
62+
63+
expect(deriveClientScreenPlan(
64+
'waitingForOpponent',
65+
null,
66+
null,
67+
null,
68+
'https://delta-v.example',
69+
)).toEqual({
70+
kind: 'waiting',
71+
code: '',
72+
inviteLink: null,
73+
});
74+
});
75+
});

src/client/game-client-screen.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { ClientState } from './game-client-phase';
2+
import { buildInviteLink } from './game-client-session';
3+
4+
export type ClientScreenPlan =
5+
| { kind: 'menu' }
6+
| { kind: 'connecting' }
7+
| { kind: 'waiting'; code: string; inviteLink: string | null }
8+
| { kind: 'fleetBuilding' }
9+
| { kind: 'hud' }
10+
| { kind: 'none' };
11+
12+
const HUD_STATES = new Set<ClientState>([
13+
'playing_astrogation',
14+
'playing_ordnance',
15+
'playing_combat',
16+
'playing_movementAnim',
17+
'playing_opponentTurn',
18+
]);
19+
20+
export function deriveClientScreenPlan(
21+
state: ClientState,
22+
gameCode: string | null,
23+
inviteLink: string | null,
24+
storedInviteToken: string | null,
25+
origin: string,
26+
): ClientScreenPlan {
27+
switch (state) {
28+
case 'menu':
29+
return { kind: 'menu' };
30+
case 'connecting':
31+
return { kind: 'connecting' };
32+
case 'waitingForOpponent':
33+
return {
34+
kind: 'waiting',
35+
code: gameCode ?? '',
36+
inviteLink: inviteLink
37+
?? (gameCode && storedInviteToken ? buildInviteLink(origin, gameCode, storedInviteToken) : null),
38+
};
39+
case 'playing_fleetBuilding':
40+
return { kind: 'fleetBuilding' };
41+
case 'gameOver':
42+
return { kind: 'none' };
43+
default:
44+
if (HUD_STATES.has(state)) {
45+
return { kind: 'hud' };
46+
}
47+
return { kind: 'none' };
48+
}
49+
}

src/client/main.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
import { deriveKeyboardAction, type KeyboardAction } from './game-client-keyboard';
5454
import { deriveGameOverPlan } from './game-client-endgame';
5555
import { deriveClientMessagePlan } from './game-client-messages';
56+
import { deriveClientScreenPlan } from './game-client-screen';
5657
import { initAudio, playSelect, playConfirm, playThrust, playCombat, playExplosion, playPhaseChange, playVictory, playDefeat, playWarning, isMuted, setMuted } from './audio';
5758

5859
class GameClient {
@@ -186,8 +187,15 @@ class GameClient {
186187
this.tooltipEl.style.display = 'none';
187188

188189
const entryPlan = deriveClientStateEntryPlan(newState, this.gameState, this.playerId);
190+
const screenPlan = deriveClientScreenPlan(
191+
newState,
192+
this.gameCode,
193+
this.inviteLink,
194+
this.gameCode ? this.getStoredInviteToken(this.gameCode) : null,
195+
window.location.origin,
196+
);
189197

190-
switch (newState) {
198+
switch (screenPlan.kind) {
191199
case 'menu':
192200
this.ui.showMenu();
193201
break;
@@ -196,27 +204,18 @@ class GameClient {
196204
this.ui.showConnecting();
197205
break;
198206

199-
case 'waitingForOpponent':
200-
if (!this.inviteLink && this.gameCode) {
201-
const storedInviteToken = this.getStoredInviteToken(this.gameCode);
202-
if (storedInviteToken) {
203-
this.inviteLink = buildInviteLink(window.location.origin, this.gameCode, storedInviteToken);
204-
}
205-
}
206-
this.ui.showWaiting(this.gameCode ?? '', this.inviteLink);
207+
case 'waiting':
208+
this.inviteLink = screenPlan.inviteLink;
209+
this.ui.showWaiting(screenPlan.code, screenPlan.inviteLink);
207210
break;
208211

209-
case 'playing_fleetBuilding':
212+
case 'fleetBuilding':
210213
this.ui.showFleetBuilding(this.gameState!, this.playerId);
211214
break;
212-
case 'playing_astrogation':
213-
case 'playing_ordnance':
214-
case 'playing_combat':
215-
case 'playing_movementAnim':
216-
case 'playing_opponentTurn':
215+
case 'hud':
217216
this.ui.showHUD();
218217
break;
219-
case 'gameOver':
218+
case 'none':
220219
break;
221220
}
222221

0 commit comments

Comments
 (0)