Files
front/components/game/card/Grouped.vue
T
2024-12-21 11:21:57 +01:00

255 lines
6.3 KiB
Vue

<script setup lang="ts">
import { delay } from "~/netcode/events";
import {
CardEffects,
CardType,
type Card,
type Container,
type Effect,
type PlayingTurn,
} from "~/netcode/interfaces";
export interface Props {
container: Container;
playingTurn?: PlayingTurn;
wrapped?: boolean;
hidden?: boolean;
noEffects?: boolean;
tokens?: Effect[];
championsKillable?: boolean;
discardable?: boolean;
locked?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
wrapped: () => false,
hidden: () => false,
noEffects: () => false,
tokens: () => [],
championsKillable: () => false,
discardable: () => false,
});
const emit = defineEmits([
"cardClick",
"effectClick",
"tokenClick",
"damageChampionClick",
"discardCardClick",
]);
const settingsStore = useSettingsStore();
const cards = computed(() => {
if (props.hidden) {
return props.container.cards.map((_v, i) => ({
cardId: undefined,
})) as unknown as Card[];
}
return props.container.cards;
});
const animationSpeed = computed(() => settingsStore.animationSpeed + "ms");
const shuffleAnimationSpeed = computed(
() => settingsStore.animationSpeed * 2 + "ms"
);
function populateContainer() { }
// const rotate = ref(false);
// async function shuffleContainer() {
// rotate.value = true;
// await delay(settingsStore.animationSpeed * 2);
// rotate.value = false;
// await delay(settingsStore.animationSpeed * 2);
// }
function getCardsPosition(cards: string) { }
const animationCallbacks = ref<Array<() => void>>([]);
function startAnimation() {
animationCallbacks.value.forEach((p) => p());
animationCallbacks.value = [];
}
function endAnimation() {
animationCallbacks.value.forEach((p) => p());
}
// watch(
// props.container,
// (newValue, oldValue) => {
// if (oldValue) {
// containerEvents.delete(oldValue._id);
// }
// if (newValue) {
// containerEvents.set(newValue._id, {
// populateContainer,
// shuffleContainer,
// getCardsPosition,
// startAnimation,
// endAnimation,
// });
// }
// },
// { immediate: true }
// );
// async function onEnter(_el: any, done: () => void) {
// animationCallbacks.value.push(done);
// }
// async function onLeave(_el: any, done: () => void) {
// animationCallbacks.value.push(done);
// }
onUnmounted(() => {
// containerEvents.delete(props.container._id);
});
const interval = ref<ReturnType<typeof setInterval>>();
// function startMove() {
// interval.value = setInterval(() => {
// props.container.cards.sort((a, b) => Math.random() - 0.5);
// }, 1000);
// }
// function stopMove() {
// clearInterval(interval.value);
// }
function tokenClick(token: Effect, card: Card) {
emit("tokenClick", { token, card });
}
function damageChampionClick(card: Card) {
emit("damageChampionClick", card);
}
function discardCardClick(card: Card) {
emit("discardCardClick", card);
}
</script>
<template>
<div class="cards_container" :class="{ wrapped, rotate: container.shuffling }">
<TransitionGroup name="list">
<GameCard @click="$emit('cardClick', c._id)" @effectClick="$emit('effectClick', c._id, $event)"
v-for="(c, i) in cards" :cardId="c?.cardId" :uuid="c._id" :wrapped="wrapped" :key="c?._id || i" :locked="locked ||
!playingTurn ||
playingTurn?.cardsLocked.includes(c._id.toString())
" :used_effects_indexes="c?.effects?.reduce((prev, e, i) => {
if (playingTurn?.lockedEffects.includes(e._id)) {
prev.push(i)
}
return prev
}, [] as number[])" :noEffects="noEffects" :discardable="discardable">
<template v-for="t in tokens">
<GameToken :token="t" @useToken="tokenClick(t, c)" v-if="
(t.effect == CardEffects.SACRIFICE &&
playingTurn &&
!playingTurn?.cardsLocked.includes(c._id.toString())) ||
(t.effect == CardEffects.PREPARE &&
c.cardType == CardType.CHAMPION) ||
(t.effect == CardEffects.STACK_NEXT_ACTION_BOUGHT &&
c.cardType == CardType.ACTION) ||
(t.effect == CardEffects.STUN &&
c.cardType == CardType.CHAMPION &&
championsKillable) ||
t.effect == CardEffects.PLAY_NEXT_CARD_BOUGHT ||
t.effect == CardEffects.STACK_NEXT_CARD_BOUGHT ||
t.effect == CardEffects.RESTACK_DISCARDED_CARD ||
(t.effect == CardEffects.RESTACK_DISCARDED_ACTION &&
c.cardType == CardType.ACTION) ||
(t.effect == CardEffects.RESTACK_DISCARDED_CHAMPION &&
c.cardType == CardType.CHAMPION)
"></GameToken>
</template>
<button v-if="championsKillable && c.cardType == CardType.CHAMPION" @click.stop="damageChampionClick(c)"
@mousedown.left.stop="">
Damage
</button>
<button v-if="
discardable && !playingTurn?.cardsLocked.includes(c._id.toString())
" @mousedown.left.stop="" @click.stop="discardCardClick(c)">
Discard
</button>
</GameCard>
</TransitionGroup>
<!-- <img :class="`card_image`" :src="'/cards/background.png'" draggable="false"> -->
</div>
</template>
<style scoped>
.list-move,
.list-enter-active,
.list-leave-active {
transition: all v-bind("animationSpeed") ease;
}
/* .list-leave-to, */
.list-enter-from {
opacity: 0;
/* transform: translateX(30px); */
}
.list-leave-active {
position: absolute;
height: unset;
}
.rotate {
rotate: 360deg;
}
.cards_container {
display: flex;
flex-direction: row;
justify-content: flex-start;
gap: 30px;
min-width: 200px;
flex-wrap: wrap;
transition: rotate v-bind("shuffleAnimationSpeed") ease;
}
.cards_container.wrapped {
flex-direction: column-reverse;
justify-content: center;
gap: 0;
}
.cards_container.wrapped:before {
height: calc(250px - 10px);
display: block;
content: "";
width: 0;
}
.empty {
max-height: 10px;
}
.empty>.card_image {
filter: brightness(0.5);
max-height: 250px;
}
</style>
<style>
use * use[data-effect-id="0"] {
opacity: 0;
}
.cards_container .card #background rect:first-child {
stroke-width: 0;
}
.cards_container .card.wrapped #background rect:first-child {
stroke-width: 1px;
}
.cards_container .card.card.wrapped:nth-child(5n) #background rect:first-child {
stroke-width: 2px;
}
</style>