mirror of
https://github.com/HexCardGames/HexDeck.git
synced 2025-09-03 10:38:40 +02:00
feat(frontend): implement changing the active card deck
This commit is contained in:
29
frontend/src/components/CardDeckShowcase.svelte
Normal file
29
frontend/src/components/CardDeckShowcase.svelte
Normal file
@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import CardDisplay from "./Game/CardDisplay.svelte";
|
||||
|
||||
export let cardComponent;
|
||||
export let cardDeckId: number;
|
||||
export let cardWidth: number;
|
||||
export let cardHeight: number;
|
||||
export let centerDistancePx: number;
|
||||
export let maxRotationDeg: number;
|
||||
|
||||
const SHOWCASED_CARDS: any = {
|
||||
0: [
|
||||
{ CanPlay: true, Card: { Symbol: "1", Color: "red" } },
|
||||
{ CanPlay: true, Card: { Symbol: "9", Color: "green" } },
|
||||
{ CanPlay: true, Card: { Symbol: "action:draw_2", Color: "blue" } },
|
||||
{ CanPlay: true, Card: { Symbol: "action:skip", Color: "yellow" } },
|
||||
{ CanPlay: true, Card: { Symbol: "action:draw_4", Color: "black" } },
|
||||
],
|
||||
1: [
|
||||
{ CanPlay: true, Card: { Symbol: "1", Color: "blue" } },
|
||||
{ CanPlay: true, Card: { Symbol: "F", Color: "green" } },
|
||||
{ CanPlay: true, Card: { Symbol: "action:shuffle", Color: "yellow" } },
|
||||
{ CanPlay: true, Card: { Symbol: "action:skip", Color: "purple" } },
|
||||
{ CanPlay: true, Card: { Symbol: "action:draw", Color: "rainbow" } },
|
||||
],
|
||||
};
|
||||
</script>
|
||||
|
||||
<CardDisplay canPlayCards={true} canUpdateCards={false} {cardComponent} cards={SHOWCASED_CARDS[cardDeckId]} {cardWidth} {cardHeight} {centerDistancePx} {maxRotationDeg} />
|
@ -69,7 +69,13 @@
|
||||
"player_name": "Spielername",
|
||||
"status": "Status",
|
||||
"host": "Host",
|
||||
"you": "Du"
|
||||
"you": "Du",
|
||||
"player": "Spieler",
|
||||
"return_to_game": "Zurück zum Spiel",
|
||||
"selected_card_deck": "Ausgewähltes Kartendeck:",
|
||||
"change_card_deck": "Ändern",
|
||||
"choose_card_deck": "Auswählen",
|
||||
"card_deck_modal": "Wähle ein Kartendeck aus"
|
||||
},
|
||||
"end_screen": {
|
||||
"game_has_ended": "Das Spiel ist vorbei",
|
||||
|
@ -71,7 +71,11 @@
|
||||
"host": "Host",
|
||||
"you": "You",
|
||||
"player": "Player",
|
||||
"return_to_game": "Return to game"
|
||||
"return_to_game": "Return to game",
|
||||
"selected_card_deck": "Selected card deck:",
|
||||
"change_card_deck": "Change",
|
||||
"choose_card_deck": "Choose",
|
||||
"card_deck_modal": "Select a card deck"
|
||||
},
|
||||
"end_screen": {
|
||||
"game_has_ended": "The game has ended",
|
||||
|
@ -10,6 +10,11 @@
|
||||
import { requestJoinRoom } from "../stores/roomStore";
|
||||
import gameStore from "../stores/gameStore";
|
||||
|
||||
let maxRotationDeg = 20;
|
||||
let centerDistancePx = 200;
|
||||
let cardWidth = 100;
|
||||
let cardHeight = 150;
|
||||
|
||||
onMount(async () => {
|
||||
// TODO: check if already connected to room, currently its overwriting the session
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@ -51,7 +56,7 @@
|
||||
{#if $sessionStore.gameState == GameState.Lobby}
|
||||
<div>
|
||||
<!-- Lobby and player list -->
|
||||
<Lobby />
|
||||
<Lobby {cardWidth} {cardHeight} {centerDistancePx} {maxRotationDeg} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@ -59,11 +64,11 @@
|
||||
<div class="size-full">
|
||||
{#if $gameStore.isLobbyOverlayShown}
|
||||
<div class="absolute inset-0 z-10 bg-white/30 dark:bg-black/30 backdrop-blur-sm mt-24">
|
||||
<Lobby />
|
||||
<Lobby {cardWidth} {cardHeight} {centerDistancePx} {maxRotationDeg} />
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Running game -->
|
||||
<Main />
|
||||
<Main {cardWidth} {cardHeight} {centerDistancePx} {maxRotationDeg} />
|
||||
</div>
|
||||
{:else if $sessionStore.gameState == GameState.Ended}
|
||||
<EndScreen />
|
||||
|
@ -65,7 +65,7 @@
|
||||
</header>
|
||||
|
||||
<div class="main-container">
|
||||
<div class="page-slot">
|
||||
<div class="page-slot" style="overflow-y: auto;">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
13
frontend/src/stores/cardDeck.ts
Normal file
13
frontend/src/stores/cardDeck.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import ClassicCard from "../components/Cards/ClassicCard.svelte";
|
||||
import HexV1Card from "../components/Cards/HexV1Card.svelte";
|
||||
|
||||
interface CardDeck {
|
||||
id: number;
|
||||
name: string;
|
||||
cardComponent: any;
|
||||
}
|
||||
|
||||
export const CardDecks: CardDeck[] = [
|
||||
{ id: 0, name: "Classic", cardComponent: ClassicCard },
|
||||
{ id: 1, name: "HexV1", cardComponent: HexV1Card },
|
||||
];
|
@ -80,6 +80,10 @@ interface PlayedCardUpdateObj {
|
||||
Card: Card;
|
||||
}
|
||||
|
||||
interface SetCardDeckReq {
|
||||
CardDeckId: number;
|
||||
}
|
||||
|
||||
interface PlayCardReq {
|
||||
CardIndex?: number;
|
||||
CardData: any;
|
||||
@ -367,6 +371,13 @@ class SessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
setCardDeck(id: number) {
|
||||
let request: SetCardDeckReq = {
|
||||
CardDeckId: id,
|
||||
};
|
||||
this.sendMessage("SetCardDeck", JSON.stringify(request));
|
||||
}
|
||||
|
||||
drawCard() {
|
||||
this.sendMessage("DrawCard", "");
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
<script lang="ts">
|
||||
import RenamePlayer from "../../components/RenamePlayer.svelte";
|
||||
import { Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell, TableSearch, Badge, Button, Modal, Popover, Tooltip } from "flowbite-svelte";
|
||||
import CardDeckShowcase from "../../components/CardDeckShowcase.svelte";
|
||||
import { Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell, TableSearch, Badge, Button, Modal, Popover, Tooltip, Card } from "flowbite-svelte";
|
||||
import { CircleArrowOutUpLeft, Copy, AlertCircle, UserX, Play, TextCursorInput, Gamepad2 } from "lucide-svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { GameState, sessionStore } from "../../stores/sessionStore";
|
||||
import { toggleLobbyOverlay } from "../../stores/gameStore";
|
||||
import { CardDecks } from "../../stores/cardDeck";
|
||||
|
||||
let copied = false;
|
||||
let showLeaveModal = false;
|
||||
@ -16,6 +18,12 @@
|
||||
let showRenameModal = false;
|
||||
let kick_player = "";
|
||||
let showKickModal = false;
|
||||
let showCardDeckModal = false;
|
||||
|
||||
export let maxRotationDeg: number;
|
||||
export let centerDistancePx: number;
|
||||
export let cardWidth: number;
|
||||
export let cardHeight: number;
|
||||
|
||||
function filteredPlayers() {
|
||||
return players.filter((player) => player.Username.toLowerCase().includes(searchQuery.toLowerCase()));
|
||||
@ -85,34 +93,61 @@
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!-- Leave Room Button -->
|
||||
<Button
|
||||
color="none"
|
||||
class="sm:absolute m-2 border-2 border-gray-500 dark:border-gray-300 hover:bg-gray-500 dark:hover:bg-gray-300 hover:text-white dark:hover:text-black rounded-full text-gray-500 dark:text-gray-300"
|
||||
on:click={() => {
|
||||
showLeaveModal = true;
|
||||
}}
|
||||
>
|
||||
<CircleArrowOutUpLeft class="mr-2" />
|
||||
<span>{$_("lobby.leave_game")}</span>
|
||||
</Button>
|
||||
<!-- Modal: Select card deck -->
|
||||
<Modal bind:open={showCardDeckModal} size="md" backdropClass="fixed inset-0 z-40 bg-gray-900 bg-black/50 dark:bg-black/80 backdrop-opacity-50" style="color: unset;" autoclose outsideclose>
|
||||
<div class="text-center">
|
||||
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">
|
||||
{$_("lobby.card_deck_modal")}
|
||||
</h3>
|
||||
<div class="grid justify-center gap-5">
|
||||
{#each CardDecks as cardDeck}
|
||||
<Card style="color: unset;">
|
||||
<span class="mb-[-15px] text-xl">{cardDeck.name}</span>
|
||||
<div class="flex justify-center">
|
||||
<CardDeckShowcase cardComponent={cardDeck.cardComponent} cardDeckId={cardDeck.id} {cardHeight} {cardWidth} centerDistancePx={centerDistancePx * 3} {maxRotationDeg} />
|
||||
</div>
|
||||
<Button
|
||||
on:click={() => {
|
||||
sessionStore.setCardDeck(cardDeck.id);
|
||||
}}>{$_("lobby.choose_card_deck")}</Button
|
||||
>
|
||||
</Card>
|
||||
{/each}
|
||||
</div>
|
||||
<Button on:click={() => (showCardDeckModal = false)} color="alternative" class="mt-5 hover:text-dark hover:bg-gray-100">{$_("lobby.cancel")}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!-- Return to game Button -->
|
||||
{#if sessionStore.getState().gameState !== GameState.Lobby}
|
||||
<div class="flex">
|
||||
<!-- Leave Room Button -->
|
||||
<Button
|
||||
color="none"
|
||||
class="sm:absolute m-2 right-0 border-2 border-gray-500 dark:border-gray-300 hover:bg-gray-500 dark:hover:bg-gray-300 hover:text-white dark:hover:text-black rounded-full text-gray-500 dark:text-gray-300"
|
||||
class="m-2 border-2 border-gray-500 dark:border-gray-300 hover:bg-gray-500 dark:hover:bg-gray-300 hover:text-white dark:hover:text-black rounded-full text-gray-500 dark:text-gray-300"
|
||||
on:click={() => {
|
||||
toggleLobbyOverlay();
|
||||
showLeaveModal = true;
|
||||
}}
|
||||
>
|
||||
<span>{$_("lobby.return_to_game")}</span>
|
||||
<Gamepad2 class="ml-2" />
|
||||
<CircleArrowOutUpLeft class="mr-2" />
|
||||
<span>{$_("lobby.leave_game")}</span>
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
<!-- Return to game Button -->
|
||||
{#if sessionStore.getState().gameState !== GameState.Lobby}
|
||||
<Button
|
||||
color="none"
|
||||
class="m-2 ml-auto border-2 border-gray-500 dark:border-gray-300 hover:bg-gray-500 dark:hover:bg-gray-300 hover:text-white dark:hover:text-black rounded-full text-gray-500 dark:text-gray-300"
|
||||
on:click={() => {
|
||||
toggleLobbyOverlay();
|
||||
}}
|
||||
>
|
||||
<span>{$_("lobby.return_to_game")}</span>
|
||||
<Gamepad2 class="ml-2" />
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Game Status -->
|
||||
<div class="text-center p-6 w-full">
|
||||
<div class="md:mt-[-65px] text-center p-6 w-full">
|
||||
<span
|
||||
>{$_("game_status.game_status")}
|
||||
<Badge color="dark">
|
||||
@ -199,6 +234,29 @@
|
||||
<TableSearch bind:inputValue={searchQuery} placeholder={$_("lobby.search_player")} />
|
||||
{/if}
|
||||
|
||||
<!-- Card deck selection -->
|
||||
{#if sessionStore.getState().gameState == GameState.Lobby}
|
||||
<div class="flex w-full justify-center my-10">
|
||||
<Card style="color: unset;">
|
||||
<span class="text-xl">{$_("lobby.selected_card_deck")}</span>
|
||||
<span class="opacity-80 mb-[-15px]">{CardDecks.find((e) => e.id == $sessionStore.cardDeckId)?.name}</span>
|
||||
<div class="flex justify-center">
|
||||
<CardDeckShowcase
|
||||
cardComponent={CardDecks.find((e) => e.id == $sessionStore.cardDeckId)?.cardComponent}
|
||||
cardDeckId={CardDecks.find((e) => e.id == $sessionStore.cardDeckId)?.id ?? 0}
|
||||
{cardHeight}
|
||||
{cardWidth}
|
||||
centerDistancePx={centerDistancePx * 3}
|
||||
{maxRotationDeg}
|
||||
/>
|
||||
</div>
|
||||
{#if sessionStore.getPlayerPermissions().isHost}
|
||||
<Button on:click={() => (showCardDeckModal = true)}>{$_("lobby.change_card_deck")}</Button>
|
||||
{/if}
|
||||
</Card>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Players Table -->
|
||||
<Table striped hoverable noborder class="mb-16">
|
||||
<TableHead>
|
||||
|
@ -1,17 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type { PlayerObj } from "../../stores/sessionStore";
|
||||
import { derived, get } from "svelte/store";
|
||||
import ClassicCard from "../../components/Cards/ClassicCard.svelte";
|
||||
import CardDisplay from "../../components/Game/CardDisplay.svelte";
|
||||
import { sessionStore } from "../../stores/sessionStore";
|
||||
import OpponentDisplay from "../../components/Game/OpponentDisplay.svelte";
|
||||
import HexV1Card from "../../components/Cards/HexV1Card.svelte";
|
||||
import { CardDecks } from "../../stores/cardDeck";
|
||||
|
||||
let maxRotationDeg = 20;
|
||||
let centerDistancePx = 200;
|
||||
let cardWidth = 100;
|
||||
let cardHeight = 150;
|
||||
let cardComponent = [ClassicCard, HexV1Card][$sessionStore.cardDeckId ?? 0];
|
||||
export let maxRotationDeg: number;
|
||||
export let centerDistancePx: number;
|
||||
export let cardWidth: number;
|
||||
export let cardHeight: number;
|
||||
let cardComponent = CardDecks.find((e) => e.id == $sessionStore.cardDeckId)?.cardComponent;
|
||||
|
||||
let opponents = derived(sessionStore.store, ($store) => $store.players.filter((e) => e.PlayerId != sessionStore.getUserId()));
|
||||
let playerActive = derived(sessionStore.store, ($store) => ($store.playerStates[$store.userId ?? ""] ?? "").Active ?? false);
|
||||
|
Reference in New Issue
Block a user