Add game join UI and OnlineGame scaffold

Rename JoinMenu to GameJoinMenu and add a JoinGameObject model + OTP input for entering a 6-digit game code. Introduce a new JoinGameObject interface file. Update OnlineMode.vue to import and render GameJoinMenu and GameEndedMenu, add refs for joiningModel, game, gameField, currentSelectionIndex and gameEndedInformation, wire create/ join handlers (stubbed) and the main game view (Field and InfoField). Refactor LocalGame to OnlineGame, export it as default and add a stubbed drop method; adjust import path formatting. Several functions remain as stubs to be implemented in follow-up commits.
This commit is contained in:
2026-03-08 20:33:21 +01:00
committed by Jonas
parent a5019fc27a
commit 466c1c387d
4 changed files with 70 additions and 6 deletions
@@ -1,5 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
defineEmits(["join"]); import type JoinGameObject from '@/scripts/interfaces/JoinGameObject';
defineEmits(['join']);
let joiningModel = defineModel<JoinGameObject>('joinGameObject', {
required: true,
});
</script> </script>
<template> <template>
@@ -7,9 +13,17 @@ defineEmits(["join"]);
<v-sheet class="text-centered pa-2 w-50" rounded> <v-sheet class="text-centered pa-2 w-50" rounded>
<h1 class="text-center mb-5">Spiel Beitreten</h1> <h1 class="text-center mb-5">Spiel Beitreten</h1>
<h3 class="text-center"> <h3 class="text-center">
Hier kannst du einem bestehenden Spiel beitreten, indem du den Spiel Code eingibst, die dir dein Freund gegeben hat. Hier kannst du einem bestehenden Spiel beitreten, indem du den Spiel Code eingibst, die dir
dein Freund gegeben hat.
</h3> </h3>
<v-divider class="mb-5 mt-5"></v-divider> <v-divider class="mb-5 mt-5"></v-divider>
<v-otp-input
length="6"
v-model="joiningModel.gameCode"
type="number"
inputmode="numeric"
:error="joiningModel.failed"
></v-otp-input>
<div class="d-flex align-center justify-space-evenly ma-4 w-100"> <div class="d-flex align-center justify-space-evenly ma-4 w-100">
<v-btn color="red" @click="$router.push('/')" rounded="xl"> Abbrechen </v-btn> <v-btn color="red" @click="$router.push('/')" rounded="xl"> Abbrechen </v-btn>
<v-btn color="primary" @click="$emit('join')" rounded="xl"> Beitreten </v-btn> <v-btn color="primary" @click="$emit('join')" rounded="xl"> Beitreten </v-btn>
+41 -1
View File
@@ -1,7 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import CreateOrJoinMenu from '@/components/CreateOrJoinMenu.vue'; import CreateOrJoinMenu from '@/components/CreateOrJoinMenu.vue';
import GameCreationMenu from '@/components/GameCreationMenu.vue'; import GameCreationMenu from '@/components/GameCreationMenu.vue';
import GameEndedMenu from '@/components/GameEndedMenu.vue';
import GameJoinMenu from '@/components/GameJoinMenu.vue';
import type { GameSettings } from '@/scripts/interfaces/GameSettings'; import type { GameSettings } from '@/scripts/interfaces/GameSettings';
import type JoinGameObject from '@/scripts/interfaces/JoinGameObject';
import OnlineGame from '@/scripts/logic/onlineMode/OnlineGame';
import type { GameEnded } from '@/scripts/logic/signalR/GameConnection';
import { ref } from 'vue'; import { ref } from 'vue';
enum CurrentState { enum CurrentState {
@@ -18,7 +23,19 @@ let settings = ref<GameSettings>({
fieldSize: { x: 7, y: 6 }, fieldSize: { x: 7, y: 6 },
}); });
let joiningModel = ref<JoinGameObject>({
failed: false,
});
var currentState = ref<CurrentState>(CurrentState.CreateOrJoinSelection); var currentState = ref<CurrentState>(CurrentState.CreateOrJoinSelection);
const game = ref<OnlineGame>(new OnlineGame(settings.value));
const gameField = ref<number[][]>([]);
const currentSelectionIndex = ref<number | null>(null);
const gameEndedInformation = ref<GameEnded | null>(null);
async function createGame() {}
async function tryToJoin() {}
</script> </script>
<template> <template>
@@ -30,9 +47,32 @@ var currentState = ref<CurrentState>(CurrentState.CreateOrJoinSelection);
<GameCreationMenu <GameCreationMenu
v-model:settings="settings" v-model:settings="settings"
@create-game="console.log('create game')" @create-game="createGame()"
v-if="currentState === CurrentState.CreatingGame" v-if="currentState === CurrentState.CreatingGame"
/> />
<GameJoinMenu
:joinGameObject="joiningModel"
v-if="currentState === CurrentState.JoiningGame"
@join="tryToJoin()"
/>
<GameEndedMenu
:game-ended-information="gameEndedInformation"
v-else-if="currentState === CurrentState.EndScreen"
></GameEndedMenu>
<div id="game" v-else>
<div class="game-content">
<Field
:game-state="gameField"
v-model:current-selection-index="currentSelectionIndex"
@click-on-game-field="game.drop(currentSelectionIndex ?? 1)"
/>
<InfoField :msg="game.currentDescription"></InfoField>
</div>
</div>
</template> </template>
<style scoped></style> <style scoped></style>
@@ -0,0 +1,4 @@
export default interface JoinGameObject {
failed: boolean;
gameCode?: number;
}
@@ -1,7 +1,7 @@
import type { GameSettings } from "@/scripts/interfaces/GameSettings"; import type { GameSettings } from '@/scripts/interfaces/GameSettings';
import GameConnection, { type GameEnded, type GameInformationDto } from "../signalR/GameConnection"; import GameConnection, { type GameEnded, type GameInformationDto } from '../signalR/GameConnection';
class LocalGame { class OnlineGame {
public player: GameConnection; public player: GameConnection;
public currentDescription: string = 'Warte auf Spielaufbau ...'; public currentDescription: string = 'Warte auf Spielaufbau ...';
private gameId: string = ''; private gameId: string = '';
@@ -13,4 +13,10 @@ class LocalGame {
constructor(settings: GameSettings) { constructor(settings: GameSettings) {
this.player = new GameConnection(); this.player = new GameConnection();
} }
async drop(index: number){
} }
}
export default OnlineGame;