Feat : various fixes, sounds and multiplayer additions

This commit is contained in:
2024-07-03 23:27:10 +02:00
parent 866a537924
commit 25cf8a55e9
16 changed files with 433 additions and 272 deletions
+18 -17
View File
@@ -15,17 +15,15 @@ const canvas = ref<HTMLCanvasElement | null>(null);
const canvas_resize_temp = ref<HTMLCanvasElement | null>(null);
export interface Props {
colorStart?: [number, number, number];
colorEnd?: [number, number, number];
isTurn?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
colorStart: () => [0, 0.3, 0],
colorEnd: () => [0, 1, 0],
isTurn: () => false,
});
const _colorStart = reactive(props.colorStart);
const _colorEnd = reactive(props.colorEnd);
const _colorStart = reactive([0, 0.3, 0]);
const _colorEnd = reactive([0, 1, 0]);
function initShader() {
return new Promise<void>((resolve, reject) => {
@@ -119,15 +117,19 @@ watch(
}
);
// watch(() => [props.game.currentTurn], () => {
// if (props.game.teams.find() != goStore.self?.team) {
// gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .2, 2: .3 })
// gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 })
// } else {
// gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .3, 2: 0 })
// gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 })
// }
// }, { immediate: true })
watch(
() => [props.isTurn],
() => {
if (props.isTurn) {
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: 0.3, 2: 0 });
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 });
} else {
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: 0.2, 2: 0.3 });
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 });
}
},
{ immediate: true }
);
watch([_colorEnd, _colorStart], () => {
recolorShader();
@@ -153,8 +155,7 @@ onUnmounted(() => {
:class="{ pixelated: settingsStore.shaderPixelated }"
width="480"
height="270"
>test</canvas
>
></canvas>
</template>
<style scoped>
+89 -52
View File
@@ -1,93 +1,128 @@
<script setup lang="ts">
export interface Props {
uuid?: string
card_id?: number
locked?: boolean
wrapped?: boolean
tilted?: boolean
uuid?: string;
card_id?: number;
locked?: boolean;
wrapped?: boolean;
tilted?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
wrapped: () => false,
tilted: () => true,
locked: () => true
})
locked: () => true,
});
const emit = defineEmits(["click"])
const emit = defineEmits(["click"]);
const settingsStore = useSettingsStore()
const settingsStore = useSettingsStore();
const childCount = ref<number>(0)
const childCount = ref<number>(0);
const card = ref<HTMLElement>()
const card = ref<HTMLElement>();
const extension = computed(() => {
if (settingsStore.cardStyle == CardStyles.SVG) {
return ".svg"
return ".svg";
}
return ".png"
})
return ".png";
});
function cardClick() {
if (!props.card_id) return
emit("click")
if (!props.card_id) return;
emit("click");
}
const isZooming = ref<boolean>(false)
const isZooming = ref<boolean>(false);
function mouseenter(e: MouseEvent) {
// User is Right Clicking
if ((e.buttons & 2) == 2) {
isZooming.value = true
isZooming.value = true;
}
// goStore.hoveredCard = props.card_id
}
function mouseleave(e: MouseEvent) {
isZooming.value = false
isZooming.value = false;
}
function rightClickDown(e: MouseEvent) {
isZooming.value = true
isZooming.value = true;
}
function rightClickUp(e: MouseEvent) {
isZooming.value = false
isZooming.value = false;
}
function activateEffect(e: CustomEvent) {
console.log(e.detail, props.uuid)
console.log(e.detail, props.uuid);
}
onMounted(() => {
card.value?.style.setProperty("--random-seed", -Math.random() * 10 + 's')
})
card.value?.style.setProperty("--random-seed", -Math.random() * 10 + "s");
});
</script>
<template>
<div ref="card" class="card" :class="{ active: childCount > 0, wrapped, unlocked: !props.locked, zoom: isZooming }"
@contextmenu.prevent="false" @click.left="cardClick" @mousedown.right="rightClickDown"
@mouseup.right="rightClickUp" @mouseenter="mouseenter" @mouseleave="mouseleave">
<div id="contextMenuButtons" :ref="(el) => { childCount = ((el as Element)?.childElementCount ?? 0) }">
<div
ref="card"
class="card"
:class="{
active: childCount > 0,
wrapped,
unlocked: !props.locked,
zoom: isZooming,
}"
@contextmenu.prevent="false"
@click.left="cardClick"
@mousedown.right="rightClickDown"
@mouseup.right="rightClickUp"
@mouseenter="mouseenter"
@mouseleave="mouseleave"
>
<div
id="contextMenuButtons"
:ref="(el) => { childCount = ((el as Element)?.childElementCount ?? 0) }"
>
<slot></slot>
</div>
<template v-if="props.card_id">
<svg width="180" height="250" v-if="settingsStore.cardStyle == CardStyles.SVG" viewBox="0 0 298 417">
<use :xlink:href="`/cards/${settingsStore.cardStyle}/` + props.card_id.toString().padStart(3, '0') + extension + '#card' + props.card_id"
@activate_effect="activateEffect($event)">
</use>
<svg
width="180"
height="250"
v-if="settingsStore.cardStyle == CardStyles.SVG"
viewBox="0 0 298 417"
>
<use
:xlink:href="
`/cards/${settingsStore.cardStyle}/` +
props.card_id.toString().padStart(3, '0') +
extension +
'#card' +
props.card_id
"
@activate_effect="activateEffect($event)"
></use>
</svg>
<img :class="`card_image`" v-else
:src="`/cards/${settingsStore.cardStyle}/` + props.card_id.toString().padStart(3, '0') + extension"
draggable="false">
<img
:class="`card_image`"
v-else
:src="
`/cards/${settingsStore.cardStyle}/` +
props.card_id.toString().padStart(3, '0') +
extension
"
draggable="false"
/>
</template>
<img :class="`card_image`" v-else :src="'/cards/background.png'" draggable="false">
<img
:class="`card_image`"
v-else
:src="'/cards/background.png'"
draggable="false"
/>
</div>
</template>
<style scoped>
@@ -100,14 +135,14 @@ onMounted(() => {
justify-content: center;
margin-bottom: 0px;
margin-top: var(--margin-bottom);
transition: all 0.075s cubic-bezier(1, 1.81, .59, 1.42);
transition: all 0.075s cubic-bezier(1, 1.81, 0.59, 1.42);
isolation: isolate;
pointer-events: all;
max-height: var(--max-height);
}
.card.active {
cursor: pointer
cursor: pointer;
}
.card_image {
@@ -117,19 +152,22 @@ onMounted(() => {
}
.card.wrapped {
height: 6px;
height: 1px;
margin: 0;
}
.card.wrapped .card_image {
filter: drop-shadow(.5px .5px .5px gray) drop-shadow(-.5px -.5px .5px gray);
filter: drop-shadow(0.5px 0.5px 0.5px gray)
drop-shadow(-0.5px -0.5px 0.5px gray);
}
.card.unlocked {
margin-top: 0px;
margin-bottom: var(--margin-bottom);
transform: translateY(calc(var(--margin-bottom)*-.25));
filter: drop-shadow(calc(var(--margin-bottom)*.3) var(--margin-bottom) 1px rgba(0, 0, 0, .60));
transform: translateY(calc(var(--margin-bottom) * -0.25));
filter: drop-shadow(
calc(var(--margin-bottom) * 0.3) var(--margin-bottom) 1px rgba(0, 0, 0, 0.6)
);
cursor: pointer;
}
@@ -147,12 +185,11 @@ onMounted(() => {
bottom: 0;
width: 99%;
transform: translateY(-0px);
transition: 0.1s transform cubic-bezier(1, 1.81, .59, 1.42);
transition: 0.1s transform cubic-bezier(1, 1.81, 0.59, 1.42);
}
.card.active:hover #contextMenuButtons {
transform: translateY(100%);
}
</style>
<style>
@@ -167,9 +204,9 @@ onMounted(() => {
}
@keyframes gelatine {
from,
to {}
to {
}
25% {
transform: scale(1, 1.5);
@@ -187,7 +224,7 @@ onMounted(() => {
.svg_resource_icon {
cursor: pointer;
animation: gelatine 0.3s normal;
transition: transform .1s cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition: transform 0.1s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.svg_resource_icon:active {
+12 -1
View File
@@ -55,13 +55,24 @@ function endAnimation() {
animationCallbacks.value.forEach((p) => p());
}
containerEvents.set(props.container._id, {
watch(
props.container,
(newValue, oldValue) => {
if (oldValue) {
containerEvents.delete(oldValue._id);
}
if (newValue) {
containerEvents.set(newValue._id, {
populateContainer,
shuffleContainer,
getCardsPosition,
startAnimation,
endAnimation,
});
}
},
{ immediate: true }
);
async function onEnter(_el: any, done: () => void) {
animationCallbacks.value.push(done);
+6 -1
View File
@@ -3,6 +3,7 @@ import type { Container } from "~/netcode/interfaces";
export interface Props {
market: Container;
marketStack: Container;
fire_gems: Container;
}
@@ -28,7 +29,11 @@ function fireGemCardClick(e: string) {
<template>
<div id="market">
<CardPreview :tilted="false" :card_id="undefined"></CardPreview>
<CardsGrouped
:container="marketStack"
:wrapped="true"
:hidden="true"
></CardsGrouped>
<div class="separator"></div>
<CardsGrouped
:container="market"
+10 -7
View File
@@ -1,19 +1,22 @@
<script setup lang="ts">
import type { Player } from '~/netcode/Player';
import type { Player } from "~/netcode/interfaces";
export interface Props {
player: Player;
}
const props = defineProps<Props>()
const props = defineProps<Props>();
</script>
<template>
<div class="player-icon-container">
<img class="avatar" v-if="props.player.image" :src="props.player.image" draggable="false">
<b class="player-name">{{ props.player.username }}</b>
<img
class="avatar"
v-if="props.player.user.avatar"
:src="`https://cdn.discordapp.com/avatars/${props.player.user.userId}/${props.player.user.avatar}.webp`"
draggable="false"
/>
<b class="player-name">{{ props.player.user.username }}</b>
</div>
</template>
@@ -31,7 +34,7 @@ const props = defineProps<Props>()
.player-name {
font-size: 20px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
margin-left: 10px;
color: white;
+39 -28
View File
@@ -1,43 +1,54 @@
<script setup lang="ts">
import goStore from '@/netcode';
import type { Player } from "~/netcode/Player";
import type { Game, Player } from "~/netcode/interfaces";
const emit = defineEmits(['click'])
const view = ref<"main" | "discard">("main")
function show(data: "main" | "discard") {
view.value = data
}
const emit = defineEmits(["click"]);
export interface Props {
game: Game;
player: Player;
focused?: Player
focused?: boolean;
isTurn?: boolean;
self?: boolean;
}
const props = defineProps<Props>()
const props = withDefaults(defineProps<Props>(), {
isTurn: () => false,
self: () => false,
});
</script>
<template>
<div class="player_list_element" @click="$emit('click')"
:class="{ self: props.player == goStore.self, focused: props.focused == props.player, isTurn: props.player.team == goStore.current_game?.current_turn }">
<div
class="player_list_element"
@click="$emit('click')"
:class="{
self,
focused,
isTurn,
}"
>
<div id="player_banner" class="small">
<div class="ready-bubble not-ready" v-if="!props.player.ready && !goStore.current_game?.started">Pas encore
prêt...
<!-- <div
class="ready-bubble not-ready"
v-if="!props.player.ready && !goStore.current_game?.started"
>
Pas encore prêt...
</div>
<div class="ready-bubble ready" v-if="props.player.ready && !goStore.current_game?.started">Prêt!</div>
<div
class="ready-bubble ready"
v-if="props.player.ready && !goStore.current_game?.started"
>
Prêt!
</div> -->
<div id="player_info">
<PlayerIcon :player="props.player"></PlayerIcon>
</div>
<div id="player_stats">
<template v-if="goStore.current_game?.started">
<PlayerStats :player="props.player">
</PlayerStats>
</template>
<!-- <template v-if="goStore.current_game?.started"> -->
<PlayerStats :game="props.game" :player="props.player"> </PlayerStats>
<!-- </template> -->
</div>
</div>
</div>
</template>
@@ -45,15 +56,15 @@ const props = defineProps<Props>()
.player_list_element {
border-radius: 10px;
margin: 10px;
filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, .60));
filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, 0.6));
transform: translate(-2.5px, -7.5px);
backdrop-filter: blur(5px);
display: flex;
justify-content: center;
box-sizing: border-box;
transition: all .1s;
transition: all 0.1s;
width: 300px;
cursor: pointer
cursor: pointer;
}
.player_list_element::before {
@@ -65,7 +76,7 @@ const props = defineProps<Props>()
left: 0;
width: 100%;
height: 100%;
opacity: .5;
opacity: 0.5;
}
.player_list_element.focused {
@@ -74,7 +85,7 @@ const props = defineProps<Props>()
}
.player_list_element.isTurn::before {
opacity: .1;
opacity: 0.1;
}
.player_list_element.self::before {
@@ -94,7 +105,7 @@ const props = defineProps<Props>()
}
#player_banner.small {
flex-direction: row
flex-direction: row;
}
#player_banner > * {
+7 -2
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { endTurn, lockCard } from "~/netcode";
import { endTurn, joinGame, lockCard, startGame } from "~/netcode";
import type { Game, Player } from "~/netcode/interfaces";
import { useTurnStore } from "~/stores/turnStore";
@@ -105,7 +105,6 @@ function boardCardClick(cardUUID: string) {
<template>
<div class="player" :class="{ turn: isPlayerTurn, self: isSelf }">
<div id="player_banner" class="big">test</div>
<div id="turn_buttons">
<button
id="end_turn"
@@ -115,6 +114,12 @@ function boardCardClick(cardUUID: string) {
>
END TURN
</button>
<button class="end_turn_button turn_icon" @click="startGame(game._id)">
Start game
</button>
<button class="end_turn_button turn_icon" @click="joinGame(game._id)">
Join game
</button>
<div
class="warning"
+15 -3
View File
@@ -1,18 +1,30 @@
<script setup lang="ts">
import type { Player } from "~/netcode/interfaces";
import { playSound } from "~/helpers/audioHelpers";
import type { Game, Player } from "~/netcode/interfaces";
export interface Props {
game: Game;
player: Player;
}
const props = defineProps<Props>();
const team = computed(() =>
props.game.teams.find((t) =>
t.players.map((p) => p._id).includes(props.player._id)
)
);
const isTurn = computed(
() => team.value && team.value?._id == props.game.currentTurn.currentTeam
);
</script>
<template>
<div class="turn_icon">
<img src="/img/champion-shield.png" class="icon_img" draggable="false" />
<span class="turn_icon_number">
{{ player.team?.health }}
{{ team?.health }}
</span>
</div>
<!-- <div class="turn_icon" v-if="showTurnData">
@@ -30,7 +42,7 @@ const props = defineProps<Props>();
<div class="turn_icon">
<img src="/img/fuck.svg" class="icon_img" draggable="false" />
<span class="turn_icon_number">{{
(player.turn?.discard_markers ?? 0) + (player.turn?.fuck_u_markers ?? 0)
(isTurn && (player.discardMarkers ?? 0) + (player.fuckUMarkers ?? 0)) || 0
}}</span>
</div>
</template>
+21 -3
View File
@@ -48,7 +48,25 @@ export async function whoami() {
export async function createGame() {
const config = useRuntimeConfig();
await $fetch<User>(new URL("/games", config.public.endpoint).href, {
const res = await $fetch<Game>(new URL("/games", config.public.endpoint).href, {
method: "POST",
credentials: "include",
});
console.log(res)
navigateTo("/game/" + res._id)
}
export async function startGame(game: string) {
const config = useRuntimeConfig();
const res = await $fetch<Game>(new URL(`games/${game}/start`, config.public.endpoint).href, {
method: "POST",
credentials: "include",
});
}
export async function joinGame(game: string) {
const config = useRuntimeConfig();
const res = await $fetch<Game>(new URL(`games/${game}/join`, config.public.endpoint).href, {
method: "POST",
credentials: "include",
});
@@ -56,7 +74,7 @@ export async function createGame() {
export async function endTurn(game: string) {
const config = useRuntimeConfig();
await $fetch<User>(
await $fetch(
new URL(`games/${game}/endTurn`, config.public.endpoint).href,
{
method: "POST",
@@ -67,7 +85,7 @@ export async function endTurn(game: string) {
export async function lockCard(game: string, cardId: string) {
const config = useRuntimeConfig();
await $fetch<User>(
await $fetch(
new URL(`games/${game}/turn/lockCard/${cardId}`, config.public.endpoint).href,
{
method: "POST",
+7
View File
@@ -16,10 +16,13 @@ export interface Game extends GameObject {
marketStack: Container;
teams: Team[];
currentTurn: PlayingTurn;
started: boolean
owner: User
}
export interface Team extends GameObject {
players: Player[];
health: number;
}
export interface Player extends GameObject {
@@ -28,10 +31,14 @@ export interface Player extends GameObject {
hand: Container;
stack: Container;
user: User;
discardMarkers: number
fuckUMarkers: number
}
export interface User extends GameObject {
userId: string;
avatar: string;
username: string;
}
export interface Container extends GameObject {
+56 -16
View File
@@ -3,9 +3,11 @@ import LoadingScreen from "~/components/LoadingScreen.vue";
import { subscribe } from "~/netcode";
import { containerEvents, delay } from "~/netcode/events";
import { type Container, type Game, type Player } from "~/netcode/interfaces";
import { playSound } from "~/helpers/audioHelpers";
const config = useRuntimeConfig();
const settingsStore = useSettingsStore();
const authStore = useAuthStore();
const route = useRoute();
const gameId = route.params.id;
@@ -76,9 +78,10 @@ eventSource.addEventListener("message", async (message) => {
queueAction(async () => {
await containerEvents.get(data.container)?.startAnimation();
});
const fromContainer = containers.value[data.container.id as string];
const fromContainer = containers.value[data.container._id as string];
queueAction(async () => {
for (const card of data.container.cards) {
playSound("/sounds/distribute.mp3");
fromContainer.cards.push(card);
await delay(settingsStore.animationSpeed);
}
@@ -88,6 +91,8 @@ eventSource.addEventListener("message", async (message) => {
});
} else if (data.type == "shuffleContainer") {
queueAction(async () => {
playSound("/sounds/shuffle.mp3");
// TODO : IMPORTANT : find a better way than containerEvents
await containerEvents.get(data.container)?.shuffleContainer();
});
} else if (data.type == "distributeCards") {
@@ -108,6 +113,7 @@ eventSource.addEventListener("message", async (message) => {
});
queueAction(async () => {
playSound("/sounds/distribute.mp3");
toContainer.cards.push(card);
await delay(settingsStore.animationSpeed);
});
@@ -119,16 +125,27 @@ eventSource.addEventListener("message", async (message) => {
});
} else if (data.type == "endTurn") {
queueAction(async () => {
current_game.currentTurn.cardsLocked = [];
current_game.currentTurn.effectsUsed = [];
current_game.currentTurn.damage = 0;
current_game.currentTurn.gold = 0;
current_game.currentTurn = data.currentTurn;
await delay(settingsStore.animationSpeed);
const playingPlayer = current_game.teams.find(
(t) => t._id == current_game.currentTurn.currentTeam
)?.players[0];
console.log(playingPlayer);
if (playingPlayer) {
focusPlayer(playingPlayer);
await delay(settingsStore.animationSpeed);
}
});
} else if (data.type == "lockCard") {
queueAction(async () => {
playSound("/sounds/lock.mp3");
current_game.currentTurn.cardsLocked.push(data.card);
});
} else if (data.type == "joinGame") {
queueAction(async () => {
playSound("/sounds/teleport.mp3");
current_game.teams.push(data.team);
});
} else if (data.type == "useEffect") {
queueAction(async () => {
current_game.currentTurn.effectsUsed.push(data.effect);
@@ -141,8 +158,6 @@ eventSource.addEventListener("message", async (message) => {
}
});
const authStore = useAuthStore();
const playerList = computed(() =>
game.value!.teams.reduce((acc, t) => acc.concat(t.players), [] as Player[])
);
@@ -151,6 +166,13 @@ const selfPlayer = computed(() =>
? playerList.value?.find((p) => p.user.userId == authStore.selfUser.userId)
: undefined
);
const isTurn = computed(
() =>
selfPlayer.value &&
game.value &&
game.value.teams.find((t) => t.players.includes(selfPlayer.value!))?._id ==
game.value.currentTurn.currentTeam
);
// TODO : current turn sound
@@ -161,6 +183,8 @@ const selfPlayer = computed(() =>
const showResults = ref(false);
const isWinner = ref(false);
const loaded = ref(false);
const discard_unwrapped = ref(false);
const focusedPlayer = ref<Player>(selfPlayer.value ?? playerList.value[0]);
function showLoserScreen() {
// playSound('/sounds/ding.mp3')
@@ -174,9 +198,6 @@ function showWinnerScreen() {
isWinner.value = true;
}
const discard_unwrapped = ref(false);
const focusedPlayer = ref<Player>(selfPlayer.value ?? playerList.value[0]);
// const clientStore = useClientSideStore()
// const restack_discarded_champion = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CHAMPION, target)
// const restack_discarded_card = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CARD, target)
@@ -202,7 +223,10 @@ onUnmounted(() => {
<template>
<div @contextmenu.prevent="false" id="game_board" v-if="game">
<AnimatedBackground @ready="loaded = true"></AnimatedBackground>
<AnimatedBackground
@ready="loaded = true"
:isTurn="isTurn ?? false"
></AnimatedBackground>
<div id="stack_discard">
<div id="stack">
<CardsGrouped
@@ -223,14 +247,25 @@ onUnmounted(() => {
</div>
</div>
<MarketCards :market="game.market" :fire_gems="game.fireGems"></MarketCards>
<MarketCards
:market="game.market"
:fire_gems="game.fireGems"
:marketStack="game.marketStack"
></MarketCards>
<!-- <div id="player_list_container">
<div id="player_list_container">
<div id="player_list">
<PlayerListElement v-for="p in playerList" :player="p" @click="focusPlayer(p)" :focused="focusedPlayer">
<PlayerListElement
v-for="p in playerList"
:player="p"
:game="game"
@click="focusPlayer(p)"
:focused="focusedPlayer == p"
:self="selfPlayer == p"
>
</PlayerListElement>
</div>
</div> -->
</div>
<div id="current_player">
<PlayerSlot
@@ -273,7 +308,12 @@ onUnmounted(() => {
height: 100%;
}
#player_list,
#player_list {
display: flex;
flex-direction: inherit;
justify-content: center;
}
#current_player {
overflow: auto;
position: relative;
+8 -6
View File
@@ -20,10 +20,13 @@ eventSource.addEventListener("message", (message) => {
throw new Error("Couldn't delete game");
}
games.value.splice(index, 1);
} else if (data.type == "update") {
throw new Error("not implemented");
} else if (data.type == "joinGame") {
games.value.find((g) => g._id == data.game)?.teams.push(data.team);
} else if (data.type == "start") {
throw new Error("not implemented");
const targetGame = games.value.find((g) => g._id == data.game);
if (targetGame) {
targetGame.started = true;
}
}
});
@@ -58,9 +61,8 @@ onUnmounted(() => {
authStore.selfUser.userId == g.owner.userId ? 'own_game' : ''
"
>
<template v-if="g.started && g.ended"> GAME OVER </template>
<template
v-else-if="
v-if="
g.teams.every((t) =>
t.players.every(
(p) => p.user.userId !== authStore.selfUser.userId
@@ -101,7 +103,7 @@ onUnmounted(() => {
<img
@click="router.push('/rules')"
class="overlay-image"
src="https://static.wikia.nocookie.net/umineko/img/9/9d/PC_Erika.Casual.Sprite_36.png"
src="https://static.wikia.nocookie.net/umineko/images/9/9d/PC_Erika.Casual.Sprite_36.png"
alt="pétasse"
/>
<div class="overlay-button">
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.