feat: card lock
This commit is contained in:
@@ -1,100 +1,123 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
import gsap from 'gsap';
|
import gsap from "gsap";
|
||||||
import ShaderWorker from '~/assets/workers/ShaderWorker?worker'
|
import ShaderWorker from "~/assets/workers/ShaderWorker?worker";
|
||||||
import type { Game } from '~/netcode/interfaces';
|
import type { Game } from "~/netcode/interfaces";
|
||||||
|
|
||||||
const emit = defineEmits(["ready"])
|
const emit = defineEmits<{
|
||||||
const worker = new ShaderWorker()
|
ready: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
const canvas = ref<HTMLCanvasElement | null>(null)
|
const worker = new ShaderWorker();
|
||||||
const canvas_resize_temp = ref<HTMLCanvasElement | null>(null)
|
|
||||||
|
|
||||||
|
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||||
|
const canvas_resize_temp = ref<HTMLCanvasElement | null>(null);
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
colorStart?: [number, number, number]
|
colorStart?: [number, number, number];
|
||||||
colorEnd?: [number, number, number]
|
colorEnd?: [number, number, number];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
colorStart: () => [0, .3, 0],
|
colorStart: () => [0, 0.3, 0],
|
||||||
colorEnd: () => [0, 1, 0]
|
colorEnd: () => [0, 1, 0],
|
||||||
})
|
});
|
||||||
|
|
||||||
const _colorStart = reactive(props.colorStart)
|
|
||||||
const _colorEnd = reactive(props.colorEnd)
|
|
||||||
|
|
||||||
|
const _colorStart = reactive(props.colorStart);
|
||||||
|
const _colorEnd = reactive(props.colorEnd);
|
||||||
|
|
||||||
function initShader() {
|
function initShader() {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
if (!canvas.value) {
|
if (!canvas.value) {
|
||||||
reject("canvas is null");
|
reject("canvas is null");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const offscreen = canvas.value?.transferControlToOffscreen()
|
const offscreen = canvas.value?.transferControlToOffscreen();
|
||||||
worker.postMessage({
|
worker.postMessage(
|
||||||
command: "initShader",
|
{
|
||||||
canvas: offscreen,
|
command: "initShader",
|
||||||
screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio },
|
canvas: offscreen,
|
||||||
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
|
screen: {
|
||||||
}, [offscreen])
|
height: window.innerHeight * devicePixelRatio,
|
||||||
if (settingsStore.shaderFramerate == 0) {
|
width: window.innerWidth * devicePixelRatio,
|
||||||
killShader()
|
devicePixelRatio,
|
||||||
}
|
},
|
||||||
resizeShader()
|
shaderOptions: {
|
||||||
worker.onmessage = (e) => {
|
quality: settingsStore.shaderQuality,
|
||||||
if (e.data == "ready") {
|
framerate: settingsStore.shaderFramerate,
|
||||||
resolve()
|
},
|
||||||
}
|
},
|
||||||
}
|
[offscreen]
|
||||||
})
|
);
|
||||||
|
if (settingsStore.shaderFramerate == 0) {
|
||||||
|
killShader();
|
||||||
|
}
|
||||||
|
resizeShader();
|
||||||
|
worker.onmessage = (e) => {
|
||||||
|
if (e.data == "ready") {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function recolorShader() {
|
function recolorShader() {
|
||||||
worker.postMessage({
|
worker.postMessage({
|
||||||
command: 'recolorShader',
|
command: "recolorShader",
|
||||||
colorStart: [..._colorStart],
|
colorStart: [..._colorStart],
|
||||||
colorEnd: [..._colorEnd]
|
colorEnd: [..._colorEnd],
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizeShader() {
|
function resizeShader() {
|
||||||
worker.postMessage({
|
worker.postMessage({
|
||||||
command: 'resizeShader',
|
command: "resizeShader",
|
||||||
screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio },
|
screen: {
|
||||||
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
|
height: window.innerHeight * devicePixelRatio,
|
||||||
})
|
width: window.innerWidth * devicePixelRatio,
|
||||||
|
devicePixelRatio,
|
||||||
|
},
|
||||||
|
shaderOptions: {
|
||||||
|
quality: settingsStore.shaderQuality,
|
||||||
|
framerate: settingsStore.shaderFramerate,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function killShader() {
|
function killShader() {
|
||||||
worker.postMessage({
|
worker.postMessage({
|
||||||
command: 'killShader'
|
command: "killShader",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resumeShader() {
|
function resumeShader() {
|
||||||
worker.postMessage({
|
worker.postMessage({
|
||||||
command: 'resumeShader',
|
command: "resumeShader",
|
||||||
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
|
shaderOptions: {
|
||||||
})
|
quality: settingsStore.shaderQuality,
|
||||||
|
framerate: settingsStore.shaderFramerate,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => [settingsStore.shaderFramerate], () => {
|
watch(
|
||||||
|
() => [settingsStore.shaderFramerate],
|
||||||
|
() => {
|
||||||
if (settingsStore.shaderFramerate == 0) {
|
if (settingsStore.shaderFramerate == 0) {
|
||||||
killShader()
|
killShader();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
resumeShader()
|
resumeShader();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
})
|
watch(
|
||||||
|
() => [settingsStore.shaderQuality],
|
||||||
watch(() => [settingsStore.shaderQuality], () => {
|
() => {
|
||||||
resizeShader()
|
resizeShader();
|
||||||
})
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// watch(() => [props.game.currentTurn], () => {
|
// watch(() => [props.game.currentTurn], () => {
|
||||||
// if (props.game.teams.find() != goStore.self?.team) {
|
// if (props.game.teams.find() != goStore.self?.team) {
|
||||||
@@ -106,38 +129,43 @@ watch(() => [settingsStore.shaderQuality], () => {
|
|||||||
// }
|
// }
|
||||||
// }, { immediate: true })
|
// }, { immediate: true })
|
||||||
|
|
||||||
|
|
||||||
watch([_colorEnd, _colorStart], () => {
|
watch([_colorEnd, _colorStart], () => {
|
||||||
recolorShader()
|
recolorShader();
|
||||||
})
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
recolorShader()
|
recolorShader();
|
||||||
await initShader()
|
await initShader();
|
||||||
window.addEventListener("resize", resizeShader, true)
|
window.addEventListener("resize", resizeShader, true);
|
||||||
emit("ready")
|
emit("ready");
|
||||||
})
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener("resize", resizeShader)
|
window.removeEventListener("resize", resizeShader);
|
||||||
killShader()
|
killShader();
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<canvas ref="canvas" id="background" :class="{ pixelated: settingsStore.shaderPixelated }" width="480"
|
<canvas
|
||||||
height="270">test</canvas>
|
ref="canvas"
|
||||||
|
id="background"
|
||||||
|
:class="{ pixelated: settingsStore.shaderPixelated }"
|
||||||
|
width="480"
|
||||||
|
height="270"
|
||||||
|
>test</canvas
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#background {
|
#background {
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#background.pixelated {
|
#background.pixelated {
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+12
-46
@@ -1,6 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import goStore from '~/netcode';
|
|
||||||
// import { useSettingsStore } from '~/stores/settingsStore';
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
uuid?: string
|
uuid?: string
|
||||||
@@ -90,9 +88,6 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -121,47 +116,6 @@ onMounted(() => {
|
|||||||
height: var(--max-height);
|
height: var(--max-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card_image.outlined {
|
|
||||||
outline: 2px solid red;
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes unlocked-float {
|
|
||||||
from {
|
|
||||||
transform: none;
|
|
||||||
filter: drop-shadow(calc(var(--margin-bottom)*.3) var(--margin-bottom) 1px rgba(0, 0, 0, .60));
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: translateY(calc(var(--margin-bottom)*-.25));
|
|
||||||
filter: drop-shadow(calc(var(--margin-bottom)*.3) calc(var(--margin-bottom)*1.5) 1px rgba(0, 0, 0, .60));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes unlocked-rotate {
|
|
||||||
from {
|
|
||||||
rotate: -2deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
rotate: 2deg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.unlocked {
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-bottom: var(--margin-bottom);
|
|
||||||
filter: drop-shadow(calc(var(--margin-bottom)*.3) var(--margin-bottom) 1px rgba(0, 0, 0, .60));
|
|
||||||
animation: 2s infinite alternate ease-in-out unlocked-float, 3.1s infinite alternate ease-in-out unlocked-rotate;
|
|
||||||
animation-delay: var(--random-seed);
|
|
||||||
animation-play-state: running;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:not(.unlocked):hover {
|
|
||||||
animation-play-state: paused;
|
|
||||||
rotate: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.wrapped {
|
.card.wrapped {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -171,6 +125,14 @@ onMounted(() => {
|
|||||||
filter: drop-shadow(.5px .5px .5px gray) drop-shadow(-.5px -.5px .5px gray);
|
filter: drop-shadow(.5px .5px .5px gray) drop-shadow(-.5px -.5px .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));
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.card.zoom {
|
.card.zoom {
|
||||||
transform: scale(200%);
|
transform: scale(200%);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
@@ -194,6 +156,10 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
|
.card.unlocked>* {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
#contextMenuButtons>* {
|
#contextMenuButtons>* {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|||||||
+88
-72
@@ -1,133 +1,149 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { containerEvents, delay } from '~/netcode/events';
|
import { containerEvents, delay } from "~/netcode/events";
|
||||||
import type { Container } from '~/netcode/interfaces';
|
import type { Container, PlayingTurn } from "~/netcode/interfaces";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
container: Container
|
container: Container;
|
||||||
wrapped?: boolean
|
playingTurn?: PlayingTurn;
|
||||||
hidden?: boolean
|
wrapped?: boolean;
|
||||||
|
hidden?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
wrapped: () => false,
|
wrapped: () => false,
|
||||||
hidden: () => false
|
hidden: () => false,
|
||||||
})
|
});
|
||||||
|
|
||||||
const settingsStore = useSettingsStore()
|
const emit = defineEmits(["cardClick"]);
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
const cards = computed(() => {
|
const cards = computed(() => {
|
||||||
if (props.hidden) {
|
if (props.hidden) {
|
||||||
return props.container.cards.map((_v, i) => ({ _id: i, cardId: undefined }))
|
return props.container.cards.map((_v, i) => ({
|
||||||
}
|
_id: i,
|
||||||
return props.container.cards
|
cardId: undefined,
|
||||||
})
|
}));
|
||||||
|
}
|
||||||
|
return props.container.cards;
|
||||||
|
});
|
||||||
|
|
||||||
const animationSpeed = computed(() => settingsStore.animationSpeed + 'ms')
|
const animationSpeed = computed(() => settingsStore.animationSpeed + "ms");
|
||||||
const shuffleAnimationSpeed = computed(() => settingsStore.animationSpeed * 2 + 'ms')
|
const shuffleAnimationSpeed = computed(
|
||||||
function populateContainer() {
|
() => settingsStore.animationSpeed * 2 + "ms"
|
||||||
|
);
|
||||||
|
function populateContainer() {}
|
||||||
|
|
||||||
}
|
const rotate = ref(false);
|
||||||
|
|
||||||
const rotate = ref(false)
|
|
||||||
async function shuffleContainer() {
|
async function shuffleContainer() {
|
||||||
console.log("rotate")
|
rotate.value = true;
|
||||||
rotate.value = true
|
await delay(settingsStore.animationSpeed * 2);
|
||||||
await delay(settingsStore.animationSpeed * 2)
|
rotate.value = false;
|
||||||
rotate.value = false
|
await delay(settingsStore.animationSpeed * 2);
|
||||||
await delay(settingsStore.animationSpeed * 2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCardsPosition(cards: string) {}
|
||||||
|
|
||||||
function getCardsPosition(cards: string) {
|
const animationCallbacks = ref<Array<() => void>>([]);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const animationCallbacks = ref<Array<() => void>>([])
|
|
||||||
|
|
||||||
function startAnimation() {
|
function startAnimation() {
|
||||||
animationCallbacks.value.forEach(p => p())
|
animationCallbacks.value.forEach((p) => p());
|
||||||
animationCallbacks.value = []
|
animationCallbacks.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function endAnimation() {
|
function endAnimation() {
|
||||||
animationCallbacks.value.forEach(p => p())
|
animationCallbacks.value.forEach((p) => p());
|
||||||
}
|
}
|
||||||
|
|
||||||
containerEvents.set(props.container._id, { populateContainer, shuffleContainer, getCardsPosition, startAnimation, endAnimation })
|
containerEvents.set(props.container._id, {
|
||||||
|
populateContainer,
|
||||||
|
shuffleContainer,
|
||||||
|
getCardsPosition,
|
||||||
|
startAnimation,
|
||||||
|
endAnimation,
|
||||||
|
});
|
||||||
|
|
||||||
async function onEnter(_el: any, done: () => void) {
|
async function onEnter(_el: any, done: () => void) {
|
||||||
animationCallbacks.value.push(done)
|
animationCallbacks.value.push(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onLeave(_el: any, done: () => void) {
|
async function onLeave(_el: any, done: () => void) {
|
||||||
animationCallbacks.value.push(done)
|
animationCallbacks.value.push(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onBeforeEnter() {
|
async function onBeforeEnter() {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
containerEvents.delete(props.container._id)
|
containerEvents.delete(props.container._id);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="cards_container" :class="{ wrapped, rotate }">
|
<div class="cards_container" :class="{ wrapped, rotate }">
|
||||||
<TransitionGroup name="list" @enter="onEnter" @leave="onLeave" @before-enter="onBeforeEnter">
|
<TransitionGroup
|
||||||
<CardPreview :card_id="c.cardId" :wrapped="wrapped" v-for="c in cards" :key="c._id"
|
name="list"
|
||||||
:uuid="c._id.toString()">
|
@enter="onEnter"
|
||||||
<slot></slot>
|
@leave="onLeave"
|
||||||
</CardPreview>
|
@before-enter="onBeforeEnter"
|
||||||
</TransitionGroup>
|
>
|
||||||
<!-- <img :class="`card_image`" :src="'/cards/background.png'" draggable="false"> -->
|
<CardPreview
|
||||||
</div>
|
@click="$emit('cardClick', c._id)"
|
||||||
|
v-for="c in cards"
|
||||||
|
:card_id="c.cardId"
|
||||||
|
:wrapped="wrapped"
|
||||||
|
:key="c._id"
|
||||||
|
:uuid="c._id.toString()"
|
||||||
|
:locked="
|
||||||
|
!playingTurn || playingTurn?.cardsLocked.includes(c._id.toString())
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</CardPreview>
|
||||||
|
</TransitionGroup>
|
||||||
|
<!-- <img :class="`card_image`" :src="'/cards/background.png'" draggable="false"> -->
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.list-enter-active,
|
.list-enter-active,
|
||||||
.list-leave-active {
|
.list-leave-active {
|
||||||
transition: all v-bind('animationSpeed') ease;
|
transition: all v-bind("animationSpeed") ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.list-leave-to,
|
.list-leave-to,
|
||||||
.list-enter-from {
|
.list-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(30px);
|
transform: translateX(30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rotate {
|
.rotate {
|
||||||
rotate: 360deg;
|
rotate: 360deg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards_container {
|
.cards_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
transition: rotate v-bind('shuffleAnimationSpeed') ease;
|
transition: rotate v-bind("shuffleAnimationSpeed") ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards_container.wrapped {
|
.cards_container.wrapped {
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards_container.wrapped:before {
|
.cards_container.wrapped:before {
|
||||||
height: calc(250px - 10px);
|
height: calc(250px - 10px);
|
||||||
display: block;
|
display: block;
|
||||||
content: "";
|
content: "";
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
max-height: 10px;
|
max-height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty>.card_image {
|
.empty > .card_image {
|
||||||
filter: brightness(0.5);
|
filter: brightness(0.5);
|
||||||
max-height: 250px;
|
max-height: 250px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const displayOptions = ref(false);
|
||||||
const displayOptions = ref(false)
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="`card_overlay`" ref="card">
|
<div :class="`card_overlay`" ref="card">
|
||||||
<img class="settings_button" src="/images/settings.png" alt="settings" @click="displayOptions = true" />
|
<img
|
||||||
</div>
|
class="settings_button"
|
||||||
<OptionsDialog :isVisible="displayOptions" @close="displayOptions = false" />
|
src="/img/settings.png"
|
||||||
|
alt="settings"
|
||||||
|
@click="displayOptions = true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<OptionsDialog :isVisible="displayOptions" @close="displayOptions = false" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.card_overlay {
|
.card_overlay {
|
||||||
/* display: none; */
|
/* display: none; */
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
display: flex;
|
display: flex;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
/* top: left: */
|
/* top: left: */
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings_button {
|
.settings_button {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+41
-48
@@ -1,16 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Container } from '~/netcode/interfaces';
|
import type { Container } from "~/netcode/interfaces";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
market: Container
|
market: Container;
|
||||||
fire_gems: Container
|
fire_gems: Container;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
// const selfPlayer = computed(() => authStore.logged ? playerList.value?.find(p => p.user.userId == authStore.selfUser.userId) : undefined)
|
// const selfPlayer = computed(() => authStore.logged ? playerList.value?.find(p => p.user.userId == authStore.selfUser.userId) : undefined)
|
||||||
|
|
||||||
@@ -19,58 +17,53 @@ const authStore = useAuthStore()
|
|||||||
|
|
||||||
// const buyableFiregem = computed(() => goStore?.current_game?.gem_stack.at(-1))
|
// const buyableFiregem = computed(() => goStore?.current_game?.gem_stack.at(-1))
|
||||||
|
|
||||||
|
function marketCardClick(e: string) {
|
||||||
function onBeforeEnter(el: Element) {
|
console.log(e);
|
||||||
(el as HTMLElement).style.visibility = "hidden"
|
|
||||||
const element = el as HTMLElement
|
|
||||||
element.style.top = element.offsetTop + "px"
|
|
||||||
element.style.left = element.offsetLeft + "px"
|
|
||||||
element.style.zIndex = "999"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnter(el: any, done: () => unknown) {
|
function fireGemCardClick(e: string) {
|
||||||
console.log("enter")
|
console.log(e);
|
||||||
// setTimeout(() => {
|
|
||||||
// el.style.visibility = null
|
|
||||||
// done()
|
|
||||||
// }, 1000)
|
|
||||||
done()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="market">
|
<div id="market">
|
||||||
<CardPreview :tilted="false" :card_id="undefined"></CardPreview>
|
<CardPreview :tilted="false" :card_id="undefined"></CardPreview>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<CardsGrouped :container="market"></CardsGrouped>
|
<CardsGrouped
|
||||||
<div class="separator"></div>
|
:container="market"
|
||||||
<CardsGrouped :container="fire_gems" :wrapped="true"></CardsGrouped>
|
@cardClick="marketCardClick"
|
||||||
</div>
|
></CardsGrouped>
|
||||||
|
<div class="separator"></div>
|
||||||
|
<CardsGrouped
|
||||||
|
:container="fire_gems"
|
||||||
|
:wrapped="true"
|
||||||
|
@cardClick="fireGemCardClick"
|
||||||
|
></CardsGrouped>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#market {
|
#market {
|
||||||
width: min-content;
|
width: min-content;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: relative;
|
position: relative;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
border-radius: 10px;
|
border-radius: 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));
|
||||||
backdrop-filter: blur(5px) brightness(80%);
|
backdrop-filter: blur(5px) brightness(80%);
|
||||||
background: rgba(155, 62, 62, 0.46);
|
background: rgba(155, 62, 62, 0.46);
|
||||||
}
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
const settingsStore = useSettingsStore()
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _devicePixelRatio = computed(() => devicePixelRatio)
|
const _devicePixelRatio = computed(() => devicePixelRatio);
|
||||||
const _window = computed(() => window)
|
const _window = computed(() => window);
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {});
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(["close"]);
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
emit('close')
|
emit("close");
|
||||||
}
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -30,7 +27,9 @@ const close = () => {
|
|||||||
<span>Toggle Card Style</span>
|
<span>Toggle Card Style</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<button @click="settingsStore.switchCardStyles">Toggle Card style</button>
|
<button @click="settingsStore.switchCardStyles">
|
||||||
|
Toggle Card style
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<span>{{ settingsStore.cardStyle }}</span>
|
<span>{{ settingsStore.cardStyle }}</span>
|
||||||
@@ -39,7 +38,13 @@ const close = () => {
|
|||||||
<span>Animation Speed</span>
|
<span>Animation Speed</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<input type="range" min="50" max="250" step="25" v-model="settingsStore.animationSpeed">
|
<input
|
||||||
|
type="range"
|
||||||
|
min="50"
|
||||||
|
max="250"
|
||||||
|
step="25"
|
||||||
|
v-model="settingsStore.animationSpeed"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<span>{{ settingsStore.animationSpeed }}ms</span>
|
<span>{{ settingsStore.animationSpeed }}ms</span>
|
||||||
@@ -51,19 +56,44 @@ const close = () => {
|
|||||||
<span>Shader Quality</span>
|
<span>Shader Quality</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<input type="range" min="1" max="10" v-model="settingsStore.shaderQuality"
|
<input
|
||||||
:disabled="settingsStore.shaderFramerate == 0">
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="10"
|
||||||
|
v-model="settingsStore.shaderQuality"
|
||||||
|
:disabled="settingsStore.shaderFramerate == 0"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<span>{{ Math.floor(_window.innerWidth * _devicePixelRatio * settingsStore.shaderQuality / 10) }}x{{
|
<span
|
||||||
Math.floor(_window.innerHeight *
|
>{{
|
||||||
_devicePixelRatio * settingsStore.shaderQuality / 10) }}</span>
|
Math.floor(
|
||||||
|
(_window.innerWidth *
|
||||||
|
_devicePixelRatio *
|
||||||
|
settingsStore.shaderQuality) /
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}}x{{
|
||||||
|
Math.floor(
|
||||||
|
(_window.innerHeight *
|
||||||
|
_devicePixelRatio *
|
||||||
|
settingsStore.shaderQuality) /
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<span>Shader Framerate</span>
|
<span>Shader Framerate</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<input type="range" min="0" max="65" step="5" v-model="settingsStore.shaderFramerate">
|
<input
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="65"
|
||||||
|
step="5"
|
||||||
|
v-model="settingsStore.shaderFramerate"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<span v-if="settingsStore.shaderFramerate == 65">vsync</span>
|
<span v-if="settingsStore.shaderFramerate == 65">vsync</span>
|
||||||
@@ -73,7 +103,7 @@ const close = () => {
|
|||||||
<span>Pixelated</span>
|
<span>Pixelated</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<input type="checkbox" v-model="settingsStore.shaderPixelated">
|
<input type="checkbox" v-model="settingsStore.shaderPixelated" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,11 +122,10 @@ const close = () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-box {
|
.dialog-box {
|
||||||
background-image: url('../images/bg-grass.png');
|
background-image: url("../img/bg-grass.png");
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: 800px;
|
width: 800px;
|
||||||
@@ -111,7 +140,7 @@ const close = () => {
|
|||||||
|
|
||||||
.dialog-box h3 {
|
.dialog-box h3 {
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
@@ -139,4 +168,4 @@ const close = () => {
|
|||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
grid-column-start: 3;
|
grid-column-start: 3;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+219
-190
@@ -1,31 +1,44 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { endTurn } from "~/netcode";
|
import { endTurn, lockCard } from "~/netcode";
|
||||||
import type { Game, Player } from '~/netcode/interfaces';
|
import type { Game, Player } from "~/netcode/interfaces";
|
||||||
import { useClientSideStore } from "~/stores/turn";
|
import { useTurnStore } from "~/stores/turnStore";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
player: Player;
|
player: Player;
|
||||||
game: Game,
|
game: Game;
|
||||||
hide_stack_and_discard?: boolean;
|
hide_stack_and_discard?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore();
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
hide_stack_and_discard: () => false,
|
hide_stack_and_discard: () => false,
|
||||||
})
|
});
|
||||||
const playerList = computed(() => props.game.teams.reduce((acc, t) => acc.concat(t.players), [] as Player[]))
|
const playerList = computed(() =>
|
||||||
const selfPlayer = computed(() => authStore.logged ? playerList.value?.find(p => p.user.userId == authStore.selfUser.userId) : undefined)
|
props.game.teams.reduce((acc, t) => acc.concat(t.players), [] as Player[])
|
||||||
|
);
|
||||||
|
const selfPlayer = computed(() =>
|
||||||
|
authStore.logged
|
||||||
|
? playerList.value?.find((p) => p.user.userId == authStore.selfUser.userId)
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
|
||||||
const currentTeam = computed(() => props.game.teams.find(t => t._id == props.game.currentTurn))
|
const currentTeam = computed(() =>
|
||||||
|
props.game.teams.find((t) => t._id == props.game.currentTurn.currentTeam)
|
||||||
|
);
|
||||||
|
|
||||||
const isPlayerTurn = computed(() => currentTeam.value?.players.some(p => p.user.userId == authStore.selfUser.userId));
|
const isPlayerTurn = computed(() =>
|
||||||
|
currentTeam.value?.players.some(
|
||||||
|
(p) => p.user.userId == authStore.selfUser.userId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const isSelf = computed(() => props.player.user.userId === authStore.selfUser.userId);
|
const isSelf = computed(
|
||||||
const isSelfTurn = computed(() => isSelf && isPlayerTurn)
|
() => props.player.user.userId === authStore.selfUser.userId
|
||||||
|
);
|
||||||
|
const isSelfTurn = computed(() => isSelf && isPlayerTurn);
|
||||||
|
|
||||||
|
const warningText = ref("Effets restants!");
|
||||||
const warningText = ref("Effets restants!")
|
const turnStore = useTurnStore();
|
||||||
const clientStore = useClientSideStore()
|
|
||||||
|
|
||||||
// const make_discard = (target: Player) =>
|
// const make_discard = (target: Player) =>
|
||||||
// clientStore.findUseEffect(CardEffects.MAKE_DISCARD, target);
|
// clientStore.findUseEffect(CardEffects.MAKE_DISCARD, target);
|
||||||
@@ -35,8 +48,7 @@ const clientStore = useClientSideStore()
|
|||||||
// const restack_discarded_card = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CARD, target)
|
// const restack_discarded_card = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CARD, target)
|
||||||
// const prepare_champion = (target: CardParser) => clientStore.findUseEffect(CardEffects.PREPARE, target)
|
// const prepare_champion = (target: CardParser) => clientStore.findUseEffect(CardEffects.PREPARE, target)
|
||||||
|
|
||||||
|
let warning = ref(false);
|
||||||
let warning = ref(false)
|
|
||||||
|
|
||||||
// function has_actions_remaining(): boolean {
|
// function has_actions_remaining(): boolean {
|
||||||
// return (goStore.self?.turn?.damage_reserve ? goStore.self?.turn?.damage_reserve > 0 : false)
|
// return (goStore.self?.turn?.damage_reserve ? goStore.self?.turn?.damage_reserve > 0 : false)
|
||||||
@@ -52,27 +64,34 @@ let warning = ref(false)
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
function checkEndTurn() {
|
function checkEndTurn() {
|
||||||
// TODO: Add a confirmation dialog if resources are left
|
// TODO: Add a confirmation dialog if resources are left
|
||||||
// if (
|
// if (
|
||||||
// (!warning.value)
|
// (!warning.value)
|
||||||
// && has_actions_remaining()) {
|
// && has_actions_remaining()) {
|
||||||
// // Set a warning flag to true
|
// // Set a warning flag to true
|
||||||
// // Display an alert somehow using this warning flag
|
// // Display an alert somehow using this warning flag
|
||||||
// warning.value = true
|
// warning.value = true
|
||||||
// }
|
// }
|
||||||
// else if (!warning.value && can_buy_fire_gem()) {
|
// else if (!warning.value && can_buy_fire_gem()) {
|
||||||
// warningText.value = "P'tite Fire gem?"
|
// warningText.value = "P'tite Fire gem?"
|
||||||
// warning.value = true
|
// warning.value = true
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// warningText.value = "Effets restants!"
|
// warningText.value = "Effets restants!"
|
||||||
// warning.value = false
|
// warning.value = false
|
||||||
// endTurn()
|
// endTurn()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
endTurn(props.game._id)
|
endTurn(props.game._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function boardCardClick(cardUUID: string) {
|
||||||
|
if (props.game.currentTurn.cardsLocked.includes(cardUUID)) {
|
||||||
|
//Card already locked
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lockCard(props.game._id, cardUUID);
|
||||||
|
}
|
||||||
// function damage_team_all(team: Team) {
|
// function damage_team_all(team: Team) {
|
||||||
// for (let i = 0; i < (goStore.self?.turn?.damage_reserve ?? 0); i++) {
|
// for (let i = 0; i < (goStore.self?.turn?.damage_reserve ?? 0); i++) {
|
||||||
// damage_team(team)
|
// damage_team(team)
|
||||||
@@ -82,25 +101,34 @@ function checkEndTurn() {
|
|||||||
// function check_for_guard(player: Player) {
|
// function check_for_guard(player: Player) {
|
||||||
// return player.board.some(c => c.guard === true)
|
// return player.board.some(c => c.guard === true)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="player" :class="{ turn: isPlayerTurn, self: isSelf }">
|
<div class="player" :class="{ turn: isPlayerTurn, self: isSelf }">
|
||||||
<div id="player_banner" class="big">test</div>
|
<div id="player_banner" class="big">test</div>
|
||||||
<div id="turn_buttons">
|
<div id="turn_buttons">
|
||||||
<button id="end_turn" class="end_turn_button turn_icon" :class="{ hidden: !isSelfTurn }"
|
<button
|
||||||
@click="checkEndTurn">END TURN</button>
|
id="end_turn"
|
||||||
|
class="end_turn_button turn_icon"
|
||||||
|
:class="{ hidden: !isSelfTurn }"
|
||||||
|
@click="checkEndTurn"
|
||||||
|
>
|
||||||
|
END TURN
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="warning"
|
<div
|
||||||
:class="{ 'warning-fire-gem': warningText == 'P\'tite Fire gem?', 'visible': warning }">
|
class="warning"
|
||||||
/!\<br>{{ warningText }}
|
:class="{
|
||||||
</div>
|
'warning-fire-gem': warningText == 'P\'tite Fire gem?',
|
||||||
</div>
|
visible: warning,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
/!\<br />{{ warningText }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="current_player_board">
|
||||||
<div id="current_player_board">
|
<!-- <div class="other_player_buttons_container">
|
||||||
<!-- <div class="other_player_buttons_container">
|
|
||||||
<button @click.stop="damage_team(props.player.team)"
|
<button @click.stop="damage_team(props.player.team)"
|
||||||
v-if="isSelfTurn && isPlayerEnemy && goStore.self?.turn && props.player.team" class="damage_button"
|
v-if="isSelfTurn && isPlayerEnemy && goStore.self?.turn && props.player.team" class="damage_button"
|
||||||
:disabled="(goStore.self?.turn.damage_reserve ?? 0) <= 0 || check_for_guard(props.player)">
|
:disabled="(goStore.self?.turn.damage_reserve ?? 0) <= 0 || check_for_guard(props.player)">
|
||||||
@@ -118,14 +146,19 @@ function checkEndTurn() {
|
|||||||
make Discard
|
make Discard
|
||||||
</button>
|
</button>
|
||||||
</div> -->
|
</div> -->
|
||||||
<!-- <br /> -->
|
<!-- <br /> -->
|
||||||
|
|
||||||
<CardsGrouped :container="player.board" :wrapped="false">
|
<CardsGrouped
|
||||||
</CardsGrouped>
|
:container="player.board"
|
||||||
|
:wrapped="false"
|
||||||
|
:playingTurn="props.game.currentTurn"
|
||||||
|
@cardClick="boardCardClick"
|
||||||
|
>
|
||||||
|
</CardsGrouped>
|
||||||
|
|
||||||
<!-- <div class="cards_container"> -->
|
<!-- <div class="cards_container"> -->
|
||||||
<!-- HAND -->
|
<!-- HAND -->
|
||||||
<!-- <template v-if="isSelf">
|
<!-- <template v-if="isSelf">
|
||||||
<CardPreview :card_id="c?.card_id" v-for="c in props.player.hand" @click="c && playCard(c)">
|
<CardPreview :card_id="c?.card_id" v-for="c in props.player.hand" @click="c && playCard(c)">
|
||||||
<button
|
<button
|
||||||
v-if="props.player.turn && c && isSelf && (props.player.turn.discard_markers + props.player.turn.fuck_u_markers) > 0"
|
v-if="props.player.turn && c && isSelf && (props.player.turn.discard_markers + props.player.turn.fuck_u_markers) > 0"
|
||||||
@@ -135,12 +168,12 @@ function checkEndTurn() {
|
|||||||
</CardPreview>
|
</CardPreview>
|
||||||
</template> -->
|
</template> -->
|
||||||
|
|
||||||
<!-- BOARD -->
|
<!-- BOARD -->
|
||||||
<!-- <CardPreview :card_id="c.cardId" v-for="c in props.player.board.cards"
|
<!-- <CardPreview :card_id="c.cardId" v-for="c in props.player.board.cards"
|
||||||
@click="console.log/*lockCard(c)*/"
|
@click="console.log/*lockCard(c)*/"
|
||||||
:locked="/*props.player.turn && c.uuid in props.player.turn.cards_locked*/ false"> -->
|
:locked="/*props.player.turn && c.uuid in props.player.turn.cards_locked*/ false"> -->
|
||||||
|
|
||||||
<!-- <template v-if="c && isSelf && isPlayerTurn && props.player.turn">
|
<!-- <template v-if="c && isSelf && isPlayerTurn && props.player.turn">
|
||||||
// Card Locked
|
// Card Locked
|
||||||
<template v-if="c.uuid in props.player.turn.cards_locked">
|
<template v-if="c.uuid in props.player.turn.cards_locked">
|
||||||
<template v-for="e in clientStore.maskedServerEffects">
|
<template v-for="e in clientStore.maskedServerEffects">
|
||||||
@@ -193,237 +226,233 @@ function checkEndTurn() {
|
|||||||
STUN
|
STUN
|
||||||
</button>
|
</button>
|
||||||
</template> -->
|
</template> -->
|
||||||
<!-- </CardPreview> -->
|
<!-- </CardPreview> -->
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.warning {
|
.warning {
|
||||||
background-color: rgba(255, 0, 0, 0.7);
|
background-color: rgba(255, 0, 0, 0.7);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
visibility: hidden
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning.visible {
|
.warning.visible {
|
||||||
visibility: visible
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-fire-gem {
|
.warning-fire-gem {
|
||||||
background-color: rgba(255, 165, 0, 0.7);
|
background-color: rgba(255, 165, 0, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#turn_buttons {
|
#turn_buttons {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
#end_turn {
|
#end_turn {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#end_turn.hidden {
|
#end_turn.hidden {
|
||||||
visibility: hidden
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stack,
|
.stack,
|
||||||
.discard,
|
.discard,
|
||||||
.cards_container {
|
.cards_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap-reverse;
|
flex-wrap: wrap-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stack {
|
.stack {
|
||||||
grid-area: stack;
|
grid-area: stack;
|
||||||
margin-left: 75px;
|
margin-left: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discard {
|
.discard {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
grid-area: discard;
|
grid-area: discard;
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
margin-left: 75px;
|
margin-left: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discard.invisible {
|
.discard.invisible {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player {
|
.player {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-template-rows: min-content 1fr;
|
grid-template-rows: min-content 1fr;
|
||||||
/* border-top: 5px solid black; */
|
/* border-top: 5px solid black; */
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"player_stats"
|
"player_stats"
|
||||||
"player";
|
"player";
|
||||||
transition: all 1s;
|
transition: all 1s;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#current_player_board {
|
#current_player_board {
|
||||||
grid-area: player;
|
grid-area: player;
|
||||||
}
|
}
|
||||||
|
|
||||||
.damage_button {
|
.damage_button {
|
||||||
background-color: #ff4d4d;
|
background-color: #ff4d4d;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 5px 25px;
|
padding: 5px 25px;
|
||||||
margin: 5px 0 0 0;
|
margin: 5px 0 0 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.damage_button:disabled {
|
.damage_button:disabled {
|
||||||
background-color: rgba(255, 77, 77, 0.5);
|
background-color: rgba(255, 77, 77, 0.5);
|
||||||
color: rgba(255, 255, 255, 0.5);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.damage_button:hover {
|
.damage_button:hover {
|
||||||
background-color: #e60000;
|
background-color: #e60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.damage_button:disabled:hover {
|
.damage_button:disabled:hover {
|
||||||
background-color: rgba(230, 0, 0, 0.5);
|
background-color: rgba(230, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.damage_button:active {
|
.damage_button:active {
|
||||||
transform: translateY(2px);
|
transform: translateY(2px);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.all_damage_button {
|
.all_damage_button {
|
||||||
padding: 5px 5px;
|
padding: 5px 5px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.make_discard_button {
|
.make_discard_button {
|
||||||
background-color: #55b42f;
|
background-color: #55b42f;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
margin: 5px 0 0 0;
|
margin: 5px 0 0 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.make_discard_button:disabled {
|
.make_discard_button:disabled {
|
||||||
background-color: rgba(85, 180, 47, 0.5);
|
background-color: rgba(85, 180, 47, 0.5);
|
||||||
color: rgba(255, 255, 255, 0.5);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.make_discard_button:hover {
|
.make_discard_button:hover {
|
||||||
background-color: #03883a;
|
background-color: #03883a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.make_discard_button:disabled:hover {
|
.make_discard_button:disabled:hover {
|
||||||
background-color: rgba(3, 136, 58, 0.5);
|
background-color: rgba(3, 136, 58, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.make_discard_button:active {
|
.make_discard_button:active {
|
||||||
transform: translateY(2px);
|
transform: translateY(2px);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.turn_icon {
|
.turn_icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#player_info {
|
#player_info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#player_banner.big {
|
#player_banner.big {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
max-width: min-content;
|
max-width: min-content;
|
||||||
border: 5px solid black;
|
border: 5px solid black;
|
||||||
border-radius: 10px;
|
border-radius: 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));
|
||||||
backdrop-filter: blur(5px) brightness(80%) grayscale(80%);
|
backdrop-filter: blur(5px) brightness(80%) grayscale(80%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#player_banner img {
|
#player_banner img {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.turn_icon img {
|
.turn_icon img {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.turn_icon_number {
|
.turn_icon_number {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.end_turn_button {
|
.end_turn_button {
|
||||||
background-color: #4CAF50;
|
background-color: #4caf50;
|
||||||
/* Green background color */
|
/* Green background color */
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 13px 10px;
|
padding: 13px 10px;
|
||||||
margin: 0 0 0 5px;
|
margin: 0 0 0 5px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.end_turn_button:hover {
|
.end_turn_button:hover {
|
||||||
background-color: #45a049;
|
background-color: #45a049;
|
||||||
/* Darker green on hover */
|
/* Darker green on hover */
|
||||||
}
|
}
|
||||||
|
|
||||||
.end_turn_button:active {
|
.end_turn_button:active {
|
||||||
transform: translateY(2px);
|
transform: translateY(2px);
|
||||||
/* Add a slight press effect on click */
|
/* Add a slight press effect on click */
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
/* Remove shadow on click */
|
/* Remove shadow on click */
|
||||||
}
|
}
|
||||||
|
|
||||||
.suicide_effect {
|
.suicide_effect {
|
||||||
background: linear-gradient(to bottom right, #666666, #ccc);
|
background: linear-gradient(to bottom right, #666666, #ccc);
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+27
-33
@@ -1,65 +1,59 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Player } from '~/netcode/Player';
|
import type { Player } from "~/netcode/interfaces";
|
||||||
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
player: Player
|
player: Player;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="turn_icon">
|
<div class="turn_icon">
|
||||||
<img src="/images/champion-shield.png" class="icon_img" draggable="false" />
|
<img src="/img/champion-shield.png" class="icon_img" draggable="false" />
|
||||||
<span class="turn_icon_number">
|
<span class="turn_icon_number">
|
||||||
{{ player.team?.health }}
|
{{ player.team?.health }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="turn_icon" v-if="showTurnData">
|
<!-- <div class="turn_icon" v-if="showTurnData">
|
||||||
<img src="/images/gold.png" class="icon_img" draggable="false" />
|
<img src="/umg/gold.png" class="icon_img" draggable="false" />
|
||||||
<span class="turn_icon_number">
|
<span class="turn_icon_number">
|
||||||
{{ player.turn?.gold_reserve }}
|
{{ player.turn?.gold_reserve }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="turn_icon" v-if="showTurnData">
|
<div class="turn_icon" v-if="showTurnData">
|
||||||
<img src="/images/damage.png" class="icon_img" draggable="false" />
|
<img src="/umg/damage.png" class="icon_img" draggable="false" />
|
||||||
<span class="turn_icon_number">
|
<span class="turn_icon_number">
|
||||||
{{ player.turn?.damage_reserve }}
|
{{ player.turn?.damage_reserve }}
|
||||||
</span>
|
</span>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="turn_icon">
|
<div class="turn_icon">
|
||||||
<img src="/images/fuck.svg" class="icon_img" draggable="false" />
|
<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)
|
<span class="turn_icon_number">{{
|
||||||
}}</span>
|
(player.turn?.discard_markers ?? 0) + (player.turn?.fuck_u_markers ?? 0)
|
||||||
</div>
|
}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.turn_icon {
|
.turn_icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.icon_img {
|
img.icon_img {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.icon_img {
|
span.icon_img {
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.turn_icon_number {
|
.turn_icon_number {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-shadow: white 0 0 1px;
|
text-shadow: white 0 0 1px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+24
-26
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Player } from '~/netcode/Player';
|
import type { Player } from "~/netcode/Player";
|
||||||
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
isVisible: Boolean;
|
isVisible: Boolean;
|
||||||
@@ -10,15 +9,14 @@ export interface Props {
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
won: () => false,
|
won: () => false,
|
||||||
players: () => []
|
players: () => [],
|
||||||
})
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(["close"]);
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
reloadNuxtApp()
|
reloadNuxtApp();
|
||||||
}
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -30,24 +28,24 @@ const close = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<PlayerIcon :player="players[0]"></PlayerIcon>
|
<PlayerIcon :player="players[0]"></PlayerIcon>
|
||||||
<p v-if="won">
|
<p v-if="won">Vous avez gagné!</p>
|
||||||
Vous avez gagné!
|
|
||||||
</p>
|
|
||||||
<p v-else>
|
<p v-else>
|
||||||
Vous avez perdu face à ce magnifique individu qui a très clairement beaucoup plus de bitches que vous! Cela
|
Vous avez perdu face à ce magnifique individu qui a très clairement
|
||||||
dit, je pense qu'il n'y a pas de bonnes
|
beaucoup plus de bitches que vous! Cela dit, je pense qu'il n'y a pas
|
||||||
ou de mauvaises situations, juste des situations différentes. Vous avez perdu, mais vous avez gagné en
|
de bonnes ou de mauvaises situations, juste des situations
|
||||||
expérience! C'est ça le plus important
|
différentes. Vous avez perdu, mais vous avez gagné en expérience!
|
||||||
dans la vie, n'est-ce pas? Apprendre de ses erreurs et devenir meilleur! Alors, ne soyez pas triste, soyez
|
C'est ça le plus important dans la vie, n'est-ce pas? Apprendre de ses
|
||||||
heureux d'avoir eu l'opportunité
|
erreurs et devenir meilleur! Alors, ne soyez pas triste, soyez heureux
|
||||||
d'apprendre quelque chose aujourd'hui! Et puis, vous savez ce qu'on dit, "ce qui ne nous tue pas nous rend
|
d'avoir eu l'opportunité d'apprendre quelque chose aujourd'hui! Et
|
||||||
plus fort"! Alors, allez-y, soyez fort!
|
puis, vous savez ce qu'on dit, "ce qui ne nous tue pas nous rend plus
|
||||||
Et n'oubliez pas, la prochaine fois, vous gagnerez! Vous verrez! Vous êtes un gagnant, vous êtes un champion!
|
fort"! Alors, allez-y, soyez fort! Et n'oubliez pas, la prochaine
|
||||||
Vous êtes le meilleur! Vous êtes
|
fois, vous gagnerez! Vous verrez! Vous êtes un gagnant, vous êtes un
|
||||||
le roi du monde!
|
champion! Vous êtes le meilleur! Vous êtes le roi du monde!
|
||||||
<img src="/images/so_back.png" alt="Une image décrivant le meme 'We are so back'" />
|
<img
|
||||||
|
src="/img/so_back.png"
|
||||||
|
alt="Une image décrivant le meme 'We are so back'"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +66,7 @@ const close = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dialog-box {
|
.dialog-box {
|
||||||
background-image: url('/images/bg-grass.png');
|
background-image: url("/img/bg-grass.png");
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: 800px;
|
width: 800px;
|
||||||
@@ -84,7 +82,7 @@ const close = () => {
|
|||||||
|
|
||||||
.dialog-box h3 {
|
.dialog-box h3 {
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
|
|||||||
@@ -64,3 +64,14 @@ export async function endTurn(game: string) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function lockCard(game: string, cardId: string) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
await $fetch<User>(
|
||||||
|
new URL(`games/${game}/turn/lockCard/${cardId}`, config.public.endpoint).href,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Submodule netcode/interfaces deleted from 9248bf70af
@@ -2,12 +2,20 @@ export interface GameObject {
|
|||||||
_id: string;
|
_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlayingTurn {
|
||||||
|
currentTeam: string;
|
||||||
|
cardsLocked: string[];
|
||||||
|
effectsUsed: string[];
|
||||||
|
damage: number;
|
||||||
|
gold: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Game extends GameObject {
|
export interface Game extends GameObject {
|
||||||
fireGems: Container;
|
fireGems: Container;
|
||||||
market: Container;
|
market: Container;
|
||||||
marketStack: Container;
|
marketStack: Container;
|
||||||
teams: Team[];
|
teams: Team[];
|
||||||
currentTurn: string;
|
currentTurn: PlayingTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Team extends GameObject {
|
export interface Team extends GameObject {
|
||||||
|
|||||||
+250
-192
@@ -1,113 +1,156 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LoadingScreen from '~/components/LoadingScreen.vue';
|
import LoadingScreen from "~/components/LoadingScreen.vue";
|
||||||
import { subscribe } from '~/netcode';
|
import { subscribe } from "~/netcode";
|
||||||
import { containerEvents, delay } from '~/netcode/events';
|
import { containerEvents, delay } from "~/netcode/events";
|
||||||
import { type Container, type Game, type Player } from '~/netcode/interfaces';
|
import { type Container, type Game, type Player } from "~/netcode/interfaces";
|
||||||
|
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig();
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute();
|
||||||
const gameId = route.params.id
|
const gameId = route.params.id;
|
||||||
|
|
||||||
const { data: game, pending, error, refresh } = await useFetch<Game>(
|
const {
|
||||||
new URL(`/games/${gameId}`, config.public.endpoint).href,
|
data: game,
|
||||||
{
|
pending,
|
||||||
credentials: "include",
|
error,
|
||||||
},
|
refresh,
|
||||||
)
|
} = await useFetch<Game>(
|
||||||
|
new URL(`/games/${gameId}`, config.public.endpoint).href,
|
||||||
|
{
|
||||||
|
credentials: "include",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (game.value == null) {
|
if (game.value == null) {
|
||||||
navigateTo("/lobby");
|
navigateTo("/lobby");
|
||||||
throw new Error("game not found")
|
throw new Error("game not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const containers = computed<Record<string, Container>>(() => ({
|
const containers = computed<Record<string, Container>>(() => ({
|
||||||
[game.value!.fireGems._id]: game.value!.fireGems,
|
[game.value!.fireGems._id]: game.value!.fireGems,
|
||||||
[game.value!.market._id]: game.value!.market,
|
[game.value!.market._id]: game.value!.market,
|
||||||
[game.value!.marketStack._id]: game.value!.marketStack,
|
[game.value!.marketStack._id]: game.value!.marketStack,
|
||||||
...Object.fromEntries(
|
...Object.fromEntries(
|
||||||
game.value!.teams.reduce(
|
game
|
||||||
(acc, t) => acc.concat(t.players), [] as Player[]
|
.value!.teams.reduce((acc, t) => acc.concat(t.players), [] as Player[])
|
||||||
).map(
|
.map((p) => [
|
||||||
p => [
|
[p.board._id, p.board],
|
||||||
[p.board._id, p.board],
|
[p.discard._id, p.discard],
|
||||||
[p.discard._id, p.discard],
|
[p.hand._id, p.hand],
|
||||||
[p.hand._id, p.hand],
|
[p.stack._id, p.stack],
|
||||||
[p.stack._id, p.stack]
|
])
|
||||||
]
|
.flat(1)
|
||||||
).flat(1)
|
),
|
||||||
)
|
}));
|
||||||
}))
|
|
||||||
|
|
||||||
const eventSource = subscribe(`/games/${gameId}/subscribe`)
|
const eventSource = subscribe(`/games/${gameId}/subscribe`);
|
||||||
|
|
||||||
const actions = ref<(() => Promise<any>)[]>([])
|
const actions = ref<(() => Promise<any>)[]>([]);
|
||||||
|
|
||||||
function queueAction(callback: (() => Promise<any>)) {
|
function queueAction(callback: () => Promise<any>) {
|
||||||
actions.value.push(callback)
|
actions.value.push(callback);
|
||||||
if (actions.value.length == 1) {
|
if (actions.value.length == 1) {
|
||||||
execAction(callback)
|
execAction(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function execAction(callback: (() => Promise<any>)) {
|
async function execAction(callback: () => Promise<any>) {
|
||||||
await callback()
|
await callback();
|
||||||
actions.value.shift()
|
actions.value.shift();
|
||||||
if (actions.value.length > 0) {
|
if (actions.value.length > 0) {
|
||||||
execAction(actions.value[0])
|
execAction(actions.value[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventSource.addEventListener("message", async (message) => {
|
eventSource.addEventListener("message", async (message) => {
|
||||||
const data = JSON.parse(message.data)
|
const current_game = game.value;
|
||||||
console.log(data)
|
if (!current_game) {
|
||||||
if (data.type == 'start') {
|
throw new Error("game not found");
|
||||||
throw new Error("not implemented")
|
}
|
||||||
} else if (data.type == "populateContainer") {
|
const data = JSON.parse(message.data);
|
||||||
throw new Error("not implemented")
|
console.log(data);
|
||||||
} else if (data.type == "shuffleContainer") {
|
if (data.type == "start") {
|
||||||
queueAction(async () => {
|
throw new Error("not implemented");
|
||||||
await containerEvents.get(data.container)?.shuffleContainer()
|
} else if (data.type == "populateContainer") {
|
||||||
})
|
queueAction(async () => {
|
||||||
} else if (data.type == "distributeCards") {
|
await containerEvents.get(data.container)?.startAnimation();
|
||||||
// containerEvents.get(data.from)?.getCardsPosition(data.cards)
|
});
|
||||||
|
const fromContainer = containers.value[data.container.id as string];
|
||||||
|
queueAction(async () => {
|
||||||
|
for (const card of data.container.cards) {
|
||||||
|
fromContainer.cards.push(card);
|
||||||
|
await delay(settingsStore.animationSpeed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
queueAction(async () => {
|
||||||
|
await containerEvents.get(data.container)?.endAnimation();
|
||||||
|
});
|
||||||
|
} else if (data.type == "shuffleContainer") {
|
||||||
|
queueAction(async () => {
|
||||||
|
await containerEvents.get(data.container)?.shuffleContainer();
|
||||||
|
});
|
||||||
|
} else if (data.type == "distributeCards") {
|
||||||
|
// containerEvents.get(data.from)?.getCardsPosition(data.cards)
|
||||||
|
|
||||||
queueAction(async () => {
|
queueAction(async () => {
|
||||||
containerEvents.get(data.from)?.startAnimation()
|
containerEvents.get(data.from)?.startAnimation();
|
||||||
containerEvents.get(data.to)?.startAnimation()
|
containerEvents.get(data.to)?.startAnimation();
|
||||||
})
|
});
|
||||||
|
|
||||||
const fromContainer = containers.value[data.from as string]
|
const fromContainer = containers.value[data.from as string];
|
||||||
const toContainer = containers.value[data.to as string]
|
const toContainer = containers.value[data.to as string];
|
||||||
for (const card of data.cards) {
|
for (const card of data.cards) {
|
||||||
queueAction(async () => {
|
queueAction(async () => {
|
||||||
const index = fromContainer.cards.findIndex(c => c._id == card._id)
|
const index = fromContainer.cards.findIndex((c) => c._id == card._id);
|
||||||
fromContainer.cards.splice(index, 1)
|
fromContainer.cards.splice(index, 1);
|
||||||
await delay(settingsStore.animationSpeed)
|
await delay(settingsStore.animationSpeed);
|
||||||
})
|
});
|
||||||
|
|
||||||
queueAction(async () => {
|
queueAction(async () => {
|
||||||
toContainer.cards.push(card)
|
toContainer.cards.push(card);
|
||||||
await delay(settingsStore.animationSpeed)
|
await delay(settingsStore.animationSpeed);
|
||||||
})
|
});
|
||||||
}
|
|
||||||
queueAction(async () => {
|
|
||||||
containerEvents.get(data.from)?.endAnimation()
|
|
||||||
containerEvents.get(data.to)?.endAnimation()
|
|
||||||
await delay(settingsStore.animationSpeed * 2)
|
|
||||||
})
|
|
||||||
|
|
||||||
} else if (data.type == "endTurn") {
|
|
||||||
throw new Error("not implemented")
|
|
||||||
}
|
}
|
||||||
|
queueAction(async () => {
|
||||||
|
containerEvents.get(data.from)?.endAnimation();
|
||||||
|
containerEvents.get(data.to)?.endAnimation();
|
||||||
|
await delay(settingsStore.animationSpeed * 2);
|
||||||
|
});
|
||||||
|
} 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;
|
||||||
|
await delay(settingsStore.animationSpeed);
|
||||||
|
});
|
||||||
|
} else if (data.type == "lockCard") {
|
||||||
|
queueAction(async () => {
|
||||||
|
current_game.currentTurn.cardsLocked.push(data.card);
|
||||||
|
});
|
||||||
|
} else if (data.type == "useEffect") {
|
||||||
|
queueAction(async () => {
|
||||||
|
current_game.currentTurn.effectsUsed.push(data.effect);
|
||||||
|
});
|
||||||
|
} else if (data.type == "currentTurn.updateGold") {
|
||||||
|
queueAction(async () => {
|
||||||
|
console.log(`adding gold amount : ` + data.operation);
|
||||||
|
current_game.currentTurn.gold = data.gold;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const playerList = computed(() =>
|
||||||
const playerList = computed(() => game.value!.teams.reduce((acc, t) => acc.concat(t.players), [] as Player[]))
|
game.value!.teams.reduce((acc, t) => acc.concat(t.players), [] as Player[])
|
||||||
const selfPlayer = computed(() => authStore.logged ? playerList.value?.find(p => p.user.userId == authStore.selfUser.userId) : undefined)
|
);
|
||||||
|
const selfPlayer = computed(() =>
|
||||||
|
authStore.logged
|
||||||
|
? playerList.value?.find((p) => p.user.userId == authStore.selfUser.userId)
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
|
||||||
// TODO : current turn sound
|
// TODO : current turn sound
|
||||||
|
|
||||||
@@ -115,24 +158,24 @@ const selfPlayer = computed(() => authStore.logged ? playerList.value?.find(p =>
|
|||||||
|
|
||||||
// TODO : join sound
|
// TODO : join sound
|
||||||
|
|
||||||
const showResults = ref(false)
|
const showResults = ref(false);
|
||||||
const isWinner = ref(false)
|
const isWinner = ref(false);
|
||||||
const loaded = ref(false)
|
const loaded = ref(false);
|
||||||
|
|
||||||
function showLoserScreen() {
|
function showLoserScreen() {
|
||||||
// playSound('/sounds/ding.mp3')
|
// playSound('/sounds/ding.mp3')
|
||||||
showResults.value = true
|
showResults.value = true;
|
||||||
isWinner.value = false
|
isWinner.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWinnerScreen() {
|
function showWinnerScreen() {
|
||||||
// playSound('/sounds/ding.mp3')
|
// playSound('/sounds/ding.mp3')
|
||||||
showResults.value = true
|
showResults.value = true;
|
||||||
isWinner.value = true
|
isWinner.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const discard_unwrapped = ref(false)
|
const discard_unwrapped = ref(false);
|
||||||
const focusedPlayer = ref<Player>(selfPlayer.value ?? playerList.value[0])
|
const focusedPlayer = ref<Player>(selfPlayer.value ?? playerList.value[0]);
|
||||||
|
|
||||||
// const clientStore = useClientSideStore()
|
// const clientStore = useClientSideStore()
|
||||||
// const restack_discarded_champion = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CHAMPION, target)
|
// const restack_discarded_champion = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CHAMPION, target)
|
||||||
@@ -140,7 +183,7 @@ const focusedPlayer = ref<Player>(selfPlayer.value ?? playerList.value[0])
|
|||||||
// const sacrifice = (target: Card) => clientStore.findUseEffect(CardEffects.SACRIFICE, target);
|
// const sacrifice = (target: Card) => clientStore.findUseEffect(CardEffects.SACRIFICE, target);
|
||||||
|
|
||||||
function focusPlayer(p: Player) {
|
function focusPlayer(p: Player) {
|
||||||
focusedPlayer.value = p
|
focusedPlayer.value = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// watch<Team>(() => goStore.current_game!.current_turn, (new_team, old_team) => {
|
// watch<Team>(() => goStore.current_game!.current_turn, (new_team, old_team) => {
|
||||||
@@ -153,159 +196,174 @@ function focusPlayer(p: Player) {
|
|||||||
// })
|
// })
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
eventSource.close()
|
eventSource.close();
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div @contextmenu.prevent="false" id="game_board" v-if="game">
|
<div @contextmenu.prevent="false" id="game_board" v-if="game">
|
||||||
<AnimatedBackground @ready="loaded = true"></AnimatedBackground>
|
<AnimatedBackground @ready="loaded = true"></AnimatedBackground>
|
||||||
<div id="stack_discard">
|
<div id="stack_discard">
|
||||||
<div id="stack">
|
<div id="stack">
|
||||||
<CardsGrouped :container="focusedPlayer.stack" :wrapped="true" :hidden="true">
|
<CardsGrouped
|
||||||
</CardsGrouped>
|
:container="focusedPlayer.stack"
|
||||||
</div>
|
:wrapped="true"
|
||||||
|
:hidden="true"
|
||||||
|
>
|
||||||
|
</CardsGrouped>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="discard" :class="{ invisible: discard_unwrapped }" @click="() => discard_unwrapped = true">
|
<div
|
||||||
<CardsGrouped :container="focusedPlayer.discard" :wrapped="true">
|
id="discard"
|
||||||
</CardsGrouped>
|
:class="{ invisible: discard_unwrapped }"
|
||||||
</div>
|
@click="() => (discard_unwrapped = true)"
|
||||||
</div>
|
>
|
||||||
|
<CardsGrouped :container="focusedPlayer.discard" :wrapped="true">
|
||||||
|
</CardsGrouped>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<MarketCards :market="game.market" :fire_gems="game.fireGems"></MarketCards>
|
<MarketCards :market="game.market" :fire_gems="game.fireGems"></MarketCards>
|
||||||
|
|
||||||
<!-- <div id="player_list_container">
|
<!-- <div id="player_list_container">
|
||||||
<div id="player_list">
|
<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" @click="focusPlayer(p)" :focused="focusedPlayer">
|
||||||
</PlayerListElement>
|
</PlayerListElement>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div id="current_player">
|
<div id="current_player">
|
||||||
<PlayerSlot v-if="focusedPlayer" :player="focusedPlayer" :game="game!"></PlayerSlot>
|
<PlayerSlot
|
||||||
</div>
|
v-if="focusedPlayer"
|
||||||
<ResultDialog :isVisible="showResults" :won="isWinner" :players="playerList" @close="showResults = false">
|
:player="focusedPlayer"
|
||||||
</ResultDialog>
|
:game="game!"
|
||||||
<GlobalOverlay></GlobalOverlay>
|
></PlayerSlot>
|
||||||
<SelfView v-if="selfPlayer" :container="selfPlayer.hand"></SelfView>
|
|
||||||
<LoadingScreen v-if="!loaded"></LoadingScreen>
|
|
||||||
</div>
|
</div>
|
||||||
|
<ResultDialog
|
||||||
|
:isVisible="showResults"
|
||||||
|
:won="isWinner"
|
||||||
|
:players="playerList"
|
||||||
|
@close="showResults = false"
|
||||||
|
>
|
||||||
|
</ResultDialog>
|
||||||
|
<GlobalOverlay></GlobalOverlay>
|
||||||
|
<SelfView v-if="selfPlayer" :container="selfPlayer.hand"></SelfView>
|
||||||
|
<LoadingScreen v-if="!loaded"></LoadingScreen>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#game_status {
|
#game_status {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
background-color: rgba(255, 255, 255, .75);
|
background-color: rgba(255, 255, 255, 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#game_board {
|
#game_board {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr min-content 1fr;
|
grid-template-columns: 1fr min-content 1fr;
|
||||||
grid-template-rows: min-content min-content 1fr;
|
grid-template-rows: min-content min-content 1fr;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"stack_discard market empty"
|
"stack_discard market empty"
|
||||||
"player_list player_list player_list"
|
"player_list player_list player_list"
|
||||||
"current_player current_player current_player";
|
"current_player current_player current_player";
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#player_list,
|
#player_list,
|
||||||
#current_player {
|
#current_player {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* background: url('/images/bg-darkgrass.png'); */
|
/* background: url('/img/bg-darkgrass.png'); */
|
||||||
}
|
}
|
||||||
|
|
||||||
#current_player {
|
#current_player {
|
||||||
grid-area: current_player;
|
grid-area: current_player;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stack_discard {
|
#stack_discard {
|
||||||
grid-area: stack_discard;
|
grid-area: stack_discard;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stack,
|
#stack,
|
||||||
#discard {
|
#discard {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
#discard {
|
#discard {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discard_overlay {
|
.discard_overlay {
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
display: flex;
|
display: flex;
|
||||||
/* background: blue; */
|
/* background: blue; */
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
flex-wrap: wrap-reverse;
|
flex-wrap: wrap-reverse;
|
||||||
align-content: flex-end;
|
align-content: flex-end;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding-bottom: inherit;
|
padding-bottom: inherit;
|
||||||
padding-left: 40px;
|
padding-left: 40px;
|
||||||
height: calc(100% - 64px);
|
height: calc(100% - 64px);
|
||||||
padding-top: 64px;
|
padding-top: 64px;
|
||||||
width: calc(100% -40px);
|
width: calc(100% -40px);
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#discard_unwrapped_bg {
|
#discard_unwrapped_bg {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
padding: inherit;
|
padding: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#discard_unwrapped_bg.unwrapped {
|
#discard_unwrapped_bg.unwrapped {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#market {
|
#market {
|
||||||
grid-area: market;
|
grid-area: market;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#player_list_container {
|
#player_list_container {
|
||||||
grid-area: player_list;
|
grid-area: player_list;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
/* background-image: url("/images/bg-darkgrass.png"); */
|
/* background-image: url("/img/bg-darkgrass.png"); */
|
||||||
}
|
}
|
||||||
|
|
||||||
#player_list {
|
#player_list {
|
||||||
/* border: 5px solid #000; */
|
/* border: 5px solid #000; */
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#player_list .card {
|
#player_list .card {
|
||||||
--max-height: 90px;
|
--max-height: 90px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+217
-202
@@ -1,273 +1,288 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import { playSound } from '~/helpers/audioHelpers';
|
// import { playSound } from '~/helpers/audioHelpers';
|
||||||
import { playSound } from "~/helpers/audioHelpers";
|
import { playSound } from "~/helpers/audioHelpers";
|
||||||
import { createGame, getLobby, subscribe } from "~/netcode"
|
import { createGame, getLobby, subscribe } from "~/netcode";
|
||||||
import type { Player } from "~/netcode/interfaces";
|
import type { Player } from "~/netcode/interfaces";
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const games = await getLobby()
|
const games = await getLobby();
|
||||||
|
|
||||||
const eventSource = subscribe("/games/subscribe")
|
const eventSource = subscribe("/games/subscribe");
|
||||||
|
|
||||||
eventSource.addEventListener("message", (message) => {
|
eventSource.addEventListener("message", (message) => {
|
||||||
const data = JSON.parse(message.data)
|
const data = JSON.parse(message.data);
|
||||||
if (data.type == "create") {
|
if (data.type == "create") {
|
||||||
games.value.push(data)
|
games.value.push(data);
|
||||||
} else if (data.type == "delete") {
|
} else if (data.type == "delete") {
|
||||||
const index = games.value.findIndex(g => g._id == data._id)
|
const index = games.value.findIndex((g) => g._id == data._id);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
throw new Error("Couldn't delete game")
|
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 == "start") {
|
|
||||||
throw new Error("not implemented")
|
|
||||||
}
|
}
|
||||||
|
games.value.splice(index, 1);
|
||||||
|
} else if (data.type == "update") {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
} else if (data.type == "start") {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const erika_alive = ref(true);
|
const erika_alive = ref(true);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function killErika() {
|
function killErika() {
|
||||||
playSound('/sounds/splat1.mp3')
|
playSound("/sounds/splat1.mp3");
|
||||||
erika_alive.value = false;
|
erika_alive.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
eventSource.close()
|
eventSource.close();
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="background">
|
<div class="background">
|
||||||
<div class="title">
|
<div class="title">Entrez dans le Royaume de Héron !</div>
|
||||||
Entrez dans le Royaume de Héron !
|
|
||||||
|
<div class="middle-row">
|
||||||
|
<div class="create-game-zone">
|
||||||
|
<button @click="createGame">Create Game</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="games-list-zone">
|
||||||
|
<div v-for="g in games">
|
||||||
|
<button
|
||||||
|
v-if="g"
|
||||||
|
@click="router.push({ path: `/game/${g._id}` })"
|
||||||
|
:class="
|
||||||
|
authStore.selfUser.userId == g.owner.userId ? 'own_game' : ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template v-if="g.started && g.ended"> GAME OVER </template>
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
g.teams.every((t) =>
|
||||||
|
t.players.every(
|
||||||
|
(p) => p.user.userId !== authStore.selfUser.userId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
SPECTATE
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="!g.started"> JOIN BACK </template>
|
||||||
|
<template v-else> CONTINUE </template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
g.teams.reduce(
|
||||||
|
(prev, curr) => prev.concat(curr.players),
|
||||||
|
[] as Player[]
|
||||||
|
).length
|
||||||
|
}}
|
||||||
|
current players:<br /><span>{{
|
||||||
|
g.teams
|
||||||
|
.reduce(
|
||||||
|
(prev, curr) => prev.concat(curr.players),
|
||||||
|
[] as Player[]
|
||||||
|
)
|
||||||
|
.map((p) => p.user.username)
|
||||||
|
.join(",")
|
||||||
|
}}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="middle-row">
|
|
||||||
<div class="create-game-zone">
|
|
||||||
<button @click="createGame">Create Game</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="games-list-zone">
|
|
||||||
<div v-for="g in games">
|
|
||||||
<button v-if="g" @click="router.push({ path: `/game/${g._id}` })"
|
|
||||||
:class="authStore.selfUser.userId == g.owner.userId ? 'own_game' : ''">
|
|
||||||
<template v-if="g.started && g.ended">
|
|
||||||
GAME OVER
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
v-else-if="g.teams.every(t => t.players.every(p => p.user.userId !== authStore.selfUser.userId))">
|
|
||||||
SPECTATE
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<template v-if="!g.started">
|
|
||||||
JOIN BACK
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
CONTINUE
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
{{ g.teams.reduce((prev, curr) => prev.concat(curr.players), [] as Player[]).length }} current
|
|
||||||
players:<br><span>{{
|
|
||||||
g.teams.reduce((prev, curr) => prev.concat(curr.players), [] as Player[]).map(p =>
|
|
||||||
p.user.username).join(",")
|
|
||||||
}}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="overlay" v-if="erika_alive">
|
|
||||||
<div class="erika-zone">
|
|
||||||
<img @click="router.push('/rules')" class="overlay-image"
|
|
||||||
src="https://static.wikia.nocookie.net/umineko/images/9/9d/PC_Erika.Casual.Sprite_36.png"
|
|
||||||
alt="pétasse" />
|
|
||||||
<div class="overlay-button">
|
|
||||||
<button class="kill-erika-button" @click.stop="killErika()">Tuer Erika</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="speech-bubble" @click.prevent.self>
|
|
||||||
<p>Ohoho!<br>Je suis Erika Furudo, la détective la plus forte du monde !</p>
|
|
||||||
<p>Je vais vous montrer comment jouer à Héron Realms !</p>
|
|
||||||
<p>Cliquez moi-dessus pour afficher les <span style="color: red">règles</span>.</p>
|
|
||||||
<p>Ou sinon, commencez direct en créant une partie ou rejoingnant une partie existante.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="overlay" v-if="erika_alive">
|
||||||
|
<div class="erika-zone">
|
||||||
|
<img
|
||||||
|
@click="router.push('/rules')"
|
||||||
|
class="overlay-image"
|
||||||
|
src="https://static.wikia.nocookie.net/umineko/img/9/9d/PC_Erika.Casual.Sprite_36.png"
|
||||||
|
alt="pétasse"
|
||||||
|
/>
|
||||||
|
<div class="overlay-button">
|
||||||
|
<button class="kill-erika-button" @click.stop="killErika()">
|
||||||
|
Tuer Erika
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="speech-bubble" @click.prevent.self>
|
||||||
|
<p>
|
||||||
|
Ohoho!<br />Je suis Erika Furudo, la détective la plus forte du monde
|
||||||
|
!
|
||||||
|
</p>
|
||||||
|
<p>Je vais vous montrer comment jouer à Héron Realms !</p>
|
||||||
|
<p>
|
||||||
|
Cliquez moi-dessus pour afficher les
|
||||||
|
<span style="color: red">règles</span>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ou sinon, commencez direct en créant une partie ou rejoingnant une
|
||||||
|
partie existante.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.background {
|
.background {
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
background-image: url('https://www.pockettactics.com/wp-content/sites/pockettactics/2023/01/fire-emblem-engage-review-29.jpg');
|
background-image: url("https://www.pockettactics.com/wp-content/sites/pockettactics/2023/01/fire-emblem-engage-review-29.jpg");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-family: 'Kanit', 'Roboto', sans-serif;
|
font-family: "Kanit", "Roboto", sans-serif;
|
||||||
font-size: 64px;
|
font-size: 64px;
|
||||||
color: white;
|
color: white;
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
margin-bottom: 100px;
|
margin-bottom: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.middle-row {
|
.middle-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 100px;
|
gap: 100px;
|
||||||
margin-bottom: 100px;
|
margin-bottom: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-game-zone {
|
.create-game-zone {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-game-zone button {
|
.create-game-zone button {
|
||||||
background-color: #4CAF50;
|
background-color: #4caf50;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 10px 24px;
|
padding: 10px 24px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
transition-duration: 0.4s;
|
transition-duration: 0.4s;
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.games-list-zone {
|
.games-list-zone {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.games-list-zone button {
|
.games-list-zone button {
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 20px 24px;
|
padding: 20px 24px;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
transition-duration: 0.4s;
|
transition-duration: 0.4s;
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.games-list-zone button.own_game {
|
.games-list-zone button.own_game {
|
||||||
border: solid yellow 5px;
|
border: solid yellow 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.erika-zone {
|
.erika-zone {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 60%;
|
top: 60%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
height: 40%;
|
height: 40%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-image {
|
.overlay-image {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-button {
|
.overlay-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.kill-erika-button {
|
.kill-erika-button {
|
||||||
display: none;
|
display: none;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
background-color: #f44336;
|
background-color: #f44336;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 10px 24px;
|
padding: 10px 24px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
transition-duration: 0.4s;
|
transition-duration: 0.4s;
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
}
|
}
|
||||||
|
|
||||||
.speech-bubble {
|
.speech-bubble {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 60%;
|
top: 60%;
|
||||||
right: 260px;
|
right: 260px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background-color: rgba(225, 225, 225, 0.8);
|
background-color: rgba(225, 225, 225, 0.8);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
font-family: 'Kanit', 'Roboto', sans-serif;
|
font-family: "Kanit", "Roboto", sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: black;
|
color: black;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.erika-zone:hover .overlay-button .kill-erika-button {
|
.erika-zone:hover .overlay-button .kill-erika-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+364
-292
@@ -1,320 +1,392 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="background">
|
<div class="background">
|
||||||
<div class="overlay-2">
|
<div class="overlay-2">
|
||||||
<div class="erika-zone-2">
|
<div class="erika-zone-2">
|
||||||
<img @click="router.push({ path: '/lobby' })" class="overlay-image"
|
<img
|
||||||
src="https://static.wikia.nocookie.net/umineko/images/9/9d/PC_Erika.Casual.Sprite_36.png"
|
@click="router.push({ path: '/lobby' })"
|
||||||
alt="pétasse" />
|
class="overlay-image"
|
||||||
</div>
|
src="https://static.wikia.nocookie.net/umineko/img/9/9d/PC_Erika.Casual.Sprite_36.png"
|
||||||
</div>
|
alt="pétasse"
|
||||||
<div class="text-content">
|
/>
|
||||||
<h1>Règles de Héron Realms</h1>
|
</div>
|
||||||
<section>
|
|
||||||
<p>Héron Realms, c'est un jeu de <span style="text-decoration:line-through;">héron building</span>
|
|
||||||
deck-building.</p>
|
|
||||||
<p>Tout le monde part d'un même deck de base.</p>
|
|
||||||
<p>Le but du jeu est de l'enrichir pour obtenir de quoi gagner.</p>
|
|
||||||
<p>Et comme la violence est une solution universelle, ça se traduit en battre ses adversaires.</p>
|
|
||||||
<p>​</p>
|
|
||||||
<p>Et quoi de mieux pour faire ça que des cartes colorées?</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="6" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="19" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="36" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="46" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<p>​</p>
|
|
||||||
<h2>Objectif</h2>
|
|
||||||
<p>L'unique objectif du jeu est d'être le dernier en lice. Pour y arriver, il faut descendre les Points
|
|
||||||
de Vie de vos adversaires à 0 en partant de 50.</p>
|
|
||||||
<p>
|
|
||||||
Le moyen principal d'y arriver, c'est de faire des <span style="color: red">Dégats</span> <img
|
|
||||||
src="/images/damage.png" class="icon_img" draggable="false" />. Tout le reste des mécaniques du
|
|
||||||
jeu sont là pour vous faciliter la tâche,
|
|
||||||
ou pour mettre des bâtons dans les roues de vos adversaires.
|
|
||||||
</p>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Deckbuilding</h2>
|
|
||||||
<p>Comme c'est un jeu de "deckbuilding", on va expliquer ce qu'est le "deck" et ce qui nous permet de le
|
|
||||||
"build".</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="undefined" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>​</p>
|
|
||||||
<p>
|
|
||||||
Le deck d'un joueur est-ce qui constitue sa pioche, qui lui permettra ensuite de constituer sa main
|
|
||||||
(la main est préparée à la fin du tour et permet de jouer le tour d'après).
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Quand la pioche est vide, elle est recyclée à partir de sa défausse.
|
|
||||||
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
La défausse est ce qui contient les cartes déjà jouées. Mais aussi celles qui sont achetées! Elle
|
|
||||||
finit par être mélangée pour former la nouvelle pioche.
|
|
||||||
</p>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Deck de base</h2>
|
|
||||||
<p>
|
|
||||||
Tout le monde commence avec 10 cartes divisées en 4 types:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>7x Or (1)</li>
|
|
||||||
<li>1x Or (2)</li>
|
|
||||||
<li>1x Dégats (1)</li>
|
|
||||||
<li>1x Dégats (2)</li>
|
|
||||||
</ul>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="56" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="59" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="58" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="57" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
La pioche est formée au hasard: on peut donc avoir une main de départ avec 5 or et 2 dégats, ou 3 or
|
|
||||||
et 4 dégats, ou 4 or et 3 dégats, etc.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
A partir de sa main, on peut jouer des cartes. Ces cartes peuvent déclencher des effets: on peut
|
|
||||||
considérer qu'ils vont dans un "pool commun", et qu'on peut les utiliser dans
|
|
||||||
l'ordre qu'on souhaite. 1 de dégats et 2 de dégats me donnent 3 ressources de dégats que je split
|
|
||||||
comme je veux ensuite. Pareil pour l'or et pour le soin, qui sont les 3 ressources
|
|
||||||
principales du jeu.
|
|
||||||
</p>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Types de cartes</h2>
|
|
||||||
<p>
|
|
||||||
Les cartes sont divisées en 3 catégories: objets, actions et champions.
|
|
||||||
</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="56" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="52" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="43" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>​</p>
|
|
||||||
<h3>Objets & actions</h3>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="56" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="52" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Par simplicité, disons que les objets et les actions sont pareil: on les joue, ils font un effet
|
|
||||||
disponible durant le tour actuel, et ils sont défaussés.
|
|
||||||
</p>
|
|
||||||
<h3>Champions (& gardes)</h3>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="43" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Les champions sont un peu plus complexes: ils restent sur le terrain, et on peut les utiliser à
|
|
||||||
chaque tour (en les mobilisant).
|
|
||||||
</p>
|
|
||||||
<p>Ils ont une défense <img src="/images/champion-shield.png" style="height: 30px" draggable="false" />
|
|
||||||
indiquée en bas à droite et ils ne sont assommés (défaussés) que si on leur fait des dégats =
|
|
||||||
défense.</p>
|
|
||||||
<p>Ces dégats doivent être faits d'un coup. Je ne peux pas faire un peu de dégats pendant un tour, et un
|
|
||||||
peu le tour d'après pour les tuer.</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="49" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Par ailleurs, certains champions sont des Gardes <img src="/images/champion-shield-guard.png"
|
|
||||||
style="height: 30px" draggable="false" /> (ils ont un bouclier noir), ce qui veut dire qu'on est
|
|
||||||
obligés de les tuer pour faire des dégats à des champions non-gardes ou au joueur.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
(J'en profite pour préciser qu'on peut en effet, sans garde, choisir d'attaquer le joueur
|
|
||||||
directement ou ses champions.)
|
|
||||||
Incidentellement, ça veut dire qu'avec seulement 2 de dégats, je ne peux rien faire à un joueur avec
|
|
||||||
un garde à 3 de défense.
|
|
||||||
</p>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Building</h2>
|
|
||||||
<p>
|
|
||||||
Passons au "building": on peut acheter des cartes pour les ajouter à notre défausse qui, si vous
|
|
||||||
avez suivi, constituera la prochaine pioche.
|
|
||||||
Avec le jeu de base, <span style="color: darkgoldenrod">l'or</span> <img src="/images/gold.png"
|
|
||||||
class="icon_img" draggable="false" /> sert exclusivement à accomplir ce but: acheter des cartes.
|
|
||||||
</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="6" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="19" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="36" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="46" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Un marché est mis à disposition de tous les joueurs, mettant à disposition 5 cartes (pas ici parce
|
|
||||||
que j'ai pas la place).
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Ces cartes ont chacune un coût indiqué en haut à droite.
|
|
||||||
Il faut au moins autant d'or que ce qui est indiqué pour acheter une carte. Une carte achetée laisse
|
|
||||||
la place à une nouvelle carte directement, qu'on peut
|
|
||||||
décider d'acheter juste après (tant qu'on a l'argent).
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Il ne nous reste plus qu'à comprendre ce que font les cartes du marché, puisqu'elles sont plus
|
|
||||||
complexes (et bien plus fortes) que votre deck de base.
|
|
||||||
Prenons des exemples!
|
|
||||||
</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Couleurs</h2>
|
|
||||||
<p>
|
|
||||||
La première chose qui ne vous échappera pas, c'est qu'on a 4 couleurs différentes pour les cartes.
|
|
||||||
</p>
|
|
||||||
<p>​</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="13" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="3" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="1" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>​</p>
|
|
||||||
<p>Les cartes <span style="color: darkgoldenrod">jaunes</span> sont les cartes Empire.</p>
|
|
||||||
<p>Elles font beaucoup de soin, et activent souvent des effets de pioche.</p>
|
|
||||||
<p>​</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="39" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="34" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="40" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>​</p>
|
|
||||||
<p>Les cartes <span style="color: red">rouges</span> sont les cartes Necros.</p>
|
|
||||||
<p>Elles sont les seules à pouvoir sacrifier des cartes pour les sortir de son deck de façon permanente.
|
|
||||||
</p>
|
|
||||||
<p>​</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="52" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="46" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="47" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>​</p>
|
|
||||||
<p>Les cartes <span style="color: darkgreen">vertes</span> sont les cartes Sauvages.</p>
|
|
||||||
<p>Elles font beaucoup de dégats, et forcent vos adversaires à jouer moins de cartes.</p>
|
|
||||||
<p>​</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="24" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="25" :tilted="false"></CardPreview>
|
|
||||||
<CardPreview :card_id="19" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>​</p>
|
|
||||||
<p>Les cartes <span style="color: darkblue">bleues</span> sont les cartes Guilde.</p>
|
|
||||||
<p>Elles donnent beaucoup d'or, assomment les champions adverses et manipulent votre pioche.</p>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Déclencheurs</h2>
|
|
||||||
<p>Les effets de cartes peuvent se déclencher pour 3 raisons différentes.</p>
|
|
||||||
<div class="card-row">
|
|
||||||
<CardPreview :card_id="14" :tilted="false"></CardPreview>
|
|
||||||
</div>
|
|
||||||
<p>Sur cette carte, on peut voir les trois en même temps.</p>
|
|
||||||
<p>La 1ère ligne s'active tout le temps.</p>
|
|
||||||
<p>La 2ème ligne est une capacité Allié: il faut une autre carte de la même couleur en jeu pour pouvoir
|
|
||||||
en profiter.</p>
|
|
||||||
<p>La 3ème ligne est un Effet Poubelle (différent du sacrifice des cartes rouges): vous pouvez retirer
|
|
||||||
cette carte de votre jeu pour bénéficier, en plus des autres effets, de l'effet marqué. Mais la
|
|
||||||
carte sera défaussée à tout jamais.</p>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Effets</h2>
|
|
||||||
<p>Une variété d'effets peuvent être inclus dans les cartes.</p>
|
|
||||||
<p>Il y en a plein et les expliquer ici serait relou, alors je vous laisse poser la question en vocal si
|
|
||||||
vous n'en comprenez pas un.</p>
|
|
||||||
<p>​</p>
|
|
||||||
<hr>
|
|
||||||
<h2>Activer les cartes dans la version en ligne</h2>
|
|
||||||
<p>Il suffit de cliquer sur la carte que vous voulez activer dans votre main pour la "LOCK".</p>
|
|
||||||
<p>Une carte "LOCKED" est considérée en jeu, & peut activer des effets Allié pour d'autres cartes, etc.
|
|
||||||
</p>
|
|
||||||
<p>Si vous avez des choix à faire sur la carte, un menu apparaîtra pour vous laisser choisir.</p>
|
|
||||||
<p>Pour faire des dommages, il y aura un bouton "Damage" sur vos ennemis. Pareil sur leurs champions.
|
|
||||||
</p>
|
|
||||||
<p>Quand vous avez un effet "Sacrifice", vous pouvez fouiller votre défausse pour choisir la carte à
|
|
||||||
sacrifier. Vous pouvez également sacrifier une carte de la main qui n'est pas encore locked!</p>
|
|
||||||
<h2>Zoomer sur les cartes</h2>
|
|
||||||
<p>Pour zoomer sur une carte, maintenez votre clic droit.</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-content">
|
||||||
|
<h1>Règles de Héron Realms</h1>
|
||||||
|
<section>
|
||||||
|
<p>
|
||||||
|
Héron Realms, c'est un jeu de
|
||||||
|
<span style="text-decoration: line-through">héron building</span>
|
||||||
|
deck-building.
|
||||||
|
</p>
|
||||||
|
<p>Tout le monde part d'un même deck de base.</p>
|
||||||
|
<p>Le but du jeu est de l'enrichir pour obtenir de quoi gagner.</p>
|
||||||
|
<p>
|
||||||
|
Et comme la violence est une solution universelle, ça se traduit en
|
||||||
|
battre ses adversaires.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<p>Et quoi de mieux pour faire ça que des cartes colorées?</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="6" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="19" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="36" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="46" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<p>​</p>
|
||||||
|
<h2>Objectif</h2>
|
||||||
|
<p>
|
||||||
|
L'unique objectif du jeu est d'être le dernier en lice. Pour y
|
||||||
|
arriver, il faut descendre les Points de Vie de vos adversaires à 0 en
|
||||||
|
partant de 50.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Le moyen principal d'y arriver, c'est de faire des
|
||||||
|
<span style="color: red">Dégats</span>
|
||||||
|
<img src="/img/damage.png" class="icon_img" draggable="false" />. Tout
|
||||||
|
le reste des mécaniques du jeu sont là pour vous faciliter la tâche,
|
||||||
|
ou pour mettre des bâtons dans les roues de vos adversaires.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Deckbuilding</h2>
|
||||||
|
<p>
|
||||||
|
Comme c'est un jeu de "deckbuilding", on va expliquer ce qu'est le
|
||||||
|
"deck" et ce qui nous permet de le "build".
|
||||||
|
</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="undefined" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>​</p>
|
||||||
|
<p>
|
||||||
|
Le deck d'un joueur est-ce qui constitue sa pioche, qui lui permettra
|
||||||
|
ensuite de constituer sa main (la main est préparée à la fin du tour
|
||||||
|
et permet de jouer le tour d'après).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Quand la pioche est vide, elle est recyclée à partir de sa défausse.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
La défausse est ce qui contient les cartes déjà jouées. Mais aussi
|
||||||
|
celles qui sont achetées! Elle finit par être mélangée pour former la
|
||||||
|
nouvelle pioche.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Deck de base</h2>
|
||||||
|
<p>Tout le monde commence avec 10 cartes divisées en 4 types:</p>
|
||||||
|
<ul>
|
||||||
|
<li>7x Or (1)</li>
|
||||||
|
<li>1x Or (2)</li>
|
||||||
|
<li>1x Dégats (1)</li>
|
||||||
|
<li>1x Dégats (2)</li>
|
||||||
|
</ul>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="56" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="59" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="58" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="57" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
La pioche est formée au hasard: on peut donc avoir une main de départ
|
||||||
|
avec 5 or et 2 dégats, ou 3 or et 4 dégats, ou 4 or et 3 dégats, etc.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
A partir de sa main, on peut jouer des cartes. Ces cartes peuvent
|
||||||
|
déclencher des effets: on peut considérer qu'ils vont dans un "pool
|
||||||
|
commun", et qu'on peut les utiliser dans l'ordre qu'on souhaite. 1 de
|
||||||
|
dégats et 2 de dégats me donnent 3 ressources de dégats que je split
|
||||||
|
comme je veux ensuite. Pareil pour l'or et pour le soin, qui sont les
|
||||||
|
3 ressources principales du jeu.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Types de cartes</h2>
|
||||||
|
<p>
|
||||||
|
Les cartes sont divisées en 3 catégories: objets, actions et
|
||||||
|
champions.
|
||||||
|
</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="56" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="52" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="43" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>​</p>
|
||||||
|
<h3>Objets & actions</h3>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="56" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="52" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Par simplicité, disons que les objets et les actions sont pareil: on
|
||||||
|
les joue, ils font un effet disponible durant le tour actuel, et ils
|
||||||
|
sont défaussés.
|
||||||
|
</p>
|
||||||
|
<h3>Champions (& gardes)</h3>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="43" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Les champions sont un peu plus complexes: ils restent sur le terrain,
|
||||||
|
et on peut les utiliser à chaque tour (en les mobilisant).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ils ont une défense
|
||||||
|
<img
|
||||||
|
src="/img/champion-shield.png"
|
||||||
|
style="height: 30px"
|
||||||
|
draggable="false"
|
||||||
|
/>
|
||||||
|
indiquée en bas à droite et ils ne sont assommés (défaussés) que si on
|
||||||
|
leur fait des dégats = défense.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ces dégats doivent être faits d'un coup. Je ne peux pas faire un peu
|
||||||
|
de dégats pendant un tour, et un peu le tour d'après pour les tuer.
|
||||||
|
</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="49" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Par ailleurs, certains champions sont des Gardes
|
||||||
|
<img
|
||||||
|
src="/img/champion-shield-guard.png"
|
||||||
|
style="height: 30px"
|
||||||
|
draggable="false"
|
||||||
|
/>
|
||||||
|
(ils ont un bouclier noir), ce qui veut dire qu'on est obligés de les
|
||||||
|
tuer pour faire des dégats à des champions non-gardes ou au joueur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
(J'en profite pour préciser qu'on peut en effet, sans garde, choisir
|
||||||
|
d'attaquer le joueur directement ou ses champions.) Incidentellement,
|
||||||
|
ça veut dire qu'avec seulement 2 de dégats, je ne peux rien faire à un
|
||||||
|
joueur avec un garde à 3 de défense.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Building</h2>
|
||||||
|
<p>
|
||||||
|
Passons au "building": on peut acheter des cartes pour les ajouter à
|
||||||
|
notre défausse qui, si vous avez suivi, constituera la prochaine
|
||||||
|
pioche. Avec le jeu de base,
|
||||||
|
<span style="color: darkgoldenrod">l'or</span>
|
||||||
|
<img src="/img/gold.png" class="icon_img" draggable="false" /> sert
|
||||||
|
exclusivement à accomplir ce but: acheter des cartes.
|
||||||
|
</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="6" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="19" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="36" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="46" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Un marché est mis à disposition de tous les joueurs, mettant à
|
||||||
|
disposition 5 cartes (pas ici parce que j'ai pas la place).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ces cartes ont chacune un coût indiqué en haut à droite. Il faut au
|
||||||
|
moins autant d'or que ce qui est indiqué pour acheter une carte. Une
|
||||||
|
carte achetée laisse la place à une nouvelle carte directement, qu'on
|
||||||
|
peut décider d'acheter juste après (tant qu'on a l'argent).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Il ne nous reste plus qu'à comprendre ce que font les cartes du
|
||||||
|
marché, puisqu'elles sont plus complexes (et bien plus fortes) que
|
||||||
|
votre deck de base. Prenons des exemples!
|
||||||
|
</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Couleurs</h2>
|
||||||
|
<p>
|
||||||
|
La première chose qui ne vous échappera pas, c'est qu'on a 4 couleurs
|
||||||
|
différentes pour les cartes.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="13" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="3" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="1" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>​</p>
|
||||||
|
<p>
|
||||||
|
Les cartes <span style="color: darkgoldenrod">jaunes</span> sont les
|
||||||
|
cartes Empire.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Elles font beaucoup de soin, et activent souvent des effets de pioche.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="39" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="34" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="40" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>​</p>
|
||||||
|
<p>
|
||||||
|
Les cartes <span style="color: red">rouges</span> sont les cartes
|
||||||
|
Necros.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Elles sont les seules à pouvoir sacrifier des cartes pour les sortir
|
||||||
|
de son deck de façon permanente.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="52" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="46" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="47" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>​</p>
|
||||||
|
<p>
|
||||||
|
Les cartes <span style="color: darkgreen">vertes</span> sont les
|
||||||
|
cartes Sauvages.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Elles font beaucoup de dégats, et forcent vos adversaires à jouer
|
||||||
|
moins de cartes.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="24" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="25" :tilted="false"></CardPreview>
|
||||||
|
<CardPreview :card_id="19" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>​</p>
|
||||||
|
<p>
|
||||||
|
Les cartes <span style="color: darkblue">bleues</span> sont les cartes
|
||||||
|
Guilde.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Elles donnent beaucoup d'or, assomment les champions adverses et
|
||||||
|
manipulent votre pioche.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Déclencheurs</h2>
|
||||||
|
<p>
|
||||||
|
Les effets de cartes peuvent se déclencher pour 3 raisons différentes.
|
||||||
|
</p>
|
||||||
|
<div class="card-row">
|
||||||
|
<CardPreview :card_id="14" :tilted="false"></CardPreview>
|
||||||
|
</div>
|
||||||
|
<p>Sur cette carte, on peut voir les trois en même temps.</p>
|
||||||
|
<p>La 1ère ligne s'active tout le temps.</p>
|
||||||
|
<p>
|
||||||
|
La 2ème ligne est une capacité Allié: il faut une autre carte de la
|
||||||
|
même couleur en jeu pour pouvoir en profiter.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
La 3ème ligne est un Effet Poubelle (différent du sacrifice des cartes
|
||||||
|
rouges): vous pouvez retirer cette carte de votre jeu pour bénéficier,
|
||||||
|
en plus des autres effets, de l'effet marqué. Mais la carte sera
|
||||||
|
défaussée à tout jamais.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Effets</h2>
|
||||||
|
<p>Une variété d'effets peuvent être inclus dans les cartes.</p>
|
||||||
|
<p>
|
||||||
|
Il y en a plein et les expliquer ici serait relou, alors je vous
|
||||||
|
laisse poser la question en vocal si vous n'en comprenez pas un.
|
||||||
|
</p>
|
||||||
|
<p>​</p>
|
||||||
|
<hr />
|
||||||
|
<h2>Activer les cartes dans la version en ligne</h2>
|
||||||
|
<p>
|
||||||
|
Il suffit de cliquer sur la carte que vous voulez activer dans votre
|
||||||
|
main pour la "LOCK".
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Une carte "LOCKED" est considérée en jeu, & peut activer des effets
|
||||||
|
Allié pour d'autres cartes, etc.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Si vous avez des choix à faire sur la carte, un menu apparaîtra pour
|
||||||
|
vous laisser choisir.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Pour faire des dommages, il y aura un bouton "Damage" sur vos ennemis.
|
||||||
|
Pareil sur leurs champions.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Quand vous avez un effet "Sacrifice", vous pouvez fouiller votre
|
||||||
|
défausse pour choisir la carte à sacrifier. Vous pouvez également
|
||||||
|
sacrifier une carte de la main qui n'est pas encore locked!
|
||||||
|
</p>
|
||||||
|
<h2>Zoomer sur les cartes</h2>
|
||||||
|
<p>Pour zoomer sur une carte, maintenez votre clic droit.</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.background {
|
.background {
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
background-image: url('/images/bg-darkgrass.png');
|
background-image: url("/img/bg-darkgrass.png");
|
||||||
background-size: auto;
|
background-size: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-content {
|
.text-content {
|
||||||
background-color: rgba(255, 255, 255, .75);
|
background-color: rgba(255, 255, 255, 0.75);
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
margin-bottom: 1.5em;
|
margin-bottom: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-row {
|
.card-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-2 {
|
.overlay-2 {
|
||||||
height: 00px;
|
height: 00px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
top: 0;
|
top: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.erika-zone-2 {
|
.erika-zone-2 {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 300px;
|
top: 300px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-image {
|
.overlay-image {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
import type { Game } from "~/netcode/interfaces";
|
||||||
// import goStore, { useEffect } from "~/netcode";
|
// import goStore, { useEffect } from "~/netcode";
|
||||||
// import type { Card } from "~/netcode/Card";
|
// import type { Card } from "~/netcode/Card";
|
||||||
// import { CardEffects, type Effect } from "~/netcode/Effect";
|
// import { CardEffects, type Effect } from "~/netcode/Effect";
|
||||||
@@ -10,7 +11,7 @@ import { defineStore } from "pinia";
|
|||||||
// useClientSideStore().activatedClientside = []
|
// useClientSideStore().activatedClientside = []
|
||||||
// })
|
// })
|
||||||
|
|
||||||
export const useClientSideStore = defineStore("clientside", {
|
export const useTurnStore = defineStore("turn", {
|
||||||
// state: () => ({
|
// state: () => ({
|
||||||
// activatedClientside: [] as Effect[],
|
// activatedClientside: [] as Effect[],
|
||||||
// effectsHandled: new Set([
|
// effectsHandled: new Set([
|
||||||
Reference in New Issue
Block a user