Improve title box with turn indicator
This commit is contained in:
parent
7cbeef6482
commit
6bb62b9c87
|
@ -97,6 +97,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
#title-box {
|
#title-box {
|
||||||
|
font-size: 1.25em;
|
||||||
padding-top: 0.25em;
|
padding-top: 0.25em;
|
||||||
padding-bottom: 0.25em;
|
padding-bottom: 0.25em;
|
||||||
}
|
}
|
||||||
|
@ -184,6 +185,29 @@ body {
|
||||||
background-color: var(--color-on-primary);
|
background-color: var(--color-on-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.player-name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-name.player-black {
|
||||||
|
color: var(--color-primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-name.player-white {
|
||||||
|
color: var(--color-on-primary);
|
||||||
|
text-shadow:
|
||||||
|
-1px -1px 0 var(--color-neutral-900),
|
||||||
|
1px -1px 0 var(--color-neutral-900),
|
||||||
|
-1px 1px 0 var(--color-neutral-900),
|
||||||
|
1px 1px 0 var(--color-neutral-900);
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-name.player-to-play {
|
||||||
|
border: 2px solid var(--color-focus-ring);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.last-move {
|
.last-move {
|
||||||
box-shadow: 0 0 5px 3px var(--color-warning-light);
|
box-shadow: 0 0 5px 3px var(--color-warning-light);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ type BoardCell = null | 'black' | 'white';
|
||||||
export class GameInstance {
|
export class GameInstance {
|
||||||
public readonly id: string;
|
public readonly id: string;
|
||||||
public readonly board: BoardCell[][];
|
public readonly board: BoardCell[][];
|
||||||
public currentPlayer: PlayerColor | null;
|
public currentPlayerColor: PlayerColor | null;
|
||||||
public status: GameStatus;
|
public status: GameStatus;
|
||||||
public winner: null | PlayerColor | 'draw';
|
public winner: null | PlayerColor | 'draw';
|
||||||
public players: { black?: string; white?: string };
|
public players: { black?: string; white?: string };
|
||||||
|
@ -20,12 +20,20 @@ export class GameInstance {
|
||||||
this.board = Array.from({ length: this.boardSize }, () =>
|
this.board = Array.from({ length: this.boardSize }, () =>
|
||||||
Array(this.boardSize).fill(null),
|
Array(this.boardSize).fill(null),
|
||||||
);
|
);
|
||||||
this.currentPlayer = null;
|
this.currentPlayerColor = null;
|
||||||
this.status = 'waiting';
|
this.status = 'waiting';
|
||||||
this.winner = null;
|
this.winner = null;
|
||||||
this.players = {};
|
this.players = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCurrentPlayerId(): string | undefined {
|
||||||
|
if (this.currentPlayerColor === 'black') {
|
||||||
|
return this.players.black;
|
||||||
|
} else {
|
||||||
|
return this.players.white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getPlayerCount(): number {
|
public getPlayerCount(): number {
|
||||||
return Object.values(this.players).filter(Boolean).length;
|
return Object.values(this.players).filter(Boolean).length;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +60,7 @@ export class GameInstance {
|
||||||
|
|
||||||
// If both players have joined, start the game.
|
// If both players have joined, start the game.
|
||||||
if (this.players.black && this.players.white) {
|
if (this.players.black && this.players.white) {
|
||||||
this.currentPlayer = 'black';
|
this.currentPlayerColor = 'black';
|
||||||
this.status = 'playing';
|
this.status = 'playing';
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -77,7 +85,7 @@ export class GameInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate it's the player's turn
|
// Validate it's the player's turn
|
||||||
if (this.currentPlayer !== playerColor) {
|
if (this.currentPlayerColor !== playerColor) {
|
||||||
return { success: false, error: 'Not your turn' };
|
return { success: false, error: 'Not your turn' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +107,7 @@ export class GameInstance {
|
||||||
if (this.checkWin(row, col, playerColor)) {
|
if (this.checkWin(row, col, playerColor)) {
|
||||||
this.winner = playerColor;
|
this.winner = playerColor;
|
||||||
this.status = 'finished';
|
this.status = 'finished';
|
||||||
this.currentPlayer = null;
|
this.currentPlayerColor = null;
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,12 +115,12 @@ export class GameInstance {
|
||||||
if (this.moveCount === this.boardSize * this.boardSize) {
|
if (this.moveCount === this.boardSize * this.boardSize) {
|
||||||
this.winner = 'draw';
|
this.winner = 'draw';
|
||||||
this.status = 'finished';
|
this.status = 'finished';
|
||||||
this.currentPlayer = null;
|
this.currentPlayerColor = null;
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch turns
|
// Switch turns
|
||||||
this.currentPlayer = playerColor === 'black' ? 'white' : 'black';
|
this.currentPlayerColor = playerColor === 'black' ? 'white' : 'black';
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,20 @@
|
||||||
import { GameInstance } from '../game/game-instance';
|
import { GameInstance } from '../game/game-instance';
|
||||||
|
|
||||||
export type GameStateType = Pick<
|
|
||||||
GameInstance,
|
|
||||||
'id' | 'board' | 'currentPlayer' | 'status' | 'winner' | 'players'
|
|
||||||
>;
|
|
||||||
|
|
||||||
export function renderGameBoardHtml(
|
export function renderGameBoardHtml(
|
||||||
gameState: GameStateType,
|
game: GameInstance,
|
||||||
playerId: string,
|
playerId: string,
|
||||||
): string {
|
): string {
|
||||||
let boardHtml = '<div id="game-board" class="game-board-grid">';
|
let boardHtml = '<div id="game-board" class="game-board-grid">';
|
||||||
|
|
||||||
const currentPlayerColor =
|
const currentPlayerColor =
|
||||||
Object.entries(gameState.players).find(([_, id]) => id === playerId)?.[0] ||
|
Object.entries(game.players).find(([_, id]) => id === playerId)?.[0] ||
|
||||||
null;
|
null;
|
||||||
const isPlayersTurn =
|
const isPlayersTurn =
|
||||||
gameState.status === 'playing' &&
|
game.status === 'playing' && game.currentPlayerColor === currentPlayerColor;
|
||||||
gameState.currentPlayer === currentPlayerColor;
|
|
||||||
|
|
||||||
for (let row = 0; row < gameState.board.length; row++) {
|
for (let row = 0; row < game.board.length; row++) {
|
||||||
for (let col = 0; col < gameState.board[row].length; col++) {
|
for (let col = 0; col < game.board[row].length; col++) {
|
||||||
const stone = gameState.board[row][col];
|
const stone = game.board[row][col];
|
||||||
const intersectionId = `intersection-${row}-${col}`;
|
const intersectionId = `intersection-${row}-${col}`;
|
||||||
let stoneHtml = '';
|
let stoneHtml = '';
|
||||||
if (stone) {
|
if (stone) {
|
||||||
|
|
|
@ -74,7 +74,7 @@ export class WebSocketHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.currentPlayer !== playerColor) {
|
if (game.currentPlayerColor !== playerColor) {
|
||||||
this.sendMessage(ws, "Error: It's not your turn");
|
this.sendMessage(ws, "Error: It's not your turn");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -142,22 +142,28 @@ export class WebSocketHandler {
|
||||||
ws.send(updatedBoardHtml);
|
ws.send(updatedBoardHtml);
|
||||||
|
|
||||||
if (game.status === 'finished') {
|
if (game.status === 'finished') {
|
||||||
if (game.winner === 'draw') {
|
if (game.winnerColor === 'draw') {
|
||||||
this.sendMessageToGame(gameId, 'Game ended in draw.');
|
this.sendMessageToGame(gameId, 'Game ended in draw.');
|
||||||
} else if (game.winner) {
|
} else if (game.winnerColor) {
|
||||||
this.sendMessageToGame(
|
this.sendMessageToGame(
|
||||||
gameId,
|
gameId,
|
||||||
`${game.winner.toUpperCase()} wins!`,
|
`${game.winnerColor.toUpperCase()} wins!`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (game.status === 'playing') {
|
} else if (game.status === 'playing') {
|
||||||
const clientPlayerColor = Object.entries(game.players).find(
|
const clientPlayerColor = Object.entries(game.players).find(
|
||||||
([_, id]) => id === playerId,
|
([_, id]) => id === playerId,
|
||||||
)?.[0] as ('black' | 'white') | undefined;
|
)?.[0] as ('black' | 'white') | undefined;
|
||||||
if (game.currentPlayer && clientPlayerColor === game.currentPlayer) {
|
if (
|
||||||
|
game.currentPlayerColor &&
|
||||||
|
clientPlayerColor === game.currentPlayerColor
|
||||||
|
) {
|
||||||
this.sendMessage(ws, "It's your turn!");
|
this.sendMessage(ws, "It's your turn!");
|
||||||
} else if (game.currentPlayer) {
|
} else if (game.currentPlayerColor) {
|
||||||
this.sendMessage(ws, `Waiting for ${game.currentPlayer}'s move.`);
|
this.sendMessage(
|
||||||
|
ws,
|
||||||
|
`Waiting for ${game.currentPlayerColor}'s move.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if (game.status === 'waiting') {
|
} else if (game.status === 'waiting') {
|
||||||
this.sendMessage(ws, 'Waiting for another player...');
|
this.sendMessage(ws, 'Waiting for another player...');
|
||||||
|
@ -220,7 +226,7 @@ export class WebSocketHandler {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'finished': {
|
case 'finished': {
|
||||||
switch (game.winner) {
|
switch (game.winnerColor) {
|
||||||
case 'draw': {
|
case 'draw': {
|
||||||
message = 'Game ended in draw.';
|
message = 'Game ended in draw.';
|
||||||
break;
|
break;
|
||||||
|
@ -244,6 +250,7 @@ export class WebSocketHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private playerTag(gameId: string, playerId: string) {
|
private playerTag(gameId: string, playerId: string) {
|
||||||
|
// Determine whether the player is disconnected
|
||||||
var connectionIcon = `<img src="/icons/disconnected.svg" alt="Disconnected" class="icon" />`;
|
var connectionIcon = `<img src="/icons/disconnected.svg" alt="Disconnected" class="icon" />`;
|
||||||
const connections = this.connections.get(gameId);
|
const connections = this.connections.get(gameId);
|
||||||
if (connections) {
|
if (connections) {
|
||||||
|
@ -254,7 +261,24 @@ export class WebSocketHandler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return `<span>${playerId}${connectionIcon}</span>`;
|
|
||||||
|
// Set the correct name color for the player
|
||||||
|
var colorClass = '';
|
||||||
|
var turnClass = '';
|
||||||
|
const game = this.games.get(gameId);
|
||||||
|
if (game) {
|
||||||
|
if (game.players.white === playerId) {
|
||||||
|
colorClass = 'player-white';
|
||||||
|
} else if (game.players.black === playerId) {
|
||||||
|
colorClass = 'player-black';
|
||||||
|
}
|
||||||
|
if (game.getCurrentPlayerId() === playerId) {
|
||||||
|
turnClass = 'player-to-play';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const classes = `player-name ${colorClass} ${turnClass}`.trim();
|
||||||
|
|
||||||
|
return `<span class="${classes}">${playerId}${connectionIcon}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getGame(gameId: string): GameInstance | undefined {
|
public getGame(gameId: string): GameInstance | undefined {
|
||||||
|
|
Loading…
Reference in New Issue