107 lines
3.7 KiB
Vue
107 lines
3.7 KiB
Vue
<template>
|
|
<v-container>
|
|
<v-row class="d-flex justify-center">
|
|
<v-sheet :elevation="2" width="800">
|
|
<v-progress-linear :model-value="timeLeft" :max="initTimeLeft" :color="moveColor"></v-progress-linear>
|
|
<v-row class="d-flex justify-center">
|
|
<v-col cols="auto">
|
|
<GameStatus :game="props.game" />
|
|
</v-col>
|
|
</v-row>
|
|
<v-row>
|
|
<v-col cols="6">
|
|
<!-- dealer -->
|
|
<Player :stats="props.game.players[0]" :dealer="true" :disabled="!props.dealer || !myTurn" @item="(evt: number) => useItem(evt)" />
|
|
</v-col>
|
|
<v-divider vertical />
|
|
<v-col cols="6">
|
|
<!-- challenger -->
|
|
<Player :stats="props.game.players[1]" :dealer="false" :disabled="props.dealer || !myTurn" @item="(evt: number) => useItem(evt)" />
|
|
</v-col>
|
|
</v-row>
|
|
<v-row v-if="gameOver" class="py-4">
|
|
<v-col cols="3"></v-col>
|
|
<v-col cols="6">
|
|
<v-btn block disabled>THE GAME IS OVER</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row v-else-if="myTurn" class="py-4">
|
|
<v-col cols="1"></v-col>
|
|
<v-col cols="5">
|
|
<v-btn block @click="attack(true)">SHOOT {{ dealerTarget }}</v-btn>
|
|
</v-col>
|
|
<v-col cols="5">
|
|
<v-btn block @click="attack(false)">SHOOT {{ challTarget }}</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row v-else class="py-4">
|
|
<v-col cols="3"></v-col>
|
|
<v-col cols="6">
|
|
<v-btn block disabled>THE {{ currentTurn }} IS CONSIDERING</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
</v-sheet>
|
|
</v-row>
|
|
<v-row class="d-flex justify-center mt-8">
|
|
<v-btn @click="leave">Leave</v-btn>
|
|
</v-row>
|
|
</v-container>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { Game, Action } from '@/lib/game';
|
|
import { computed, ref, watch } from 'vue';
|
|
import { useTimestamp } from '@vueuse/core';
|
|
|
|
export interface Props {
|
|
/**
|
|
* Game state to display.
|
|
*/
|
|
game: Game;
|
|
/**
|
|
* Whether this client is connected as the dealer.
|
|
*/
|
|
dealer: boolean;
|
|
}
|
|
|
|
export interface Emits {
|
|
(e: 'action', v: Action): void;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
const emit = defineEmits<Emits>();
|
|
|
|
const gameOver = computed(() => props.game.winner != null);
|
|
const myTurn = computed(() => !gameOver.value && props.dealer === props.game.dealer);
|
|
const currentTurn = computed(() => props.game.dealer ? 'DEALER' : 'CHALLENGER');
|
|
|
|
const dealerTarget = computed(() => props.dealer ? 'YOURSELF' : 'THE DEALER');
|
|
const challTarget = computed(() => props.dealer ? 'THE CHALLENGER' : 'YOURSELF');
|
|
|
|
const initTimeLeft = ref(15000);
|
|
const timeLeft = ref(15000);
|
|
const timestamp = useTimestamp({
|
|
callback: (t) => {
|
|
timeLeft.value = props.game.deadline - t;
|
|
},
|
|
interval: 60,
|
|
});
|
|
watch(props.game, (now) => {
|
|
initTimeLeft.value = now.deadline - timestamp.value;
|
|
});
|
|
const moveColor = computed(() => timeLeft.value > 3000 ? 'primary' : 'red');
|
|
|
|
function attack(left: boolean) {
|
|
const action = left === props.dealer ? 'self' : 'across';
|
|
emit('action', { action });
|
|
}
|
|
|
|
function useItem(evt: number) {
|
|
const action = evt.toString() as "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7";
|
|
emit('action', { action });
|
|
}
|
|
|
|
function leave() {
|
|
emit('action', { action: 'quit' });
|
|
}
|
|
</script> |