From 919bb71f19ec504924b1e6eecae1e97f24af4a18 Mon Sep 17 00:00:00 2001 From: jhim Date: Sat, 7 Mar 2026 14:05:28 +0100 Subject: [PATCH] Game Logic in Frontend --- API/Models/Game/GameField.cs | 10 ---- API/Services/GameManager/GameManager.cs | 2 + GUI/src/components/GameEndedMenu.vue | 41 ++++++++++++++ GUI/src/components/game/Field.vue | 8 ++- GUI/src/components/game/InfoField.vue | 14 +++++ GUI/src/routes/LocalMode.vue | 56 +++++++++++++++---- GUI/src/scripts/logic/localMode/LocalGame.ts | 49 ++++++++++------ .../scripts/logic/signalR/GameConnection.ts | 22 ++++---- 8 files changed, 148 insertions(+), 54 deletions(-) create mode 100644 GUI/src/components/GameEndedMenu.vue create mode 100644 GUI/src/components/game/InfoField.vue diff --git a/API/Models/Game/GameField.cs b/API/Models/Game/GameField.cs index 93be1a7..13ce902 100644 --- a/API/Models/Game/GameField.cs +++ b/API/Models/Game/GameField.cs @@ -4,17 +4,7 @@ namespace API.Models.Game; public readonly struct Coordinates { - [JsonConstructor] - public Coordinates(int x, int y) - { - X = x; - Y = y; - } - - [JsonPropertyName("x")] public int X { get; init; } - - [JsonPropertyName("y")] public int Y { get; init; } } diff --git a/API/Services/GameManager/GameManager.cs b/API/Services/GameManager/GameManager.cs index fb3a54d..cd8b3ef 100644 --- a/API/Services/GameManager/GameManager.cs +++ b/API/Services/GameManager/GameManager.cs @@ -69,6 +69,8 @@ public class GameManager(IGameRepository gameRepository, IHubContext +import { computed } from 'vue' +import type { GameEnded } from '@/scripts/logic/signalR/GameConnection' + +const props = defineProps<{ gameEndedInformation: GameEnded | null }>() + +const message = computed(() => { + switch (props.gameEndedInformation?.method) { + case "PlayerDisconnected": + return `Bei Spieler ${props.gameEndedInformation.player?.name} ist die Verbindung abgebrochen` + case "Draw": + return "Das Spielfeld ist voll und es ist ein Unentschieden" + case "Win": + return `Spieler ${props.gameEndedInformation.player?.name} hat gewonnen!` + default: + return "" + } +}) + + + + + \ No newline at end of file diff --git a/GUI/src/components/game/Field.vue b/GUI/src/components/game/Field.vue index 0f621bd..d285d3a 100644 --- a/GUI/src/components/game/Field.vue +++ b/GUI/src/components/game/Field.vue @@ -76,8 +76,8 @@ function selectionUpdate(index: number, active: boolean) { \ No newline at end of file diff --git a/GUI/src/routes/LocalMode.vue b/GUI/src/routes/LocalMode.vue index 817406c..d098c15 100644 --- a/GUI/src/routes/LocalMode.vue +++ b/GUI/src/routes/LocalMode.vue @@ -4,10 +4,14 @@ import GameCreationMenu from '@/components/GameCreationMenu.vue'; import type { GameSettings } from '@/scripts/interfaces/GameSettings'; import LocalGame from '@/scripts/logic/localMode/LocalGame'; import Field from '@/components/game/Field.vue'; +import InfoField from '@/components/game/InfoField.vue'; +import type { GameEnded } from '@/scripts/logic/signalR/GameConnection'; +import GameEndedMenu from '@/components/GameEndedMenu.vue'; enum CurrentState { CreatingGame, Game, + EndScreen, } let settings = ref({ @@ -20,32 +24,60 @@ var currentState = ref(CurrentState.CreatingGame); const game = ref(new LocalGame(settings.value)); const gameField = ref([]); const currentSelectionIndex = ref(null); +const gameEndedInformation = ref(null); game.value.onGameStateChanged = (gameState) => { gameField.value = gameState?.currentField ?? []; }; +game.value.onGameEnded = (gameEndedInfo) => { + gameEndedInformation.value = gameEndedInfo; + currentState.value = CurrentState.EndScreen; +} + async function startGame() { - await game.value.start(); + await game.value.start(settings.value); currentState.value = CurrentState.Game; } - + diff --git a/GUI/src/scripts/logic/localMode/LocalGame.ts b/GUI/src/scripts/logic/localMode/LocalGame.ts index ca01e47..a99bd5c 100644 --- a/GUI/src/scripts/logic/localMode/LocalGame.ts +++ b/GUI/src/scripts/logic/localMode/LocalGame.ts @@ -2,33 +2,34 @@ import type { GameSettings } from '@/scripts/interfaces/GameSettings'; import GameConnection, { type GameIdentifier, type GameInformationDto, + type GameEnded, } from '../signalR/GameConnection'; class LocalGame { - public _settings: GameSettings; public player1: GameConnection; public player2: GameConnection; + public currentDescription: string = 'Warte auf Spielaufbau...'; private gameId: string = ''; public gameState: GameInformationDto | undefined; public onGameStateChanged?: (gameState: GameInformationDto | undefined) => void; + public onGameEnded?: (gameEndedInfo: GameEnded) => void; constructor(settings: GameSettings) { - this._settings = settings; - - this.player1 = new GameConnection(settings.playerName1); - this.player2 = new GameConnection(settings.playerName2 ?? 'Player 2'); + this.player1 = new GameConnection(); + this.player2 = new GameConnection(); this.player1.onGameStarted = (gameInfo: GameInformationDto) => { console.log('Game started for player 1', gameInfo); - this.updateState(gameInfo); + this.gameState = gameInfo; + this.onGameStateChanged?.(this.gameState); + + this.changePlaceDescription(); }; - + this.player1.onFieldUpdated = (currentState: GameInformationDto) => { - if (this.gameState) { - this.gameState = currentState; - this.onGameStateChanged?.(this.gameState); - } + this.updateState(currentState); + this.changePlaceDescription(); }; this.player1.onGameCreated = (gameIdentifier: GameIdentifier) => { @@ -39,18 +40,24 @@ class LocalGame { this.player2.onGameJoined = (gameIdentifier: GameIdentifier) => { this.gameId = gameIdentifier.gameId; }; + + this.player1.onGameEnded = (gameEndedInfo: GameEnded) => { + this.onGameEnded?.(gameEndedInfo); + }; } - async start() { - await this.player1.connect(); - await this.player2.connect(); + async start(settings: GameSettings) { + await this.player1.connect(settings.playerName1); + await this.player2.connect(settings.playerName2 ?? 'Spieler 2'); - await this.player1.createGame(this._settings.fieldSize); + await this.player1.createGame(settings.fieldSize); } - async updateState(gameInfo: GameInformationDto) { - this.gameState = gameInfo; - this.onGameStateChanged?.(this.gameState); + async updateState(newState: GameInformationDto) { + if (this.gameState) { + this.gameState = newState; + this.onGameStateChanged?.(this.gameState); + } } async drop(index: number) { @@ -60,6 +67,12 @@ class LocalGame { this.player2.drop(this.gameId, index); } } + + changePlaceDescription() { + const playerName = + this.gameState?.currentTurn == 1 ? this.player1.playerName : this.player2.playerName; + this.currentDescription = `${playerName} ist dran mit setzen!`; + } } export default LocalGame; diff --git a/GUI/src/scripts/logic/signalR/GameConnection.ts b/GUI/src/scripts/logic/signalR/GameConnection.ts index e9ff091..44ae1bc 100644 --- a/GUI/src/scripts/logic/signalR/GameConnection.ts +++ b/GUI/src/scripts/logic/signalR/GameConnection.ts @@ -15,36 +15,35 @@ export interface Player { playerTag: number; } -export interface GameEndedDto { - method: string, +export interface GameEnded { + method: string; player?: Player | null; } -export interface GameIdentifier{ +export interface GameIdentifier { gameId: string; gameCode: number; } class GameConnection { private connection: signalR.HubConnection; - public playerName: string; + public playerName: string = 'Spieler'; public onGameCreated?: (gameIdentifier: GameIdentifier) => void; public onGameJoined?: (gameIdentifier: GameIdentifier) => void; public onGameStarted?: (gameInfo: GameInformationDto) => void; public onGameInformation?: (gameInfo: GameInformationDto) => void; public onFieldUpdated?: (currentField: GameInformationDto) => void; - public onGameEnded?: (gameEndedInfo: GameEndedDto) => void; + public onGameEnded?: (gameEndedInfo: GameEnded) => void; public onError?: (error: string) => void; public onGameDestroyed?: () => void; - constructor(playerName: string) { - this.playerName = playerName; + constructor() { this.connection = new signalR.HubConnectionBuilder() .withUrl('/api/gamehub') .withAutomaticReconnect() .build(); - + this.connection.on('GameCreated', (payload: GameIdentifier) => { this.onGameCreated?.(payload); }); @@ -65,7 +64,7 @@ class GameConnection { this.onFieldUpdated?.(payload); }); - this.connection.on('GameEnded', (payload: GameEndedDto) => { + this.connection.on('GameEnded', (payload: GameEnded) => { this.onGameEnded?.(payload); }); @@ -78,7 +77,8 @@ class GameConnection { }); } - async connect() { + async connect(playerName: string) { + this.playerName = playerName; await this.connection.start(); console.log('Connected'); } @@ -95,7 +95,7 @@ class GameConnection { await this.connection.invoke('RequestGameInformation', gameId); } - async drop(gameId: string, column: number){ + async drop(gameId: string, column: number) { await this.connection.invoke('Drop', gameId, column); } }