Convert field to jagged arrays; add local SignalR UI

Change game field representation to jagged arrays (int[][]) for JSON/SignalR compatibility and add conversion helpers in GameManager. Make Coordinates JSON-serializable (constructor and JsonPropertyName attributes). Update GameHubSocket: validate field size, fix JoinGame parameter order/Group join, rename Place->Drop and send proper game id. Add client-side local play support: GameConnection SignalR client, LocalGame orchestration for two local players, and UI components (GameCreationMenu, Slider) plus GameSettings interface. Update LocalMode route to use the new creation UI and start local games. These changes enable reliable serialization over SignalR and a local two-player flow with a creation UI.
This commit is contained in:
Jonas
2026-03-05 21:58:17 +01:00
parent bec5df7e88
commit 0eed8020b8
10 changed files with 396 additions and 39 deletions
+59 -6
View File
@@ -1,12 +1,65 @@
import type { FieldSize } from '@/scripts/interfaces/FieldSize.ts'
import buildFieldArray from '@/scripts/utils/BuildFieldArray'
import type { GameSettings } from '@/scripts/interfaces/GameSettings';
import GameConnection, {
type GameIdentifier,
type GameInformationDto,
} from '../signalR/GameConnection';
class LocalGame {
public field: number[][]
public _settings: GameSettings;
public player1: GameConnection;
public player2: GameConnection;
private gameId: string = '';
constructor(fieldSize: FieldSize) {
this.field = buildFieldArray(fieldSize);
public gameState: GameInformationDto | undefined;
public onGameStateChanged?: (gameState: GameInformationDto | undefined) => void;
constructor(settings: GameSettings) {
this._settings = settings;
this.player1 = new GameConnection(settings.playerName1);
this.player2 = new GameConnection(settings.playerName2 ?? 'Player 2');
this.player1.onGameStarted = (gameInfo: GameInformationDto) => {
console.log('Game started for player 1', gameInfo);
this.gameStarted(gameInfo);
};
this.player1.onFieldUpdated = (currentField: number[][]) => {
if (this.gameState) {
this.gameState.currentField = currentField;
this.onGameStateChanged?.(this.gameState);
}
};
this.player1.onGameCreated = (gameIdentifier: GameIdentifier) => {
this.gameId = gameIdentifier.gameId;
this.player2.joinGame(gameIdentifier.gameCode);
};
this.player2.onGameJoined = (gameIdentifier: GameIdentifier) => {
this.gameId = gameIdentifier.gameId;
};
}
async start() {
await this.player1.connect();
await this.player2.connect();
await this.player1.createGame(this._settings.fieldSize);
}
async gameStarted(gameInfo: GameInformationDto) {
this.gameState = gameInfo;
this.onGameStateChanged?.(this.gameState);
}
async drop(index: number) {
if (this.gameState?.currentTurn == 1) {
this.player1.drop(this.gameId, index);
} else {
this.player2.drop(this.gameId, index);
}
}
}
export default LocalGame
export default LocalGame;