448 lines
14 KiB
Vue
448 lines
14 KiB
Vue
<script setup lang="ts">
|
|
import {
|
|
damageChampion,
|
|
discardCard,
|
|
endTurn,
|
|
joinGame,
|
|
lockCard,
|
|
lockEffect,
|
|
startGame,
|
|
useToken,
|
|
} from "~/netcode";
|
|
import {
|
|
CardEffects,
|
|
type Card,
|
|
type Effect,
|
|
type Game,
|
|
type Player,
|
|
} from "~/netcode/interfaces";
|
|
import { useTurnStore } from "~/stores/turnStore";
|
|
|
|
export interface Props {
|
|
player: Player;
|
|
game: Game;
|
|
hide_stack_and_discard?: boolean;
|
|
}
|
|
|
|
const authStore = useAuthStore();
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
hide_stack_and_discard: () => false,
|
|
});
|
|
const playerList = computed(() =>
|
|
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.currentTeam)
|
|
);
|
|
|
|
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 isSelfTurn = computed(() => isSelf && isPlayerTurn);
|
|
|
|
const cardTokens = computed(() =>
|
|
props.game.currentTurn.tokens.filter(
|
|
(t) =>
|
|
[CardEffects.PREPARE, CardEffects.STUN].includes(t.effect) ||
|
|
(CardEffects.SACRIFICE == t.effect && isSelf)
|
|
)
|
|
);
|
|
|
|
const warningText = ref("Effets restants!");
|
|
|
|
// const make_discard = (target: Player) =>
|
|
// clientStore.findUseEffect(CardEffects.MAKE_DISCARD, target);
|
|
// const sacrifice = (target: CardParser) => clientStore.findUseEffect(CardEffects.SACRIFICE, target);
|
|
// const stun = (target: CardParser) => clientStore.findUseEffect(CardEffects.STUN, target)
|
|
// const restack_discarded_champion = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CHAMPION, target)
|
|
// const restack_discarded_card = (target: Card) => clientStore.findUseEffect(CardEffects.RESTACK_DISCARDED_CARD, target)
|
|
// const prepare_champion = (target: CardParser) => clientStore.findUseEffect(CardEffects.PREPARE, target)
|
|
|
|
let warning = ref(false);
|
|
|
|
// function has_actions_remaining(): boolean {
|
|
// return (goStore.self?.turn?.damage_reserve ? goStore.self?.turn?.damage_reserve > 0 : false)
|
|
// || (goStore.self?.turn?.gold_reserve ? goStore.self?.turn?.gold_reserve >= (goStore.current_game?.market.reduce((min, card) => Math.min(min, card?.cost ?? Infinity), Infinity) ?? Infinity) : false)
|
|
// || (goStore.self?.turn?.fuck_u_markers ? goStore.self?.turn?.fuck_u_markers > 0 : false)
|
|
// || !goStore.self?.turn?.effects_availables.every(e => !e.usable || e.effect_type == EffectType.SUICIDE)
|
|
// || !goStore.self?.board.every(c => (c.uuid in (goStore.self?.turn?.cards_locked ?? {})))
|
|
// || false
|
|
// }
|
|
|
|
// function can_buy_fire_gem(): boolean {
|
|
// return (goStore.self?.turn?.gold_reserve ? goStore.self?.turn?.gold_reserve >= 2 : false)
|
|
// }
|
|
|
|
function checkEndTurn() {
|
|
// TODO: Add a confirmation dialog if resources are left
|
|
// if (
|
|
// (!warning.value)
|
|
// && has_actions_remaining()) {
|
|
// // Set a warning flag to true
|
|
// // Display an alert somehow using this warning flag
|
|
// warning.value = true
|
|
// }
|
|
// else if (!warning.value && can_buy_fire_gem()) {
|
|
// warningText.value = "P'tite Fire gem?"
|
|
// warning.value = true
|
|
// }
|
|
// else {
|
|
// warningText.value = "Effets restants!"
|
|
// warning.value = false
|
|
// endTurn()
|
|
// }
|
|
|
|
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 effectClick(cardUUID: string, effectIndex: number) {
|
|
const card = props.player.board.cards.find((c) => c._id == cardUUID);
|
|
if (!card) {
|
|
console.error("card not found");
|
|
return;
|
|
}
|
|
if (!props.game.currentTurn.cardsLocked.includes(cardUUID)) {
|
|
return;
|
|
}
|
|
const effectUUID = card.effects[effectIndex]._id;
|
|
lockEffect(props.game._id, effectUUID);
|
|
}
|
|
// function damage_team_all(team: Team) {
|
|
// for (let i = 0; i < (goStore.self?.turn?.damage_reserve ?? 0); i++) {
|
|
// damage_team(team)
|
|
// }
|
|
// }
|
|
|
|
// function check_for_guard(player: Player) {
|
|
// return player.board.some(c => c.guard === true)
|
|
// }
|
|
|
|
function tokenClick({ card, token }: { card: Card; token: Effect }) {
|
|
useToken(props.game._id, token._id, card._id, props.player._id);
|
|
}
|
|
function damageChampionClick(card: Card) {
|
|
damageChampion(props.game._id, props.player._id, card._id);
|
|
}
|
|
function discardCardClick(card: Card) {
|
|
discardCard(props.game._id, card._id);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="player" :class="{ turn: isPlayerTurn, self: isSelf }">
|
|
<div id="current_player_board">
|
|
<!-- <div class="other_player_buttons_container">
|
|
<button @click.stop="damage_team(props.player.team)"
|
|
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)">
|
|
Damage
|
|
</button>
|
|
<button @click.stop="damage_team_all(props.player.team)"
|
|
v-if="isSelfTurn && isPlayerEnemy && goStore.self?.turn && props.player.team"
|
|
class="damage_button all_damage_button"
|
|
:disabled="(goStore.self?.turn.damage_reserve ?? 0) <= 0 || check_for_guard(props.player)">
|
|
(All)
|
|
</button>
|
|
<button v-if="goStore.self && isSelfTurn && isPlayerEnemy && goStore.self?.turn"
|
|
@click.stop="make_discard(props.player)" class="make_discard_button"
|
|
:disabled="!clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.MAKE_DISCARD) && e.usable)">
|
|
make Discard
|
|
</button>
|
|
</div> -->
|
|
<!-- <br /> -->
|
|
|
|
<GameCardGrouped
|
|
:container="player.board"
|
|
:wrapped="false"
|
|
:playingTurn="props.game.currentTurn"
|
|
@cardClick="boardCardClick"
|
|
@effectClick="effectClick"
|
|
:tokens="cardTokens"
|
|
@tokenClick="tokenClick"
|
|
:championsKillable="!isSelf && isSelfTurn.value"
|
|
:discardable="
|
|
(player.fuckUMarkers > 0 || player.discardMarkers > 0) && isSelf
|
|
"
|
|
@damageChampionClick="damageChampionClick"
|
|
@discardCardClick="discardCardClick"
|
|
>
|
|
</GameCardGrouped>
|
|
<br />
|
|
<!-- <div class="cards_container"> -->
|
|
<!-- HAND -->
|
|
<!-- <template v-if="isSelf">
|
|
<CardPreview :card_id="c?.card_id" v-for="c in props.player.hand" @click="c && playCard(c)">
|
|
<button
|
|
v-if="props.player.turn && c && isSelf && (props.player.turn.discard_markers + props.player.turn.fuck_u_markers) > 0"
|
|
@click.stop="discard(c)">
|
|
DISCARD
|
|
</button>
|
|
</CardPreview>
|
|
</template> -->
|
|
|
|
<!-- BOARD -->
|
|
<!-- <CardPreview :card_id="c.cardId" v-for="c in props.player.board.cards"
|
|
@click="console.log/*lockCard(c)*/"
|
|
:locked="/*props.player.turn && c.uuid in props.player.turn.cards_locked*/ false"> -->
|
|
|
|
<!-- <template v-if="c && isSelf && isPlayerTurn && props.player.turn">
|
|
// Card Locked
|
|
<template v-if="c.uuid in props.player.turn.cards_locked">
|
|
<template v-for="e in clientStore.maskedServerEffects">
|
|
<template v-if="e.card == c">
|
|
<button v-if="!clientStore.effectsHandled.has(e.effect)" @click.stop="useEffect(e)"
|
|
:disabled="!e.usable"
|
|
:class="{ 'suicide_effect': e.effect_type == EffectType.SUICIDE }">
|
|
EFFECT<br>
|
|
{{ e.effect + " " + (e.effect_type ?? '') }}
|
|
</button>
|
|
<button v-if="clientStore.effectsHandled.has(e.effect)"
|
|
@click.stop="clientStore.activateEffect(e)" :disabled="!e.usable"
|
|
:class="{ 'suicide_effect': e.effect_type == EffectType.SUICIDE }">
|
|
EFFECT<br>
|
|
{{ e.effect + " " + (e.effect_type ?? '') }}
|
|
</button>
|
|
</template>
|
|
</template>
|
|
<button
|
|
v-if="clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.PREPARE) && e.usable) && c.card_type == CardType.CHAMPION"
|
|
@click.stop="prepare_champion(c)">PREPARE</button>
|
|
</template>
|
|
// Card Unlocked
|
|
<template v-if="!(c.uuid in props.player.turn.cards_locked)">
|
|
<button v-if="(props.player.turn.discard_markers + props.player.turn.fuck_u_markers) > 0"
|
|
@click.stop="discard(c)">
|
|
DISCARD
|
|
</button>
|
|
<button
|
|
v-if="clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.SACRIFICE) && e.usable)"
|
|
@click.stop="sacrifice(c)">
|
|
SACRIFICE
|
|
</button>
|
|
<button @click.stop="lockCard(c)"
|
|
:disabled="((props.player.turn.discard_markers + props.player.turn.fuck_u_markers) > 0 && c !== props.player.turn.discard_marker_card) || props.player.turn.fuck_u_markers > 0">
|
|
LOCK
|
|
</button>
|
|
</template>
|
|
</template>
|
|
<template
|
|
v-if="c && goStore.self?.turn && isPlayerEnemy && isSelfTurn && (c.health_as_champion ?? 0) > 0">
|
|
<button class="enemy_action"
|
|
:disabled="(goStore.self?.turn.damage_reserve ?? 0) < (c.health_as_champion ?? Infinity) || (check_for_guard(props.player) && !c.guard)"
|
|
@click.stop="damage_champion(c)">
|
|
DAMAGE ({{ c.health_as_champion }})
|
|
</button>
|
|
<button class="enemy_action"
|
|
v-if="clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.STUN) && e.usable)"
|
|
@click.stop="stun(c)" :disabled="(check_for_guard(props.player) && !c.guard)">
|
|
STUN
|
|
</button>
|
|
</template> -->
|
|
<!-- </CardPreview> -->
|
|
<!-- </div> -->
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.warning {
|
|
background-color: rgba(255, 0, 0, 0.7);
|
|
color: white;
|
|
padding: 5px;
|
|
border-radius: 5px;
|
|
width: 100px;
|
|
font-size: 0.85em;
|
|
margin-left: 5px;
|
|
visibility: hidden;
|
|
}
|
|
|
|
.warning.visible {
|
|
visibility: visible;
|
|
}
|
|
|
|
.warning-fire-gem {
|
|
background-color: rgba(255, 165, 0, 0.7);
|
|
}
|
|
|
|
#turn_buttons {
|
|
grid-area: turn_buttons;
|
|
}
|
|
|
|
#end_turn {
|
|
z-index: 10;
|
|
height: 100%;
|
|
}
|
|
|
|
#end_turn.hidden {
|
|
visibility: hidden;
|
|
}
|
|
|
|
.player {
|
|
display: grid;
|
|
height: 100%;
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: min-content 1fr;
|
|
/* border-top: 5px solid black; */
|
|
grid-template-areas:
|
|
"player_stats"
|
|
"player"
|
|
"turn_buttons";
|
|
justify-items: center;
|
|
transition: all 1s;
|
|
}
|
|
|
|
#current_player_board {
|
|
grid-area: player;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
max-width: 1200px;
|
|
}
|
|
|
|
.damage_button {
|
|
background-color: #ff4d4d;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 5px 25px;
|
|
margin: 5px 0 0 0;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s ease;
|
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.damage_button:disabled {
|
|
background-color: rgba(255, 77, 77, 0.5);
|
|
color: rgba(255, 255, 255, 0.5);
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.damage_button:hover {
|
|
background-color: #e60000;
|
|
}
|
|
|
|
.damage_button:disabled:hover {
|
|
background-color: rgba(230, 0, 0, 0.5);
|
|
}
|
|
|
|
.damage_button:active {
|
|
transform: translateY(2px);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.all_damage_button {
|
|
padding: 5px 5px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.make_discard_button {
|
|
background-color: #55b42f;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 5px 15px;
|
|
margin: 5px 0 0 0;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s ease;
|
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.make_discard_button:disabled {
|
|
background-color: rgba(85, 180, 47, 0.5);
|
|
color: rgba(255, 255, 255, 0.5);
|
|
text-transform: uppercase;
|
|
cursor: not-allowed;
|
|
transition: background-color 0.3s ease;
|
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.make_discard_button:hover {
|
|
background-color: #03883a;
|
|
}
|
|
|
|
.make_discard_button:disabled:hover {
|
|
background-color: rgba(3, 136, 58, 0.5);
|
|
}
|
|
|
|
.make_discard_button:active {
|
|
transform: translateY(2px);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.turn_icon {
|
|
position: relative;
|
|
}
|
|
|
|
#player_info {
|
|
display: flex;
|
|
align-items: center;
|
|
min-width: 200px;
|
|
}
|
|
|
|
#player_banner.big {
|
|
padding: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
max-width: min-content;
|
|
border: 5px solid black;
|
|
border-radius: 10px;
|
|
filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, 0.6));
|
|
backdrop-filter: blur(5px) brightness(80%) grayscale(80%);
|
|
}
|
|
|
|
#player_banner img {
|
|
height: 50px;
|
|
border-radius: 25px;
|
|
}
|
|
|
|
.turn_icon img {
|
|
height: 50px;
|
|
}
|
|
|
|
.turn_icon_number {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
.end_turn_button:active {
|
|
transform: translateY(2px);
|
|
/* Add a slight press effect on click */
|
|
box-shadow: none;
|
|
/* Remove shadow on click */
|
|
}
|
|
|
|
.suicide_effect {
|
|
background: linear-gradient(to bottom right, #666666, #ccc);
|
|
color: red;
|
|
}
|
|
</style>
|