Merge branch 'feat/netcode-rework' of https://git.legonzaur.fr/heronfief/front into feat/netcode-rework
release-tag / release-image (push) Successful in 28s
release-tag / release-image (push) Successful in 28s
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
NUXT_PUBLIC_ENDPOINT=http://localhost:8765
|
||||||
|
NUXT_PUBLIC_DISCORD_LOGIN_ENDPOINT=https://discord.com/oauth2/authorize?client_id=
|
||||||
|
NUXT_DEV_MODE=TRUE
|
||||||
@@ -6,6 +6,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- feat/netcode-rework
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release-image:
|
release-image:
|
||||||
|
|||||||
+215
-36
@@ -10,6 +10,7 @@ export interface Props {
|
|||||||
interactible?: boolean;
|
interactible?: boolean;
|
||||||
noEffects?: boolean;
|
noEffects?: boolean;
|
||||||
used_effects_indexes?: number[];
|
used_effects_indexes?: number[];
|
||||||
|
discardable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -18,9 +19,17 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
locked: () => true,
|
locked: () => true,
|
||||||
interactible: () => true,
|
interactible: () => true,
|
||||||
noEffects: () => false,
|
noEffects: () => false,
|
||||||
|
discardable: () => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["click", "effectClick"]);
|
const emit = defineEmits([
|
||||||
|
"click",
|
||||||
|
"effectClick",
|
||||||
|
"drop",
|
||||||
|
"move",
|
||||||
|
"startMove",
|
||||||
|
"stopMove",
|
||||||
|
]);
|
||||||
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
@@ -28,12 +37,35 @@ const childCount = ref<number>(0);
|
|||||||
|
|
||||||
const card = ref<HTMLElement>();
|
const card = ref<HTMLElement>();
|
||||||
|
|
||||||
|
const dragStatus = ref<{
|
||||||
|
hasClickedDown: boolean;
|
||||||
|
dragging: boolean;
|
||||||
|
intervalEvent?: ReturnType<typeof setTimeout>;
|
||||||
|
}>({
|
||||||
|
hasClickedDown: false,
|
||||||
|
dragging: false,
|
||||||
|
});
|
||||||
|
const initialOffsetX = ref(0);
|
||||||
|
const initialOffsetY = ref(0);
|
||||||
|
|
||||||
|
const mousePageX = ref(0);
|
||||||
|
const mousePageY = ref(0);
|
||||||
|
|
||||||
|
const offsetX = ref(0);
|
||||||
|
const offsetY = ref(0);
|
||||||
|
const offsetWidth = ref(0);
|
||||||
|
const offsetHeight = ref(0);
|
||||||
|
|
||||||
|
const translateX = ref(0);
|
||||||
|
const translateY = ref(0);
|
||||||
|
|
||||||
const extension = computed(() => {
|
const extension = computed(() => {
|
||||||
if (settingsStore.cardStyle == CardStyles.SVG) {
|
if (settingsStore.cardStyle == CardStyles.SVG) {
|
||||||
return ".svg";
|
return ".svg";
|
||||||
}
|
}
|
||||||
return ".png";
|
return ".png";
|
||||||
});
|
});
|
||||||
|
|
||||||
function cardClick() {
|
function cardClick() {
|
||||||
if (!props.card_id) return;
|
if (!props.card_id) return;
|
||||||
emit("click");
|
emit("click");
|
||||||
@@ -61,8 +93,126 @@ function rightClickUp(e: MouseEvent) {
|
|||||||
isZooming.value = false;
|
isZooming.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startDrag(e: MouseEvent) {
|
||||||
|
emit("startMove");
|
||||||
|
dragStatus.value.dragging = true;
|
||||||
|
translateX.value = 0;
|
||||||
|
translateY.value = 0;
|
||||||
|
const cardElement = card.value;
|
||||||
|
if (!cardElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mousePageX.value = e.pageX;
|
||||||
|
mousePageY.value = e.pageY;
|
||||||
|
initialOffsetX.value = e.offsetX;
|
||||||
|
initialOffsetY.value = e.offsetY;
|
||||||
|
offsetX.value = cardElement.offsetLeft + e.offsetX;
|
||||||
|
offsetY.value = cardElement.offsetTop + e.offsetY;
|
||||||
|
translateX.value = mousePageX.value - offsetX.value;
|
||||||
|
translateY.value = mousePageY.value - offsetY.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function leftClickDown(e: MouseEvent) {
|
||||||
|
dragStatus.value.hasClickedDown = true;
|
||||||
|
window.addEventListener("mousemove", mouseMove, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function leftClickUpGlobal(e: MouseEvent) {
|
||||||
|
mousePageX.value = e.pageX;
|
||||||
|
mousePageY.value = e.pageY;
|
||||||
|
|
||||||
|
if (dragStatus.value.dragging && dragStatus.value.hasClickedDown) {
|
||||||
|
dragStatus.value.dragging = false;
|
||||||
|
emit("drop", { x: e.pageX, y: e.pageY, uuid: props.card?._id });
|
||||||
|
emit("stopMove");
|
||||||
|
}
|
||||||
|
dragStatus.value.hasClickedDown = false;
|
||||||
|
clearTimeout(dragStatus.value.intervalEvent);
|
||||||
|
window.removeEventListener("mousemove", mouseMove, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function leftClickUp(e: MouseEvent) {
|
||||||
|
if (dragStatus.value.dragging == false && dragStatus.value.hasClickedDown) {
|
||||||
|
dragStatus.value.hasClickedDown = false;
|
||||||
|
cardClick();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sensitivity = 0.5;
|
||||||
|
function mouseMove(e: MouseEvent) {
|
||||||
|
if (
|
||||||
|
dragStatus.value.hasClickedDown &&
|
||||||
|
!dragStatus.value.dragging &&
|
||||||
|
Math.sqrt(Math.abs(e.movementX * e.movementY)) > sensitivity
|
||||||
|
) {
|
||||||
|
startDrag(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardElement = card.value;
|
||||||
|
if (!cardElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mousePageX.value = e.pageX;
|
||||||
|
mousePageY.value = e.pageY;
|
||||||
|
// console.log(cardElement.offsetLeft);
|
||||||
|
offsetX.value = cardElement.offsetLeft + initialOffsetX.value;
|
||||||
|
offsetY.value = cardElement.offsetTop + initialOffsetY.value;
|
||||||
|
|
||||||
|
translateX.value = mousePageX.value - offsetX.value;
|
||||||
|
translateY.value = mousePageY.value - offsetY.value;
|
||||||
|
|
||||||
|
// emit("move", { x: e.pageX, y: e.pageY, uuid: props.card?._id });
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
offsetWidth,
|
||||||
|
offsetHeight,
|
||||||
|
uuid: props.card?._id,
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
card.value?.style.setProperty("--random-seed", -Math.random() * 10 + "s");
|
window.addEventListener("mouseup", leftClickUpGlobal, false);
|
||||||
|
const cardElement = card.value;
|
||||||
|
if (!cardElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offsetX.value = cardElement.offsetLeft;
|
||||||
|
offsetY.value = cardElement.offsetTop;
|
||||||
|
offsetWidth.value = cardElement.offsetWidth;
|
||||||
|
offsetHeight.value = cardElement.offsetHeight;
|
||||||
|
cardElement.offsetWidth;
|
||||||
|
});
|
||||||
|
|
||||||
|
// onRenderTriggered(() => {
|
||||||
|
// const cardElement = card.value;
|
||||||
|
// if (!cardElement) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// offsetX.value = cardElement.offsetLeft + initialOffsetX.value;
|
||||||
|
// offsetY.value = cardElement.offsetTop + initialOffsetY.value;
|
||||||
|
|
||||||
|
// translateX.value = mousePageX.value - offsetX.value;
|
||||||
|
// translateY.value = mousePageY.value - offsetY.value;
|
||||||
|
// });
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
const cardElement = card.value;
|
||||||
|
if (!cardElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offsetX.value = cardElement.offsetLeft + initialOffsetX.value;
|
||||||
|
offsetY.value = cardElement.offsetTop + initialOffsetY.value;
|
||||||
|
|
||||||
|
translateX.value = mousePageX.value - offsetX.value;
|
||||||
|
translateY.value = mousePageY.value - offsetY.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("mouseup", leftClickUpGlobal, false);
|
||||||
|
// window.addEventListener("mousemove", mouseMove, false);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -71,25 +221,22 @@ onMounted(() => {
|
|||||||
ref="card"
|
ref="card"
|
||||||
class="card"
|
class="card"
|
||||||
:class="{
|
:class="{
|
||||||
|
dragging: dragStatus.dragging,
|
||||||
active: childCount > 0,
|
active: childCount > 0,
|
||||||
wrapped,
|
wrapped,
|
||||||
unlocked: !props.locked,
|
unlocked: !props.locked,
|
||||||
zoom: isZooming,
|
zoom: isZooming,
|
||||||
no_effects: noEffects || !props.locked,
|
no_effects: noEffects || !props.locked,
|
||||||
|
discardable,
|
||||||
}"
|
}"
|
||||||
@contextmenu.prevent="false"
|
@contextmenu.prevent="false"
|
||||||
@click.left="cardClick"
|
|
||||||
@mousedown.right="rightClickDown"
|
@mousedown.right="rightClickDown"
|
||||||
@mouseup.right="rightClickUp"
|
@mouseup.right="rightClickUp"
|
||||||
|
@mousedown.left="leftClickDown"
|
||||||
|
@mouseup.left="leftClickUp"
|
||||||
@mouseenter="mouseenter"
|
@mouseenter="mouseenter"
|
||||||
@mouseleave="mouseleave"
|
@mouseleave="mouseleave"
|
||||||
>
|
>
|
||||||
<div
|
|
||||||
id="contextMenuButtons"
|
|
||||||
:ref="(el) => { childCount = ((el as Element)?.childElementCount ?? 0) }"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
<template v-if="props.card_id">
|
<template v-if="props.card_id">
|
||||||
<!-- <svg
|
<!-- <svg
|
||||||
width="180"
|
width="180"
|
||||||
@@ -124,7 +271,7 @@ onMounted(() => {
|
|||||||
"
|
"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
/>
|
/>
|
||||||
<div></div>
|
<div id="overlay"><slot></slot></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
@@ -137,19 +284,15 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.card {
|
.card.dragging {
|
||||||
--max-height: 250px;
|
transform: translateX(v-bind("translateX + 'px'"))
|
||||||
--max-width: 179px;
|
translateY(v-bind("translateY + 'px'"));
|
||||||
--margin-bottom: calc(var(--max-height) * 0.05);
|
cursor: grabbing;
|
||||||
position: relative;
|
z-index: 999;
|
||||||
display: flex;
|
}
|
||||||
justify-content: center;
|
|
||||||
margin-bottom: 0px;
|
.card.dragging > svg {
|
||||||
margin-top: var(--margin-bottom);
|
transform: scale(110%);
|
||||||
transition: all 0.075s cubic-bezier(1, 1.81, 0.59, 1.42);
|
|
||||||
pointer-events: all;
|
|
||||||
max-height: var(--max-height);
|
|
||||||
max-width: var(--max-width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card.active {
|
.card.active {
|
||||||
@@ -172,15 +315,6 @@ onMounted(() => {
|
|||||||
drop-shadow(-0.5px -0.5px 0.5px gray);
|
drop-shadow(-0.5px -0.5px 0.5px gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card.unlocked {
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-bottom: var(--margin-bottom);
|
|
||||||
transform: translateY(calc(var(--margin-bottom) * -0.25));
|
|
||||||
filter: drop-shadow(
|
|
||||||
calc(var(--margin-bottom) * 0.3) var(--margin-bottom) 1px rgba(0, 0, 0, 0.6)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.zoom {
|
.card.zoom {
|
||||||
transform: scale(200%);
|
transform: scale(200%);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
@@ -207,10 +341,6 @@ onMounted(() => {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card.no_effects {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#contextMenuButtons > * {
|
#contextMenuButtons > * {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -253,4 +383,53 @@ onMounted(() => {
|
|||||||
filter: grayscale(0.6);
|
filter: grayscale(0.6);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.svg_effect text {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
--max-height: 250px;
|
||||||
|
--max-width: 179px;
|
||||||
|
--margin-bottom: calc(var(--max-height) * 0.05);
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-top: var(--margin-bottom);
|
||||||
|
transition: all 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
pointer-events: all;
|
||||||
|
max-height: var(--max-height);
|
||||||
|
max-width: var(--max-width);
|
||||||
|
/* cursor: grab; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:not(svg) {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
#overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 20%;
|
||||||
|
width: calc(100% - 8%);
|
||||||
|
height: 49%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.unlocked {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: var(--margin-bottom);
|
||||||
|
transform: translateY(calc(var(--margin-bottom) * -0.25));
|
||||||
|
filter: drop-shadow(
|
||||||
|
calc(var(--margin-bottom) * 0.3) var(--margin-bottom) 1px rgba(0, 0, 0, 0.6)
|
||||||
|
);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.discardable {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.discardable svg {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { delay } from "~/netcode/events";
|
import { delay } from "~/netcode/events";
|
||||||
import type { Card, Container, PlayingTurn } from "~/netcode/interfaces";
|
import {
|
||||||
|
CardEffects,
|
||||||
|
CardType,
|
||||||
|
type Card,
|
||||||
|
type Container,
|
||||||
|
type Effect,
|
||||||
|
type PlayingTurn,
|
||||||
|
} from "~/netcode/interfaces";
|
||||||
|
import TokenComponent from "./tokens/TokenComponent.vue";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
container: Container;
|
container: Container;
|
||||||
@@ -8,15 +16,27 @@ export interface Props {
|
|||||||
wrapped?: boolean;
|
wrapped?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
noEffects?: boolean;
|
noEffects?: boolean;
|
||||||
|
tokens?: Effect[];
|
||||||
|
championsKillable?: boolean;
|
||||||
|
discardable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
wrapped: () => false,
|
wrapped: () => false,
|
||||||
hidden: () => false,
|
hidden: () => false,
|
||||||
noEffects: () => false,
|
noEffects: () => false,
|
||||||
|
tokens: () => [],
|
||||||
|
championsKillable: () => false,
|
||||||
|
discardable: () => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["cardClick", "effectClick"]);
|
const emit = defineEmits([
|
||||||
|
"cardClick",
|
||||||
|
"effectClick",
|
||||||
|
"tokenClick",
|
||||||
|
"damageChampionClick",
|
||||||
|
"discardCardClick",
|
||||||
|
]);
|
||||||
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
@@ -34,6 +54,7 @@ const animationSpeed = computed(() => settingsStore.animationSpeed + "ms");
|
|||||||
const shuffleAnimationSpeed = computed(
|
const shuffleAnimationSpeed = computed(
|
||||||
() => settingsStore.animationSpeed * 2 + "ms"
|
() => settingsStore.animationSpeed * 2 + "ms"
|
||||||
);
|
);
|
||||||
|
|
||||||
function populateContainer() {}
|
function populateContainer() {}
|
||||||
|
|
||||||
// const rotate = ref(false);
|
// const rotate = ref(false);
|
||||||
@@ -87,6 +108,28 @@ function endAnimation() {
|
|||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// containerEvents.delete(props.container._id);
|
// 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -98,22 +141,64 @@ onUnmounted(() => {
|
|||||||
<CardPreview
|
<CardPreview
|
||||||
@click="$emit('cardClick', c._id)"
|
@click="$emit('cardClick', c._id)"
|
||||||
@effectClick="$emit('effectClick', c._id, $event)"
|
@effectClick="$emit('effectClick', c._id, $event)"
|
||||||
v-for="c in cards"
|
v-for="(c, i) in cards"
|
||||||
:card_id="c.cardId"
|
:card_id="c?.cardId"
|
||||||
:wrapped="wrapped"
|
:wrapped="wrapped"
|
||||||
:key="c._id"
|
:key="c?._id || i"
|
||||||
:card="c"
|
:card="c"
|
||||||
:locked="
|
:locked="
|
||||||
!playingTurn || playingTurn?.cardsLocked.includes(c._id.toString())
|
!playingTurn || playingTurn?.cardsLocked.includes(c._id.toString())
|
||||||
"
|
"
|
||||||
:used_effects_indexes="c.effects?.reduce( (prev,e, i)=>{
|
:used_effects_indexes="c?.effects?.reduce( (prev,e, i)=>{
|
||||||
if(playingTurn?.lockedEffects.includes(e._id)){
|
if(playingTurn?.lockedEffects.includes(e._id)){
|
||||||
prev.push(i)
|
prev.push(i)
|
||||||
}
|
}
|
||||||
return prev
|
return prev
|
||||||
},[] as number[])"
|
},[] as number[])"
|
||||||
:noEffects="noEffects"
|
:noEffects="noEffects"
|
||||||
|
:discardable="discardable"
|
||||||
>
|
>
|
||||||
|
<template v-for="t in tokens">
|
||||||
|
<TokenComponent
|
||||||
|
: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)
|
||||||
|
"
|
||||||
|
></TokenComponent>
|
||||||
|
</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>
|
||||||
</CardPreview>
|
</CardPreview>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
<!-- <img :class="`card_image`" :src="'/cards/background.png'" draggable="false"> -->
|
<!-- <img :class="`card_image`" :src="'/cards/background.png'" draggable="false"> -->
|
||||||
@@ -147,6 +232,7 @@ onUnmounted(() => {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
|
flex-wrap: wrap;
|
||||||
transition: rotate v-bind("shuffleAnimationSpeed") ease;
|
transition: rotate v-bind("shuffleAnimationSpeed") ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { buyCard, buyGem, useToken } from "~/netcode";
|
||||||
|
import {
|
||||||
|
CardEffects,
|
||||||
|
type Card,
|
||||||
|
type Container,
|
||||||
|
type Effect,
|
||||||
|
type Game,
|
||||||
|
type Player,
|
||||||
|
} from "~/netcode/interfaces";
|
||||||
|
import TokenComponent from "./tokens/TokenComponent.vue";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
game: Game;
|
||||||
|
discard: Container;
|
||||||
|
player: Player;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const emit = defineEmits(["close"]);
|
||||||
|
function marketCardClick(e: string) {
|
||||||
|
buyCard(props.game._id, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fireGemCardClick(e: string) {
|
||||||
|
buyGem(props.game._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const discardTokens = computed(() =>
|
||||||
|
props.game.currentTurn.tokens.filter((t) =>
|
||||||
|
[
|
||||||
|
CardEffects.RESTACK_DISCARDED_ACTION,
|
||||||
|
CardEffects.RESTACK_DISCARDED_CARD,
|
||||||
|
CardEffects.RESTACK_DISCARDED_CHAMPION,
|
||||||
|
CardEffects.SACRIFICE,
|
||||||
|
].includes(t.effect)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
function tokenClick({ card, token }: { card: Card; token: Effect }) {
|
||||||
|
useToken(props.game._id, token._id, card._id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="overlay">
|
||||||
|
<button @click="$emit('close')">Close</button>
|
||||||
|
<span>Discard of {{ player.user.username }}</span>
|
||||||
|
|
||||||
|
<div class="discard_unwrapped" @click.stop>
|
||||||
|
<CardsGrouped
|
||||||
|
:container="discard"
|
||||||
|
:wrapped="false"
|
||||||
|
:hidden="false"
|
||||||
|
:tokens="discardTokens"
|
||||||
|
:noEffects="true"
|
||||||
|
:playingTurn="game.currentTurn"
|
||||||
|
@tokenClick="tokenClick"
|
||||||
|
></CardsGrouped>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
span {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
.overlay {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
position: fixed;
|
||||||
|
z-index: 200;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.discard_unwrapped {
|
||||||
|
width: max-content;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
/* position: relative; */
|
||||||
|
gap: 2px;
|
||||||
|
margin: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
/* filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, 0.6)); */
|
||||||
|
/* backdrop-filter: blur(5px) brightness(80%); */
|
||||||
|
background: rgba(60, 47, 117, 0.46);
|
||||||
|
/* left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%); */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+46
-10
@@ -1,6 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { buyCard } from "~/netcode";
|
import { buyCard, buyGem, useToken } from "~/netcode";
|
||||||
import type { Container, Game } from "~/netcode/interfaces";
|
import {
|
||||||
|
CardEffects,
|
||||||
|
type Card,
|
||||||
|
type Container,
|
||||||
|
type Effect,
|
||||||
|
type Game,
|
||||||
|
} from "~/netcode/interfaces";
|
||||||
|
import TokenComponent from "./tokens/TokenComponent.vue";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
game: Game;
|
game: Game;
|
||||||
@@ -25,12 +32,35 @@ function marketCardClick(e: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fireGemCardClick(e: string) {
|
function fireGemCardClick(e: string) {
|
||||||
console.log(e);
|
buyGem(props.game._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const marketTokens = computed(() =>
|
||||||
|
props.game.currentTurn.tokens.filter((t) =>
|
||||||
|
[
|
||||||
|
CardEffects.STACK_NEXT_CARD_BOUGHT,
|
||||||
|
CardEffects.STACK_NEXT_ACTION_BOUGHT,
|
||||||
|
CardEffects.PLAY_NEXT_CARD_BOUGHT,
|
||||||
|
].includes(t.effect)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const gemsTokens = computed(() =>
|
||||||
|
props.game.currentTurn.tokens.filter((t) =>
|
||||||
|
[
|
||||||
|
CardEffects.STACK_NEXT_CARD_BOUGHT,
|
||||||
|
CardEffects.PLAY_NEXT_CARD_BOUGHT,
|
||||||
|
].includes(t.effect)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
function tokenClick({ card, token }: { card: Card; token: Effect }) {
|
||||||
|
useToken(props.game._id, token._id, card._id);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="market">
|
<div class="market">
|
||||||
<CardsGrouped
|
<CardsGrouped
|
||||||
:container="marketStack"
|
:container="marketStack"
|
||||||
:wrapped="true"
|
:wrapped="true"
|
||||||
@@ -41,34 +71,40 @@ function fireGemCardClick(e: string) {
|
|||||||
:container="market"
|
:container="market"
|
||||||
@cardClick="marketCardClick"
|
@cardClick="marketCardClick"
|
||||||
:noEffects="true"
|
:noEffects="true"
|
||||||
></CardsGrouped>
|
:tokens="marketTokens"
|
||||||
|
@tokenClick="tokenClick"
|
||||||
|
>
|
||||||
|
</CardsGrouped>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<CardsGrouped
|
<CardsGrouped
|
||||||
:container="fire_gems"
|
:container="fire_gems"
|
||||||
:wrapped="true"
|
:wrapped="true"
|
||||||
@cardClick="fireGemCardClick"
|
@cardClick="fireGemCardClick"
|
||||||
:noEffects="true"
|
:noEffects="true"
|
||||||
|
:tokens="gemsTokens"
|
||||||
|
@tokenClick="tokenClick"
|
||||||
></CardsGrouped>
|
></CardsGrouped>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#market {
|
.market {
|
||||||
width: min-content;
|
width: max-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, 0.6));
|
isolation: initial;
|
||||||
backdrop-filter: blur(5px) brightness(80%);
|
/* filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, 0.6)); */
|
||||||
|
/* backdrop-filter: blur(5px) brightness(80%); */
|
||||||
background: rgba(155, 62, 62, 0.46);
|
background: rgba(155, 62, 62, 0.46);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Game, Player } from "~/netcode/interfaces";
|
import { damagePlayer, makeDiscard } from "~/netcode";
|
||||||
|
import { CardEffects, type Game, type Player } from "~/netcode/interfaces";
|
||||||
|
|
||||||
const emit = defineEmits(["click"]);
|
const emit = defineEmits(["click"]);
|
||||||
|
|
||||||
@@ -15,6 +16,24 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
isTurn: () => false,
|
isTurn: () => false,
|
||||||
self: () => false,
|
self: () => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
function makeDiscardClick() {
|
||||||
|
makeDiscard(props.game._id, props.player._id);
|
||||||
|
}
|
||||||
|
function damage() {
|
||||||
|
damagePlayer(props.game._id, props.player._id, 1);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -48,6 +67,24 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
<PlayerStats :game="props.game" :player="props.player"> </PlayerStats>
|
<PlayerStats :game="props.game" :player="props.player"> </PlayerStats>
|
||||||
<!-- </template> -->
|
<!-- </template> -->
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
v-if="
|
||||||
|
game.currentTurn.tokens.some(
|
||||||
|
(t) => t.effect == CardEffects.MAKE_DISCARD
|
||||||
|
) &&
|
||||||
|
props.player._id != selfPlayer?._id &&
|
||||||
|
!isTurn
|
||||||
|
"
|
||||||
|
@click.stop="makeDiscardClick"
|
||||||
|
>
|
||||||
|
Make Discard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="props.player._id != selfPlayer?._id && !isTurn"
|
||||||
|
@click.stop="damage"
|
||||||
|
>
|
||||||
|
Damage
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { endTurn, joinGame, lockCard, lockEffect, startGame } from "~/netcode";
|
import {
|
||||||
import type { Game, Player } from "~/netcode/interfaces";
|
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";
|
import { useTurnStore } from "~/stores/turnStore";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@@ -37,8 +52,15 @@ const isSelf = computed(
|
|||||||
);
|
);
|
||||||
const isSelfTurn = computed(() => isSelf && isPlayerTurn);
|
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 warningText = ref("Effets restants!");
|
||||||
const turnStore = useTurnStore();
|
|
||||||
|
|
||||||
// const make_discard = (target: Player) =>
|
// const make_discard = (target: Player) =>
|
||||||
// clientStore.findUseEffect(CardEffects.MAKE_DISCARD, target);
|
// clientStore.findUseEffect(CardEffects.MAKE_DISCARD, target);
|
||||||
@@ -114,6 +136,16 @@ function effectClick(cardUUID: string, effectIndex: number) {
|
|||||||
// 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)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -172,6 +204,14 @@ function effectClick(cardUUID: string, effectIndex: number) {
|
|||||||
:playingTurn="props.game.currentTurn"
|
:playingTurn="props.game.currentTurn"
|
||||||
@cardClick="boardCardClick"
|
@cardClick="boardCardClick"
|
||||||
@effectClick="effectClick"
|
@effectClick="effectClick"
|
||||||
|
:tokens="cardTokens"
|
||||||
|
@tokenClick="tokenClick"
|
||||||
|
:championsKillable="!isSelf && isSelfTurn.value"
|
||||||
|
:discardable="
|
||||||
|
(player.fuckUMarkers > 0 || player.discardMarkers > 0) && isSelf
|
||||||
|
"
|
||||||
|
@damageChampionClick="damageChampionClick"
|
||||||
|
@discardCardClick="discardCardClick"
|
||||||
>
|
>
|
||||||
</CardsGrouped>
|
</CardsGrouped>
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const isTurn = computed(
|
|||||||
<div class="turn_icon">
|
<div class="turn_icon">
|
||||||
<img src="/img/fuck.svg" class="icon_img" draggable="false" />
|
<img src="/img/fuck.svg" class="icon_img" draggable="false" />
|
||||||
<span class="turn_icon_number">{{
|
<span class="turn_icon_number">{{
|
||||||
(isTurn && (player.discardMarkers ?? 0) + (player.fuckUMarkers ?? 0)) || 0
|
(player.discardMarkers ?? 0) + (player.fuckUMarkers ?? 0)
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Effect } from "~/netcode/interfaces";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
token: Effect;
|
||||||
|
};
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const emit = defineEmits(["useToken"]);
|
||||||
|
|
||||||
|
function onClick(e: MouseEvent) {
|
||||||
|
emit("useToken", props.token);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div @click.stop="onClick" @mousedown.left.stop="" id="token">
|
||||||
|
{{ props.token.effect }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#token {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid white;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: 0.2s all;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#token:hover {
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+95
-21
@@ -2,7 +2,7 @@ import type { Game, User } from "./interfaces";
|
|||||||
|
|
||||||
export function subscribe(endpoint: string) {
|
export function subscribe(endpoint: string) {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const evtSource = new EventSource(new URL(endpoint, config.public.endpoint), {
|
const evtSource = new EventSource(config.public.endpoint + endpoint, {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -11,12 +11,9 @@ export function subscribe(endpoint: string) {
|
|||||||
|
|
||||||
export async function getLobby() {
|
export async function getLobby() {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const req = await useFetch<Array<Game>>(
|
const req = await useFetch<Array<Game>>(config.public.endpoint + "/games", {
|
||||||
new URL("/games", config.public.endpoint).href,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
}
|
});
|
||||||
);
|
|
||||||
if (req.data.value == null) {
|
if (req.data.value == null) {
|
||||||
throw new Error("Lobby answer is empty");
|
throw new Error("Lobby answer is empty");
|
||||||
}
|
}
|
||||||
@@ -26,7 +23,7 @@ export async function getLobby() {
|
|||||||
export async function whoami() {
|
export async function whoami() {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const { data, pending, error, refresh } = await useFetch<User>(
|
const { data, pending, error, refresh } = await useFetch<User>(
|
||||||
new URL("/whoami", config.public.endpoint).href,
|
config.public.endpoint + "/whoami",
|
||||||
{
|
{
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
}
|
}
|
||||||
@@ -48,13 +45,10 @@ export async function whoami() {
|
|||||||
|
|
||||||
export async function createGame() {
|
export async function createGame() {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const res = await $fetch<Game>(
|
const res = await $fetch<Game>(config.public.endpoint + "/games", {
|
||||||
new URL("/games", config.public.endpoint).href,
|
|
||||||
{
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
}
|
});
|
||||||
);
|
|
||||||
console.log(res);
|
console.log(res);
|
||||||
navigateTo("/game/" + res._id);
|
navigateTo("/game/" + res._id);
|
||||||
}
|
}
|
||||||
@@ -62,7 +56,7 @@ export async function createGame() {
|
|||||||
export async function startGame(game: string) {
|
export async function startGame(game: string) {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const res = await $fetch<Game>(
|
const res = await $fetch<Game>(
|
||||||
new URL(`games/${game}/start`, config.public.endpoint).href,
|
config.public.endpoint + `/games/${game}/start`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
@@ -73,7 +67,7 @@ export async function startGame(game: string) {
|
|||||||
export async function joinGame(game: string) {
|
export async function joinGame(game: string) {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const res = await $fetch<Game>(
|
const res = await $fetch<Game>(
|
||||||
new URL(`games/${game}/join`, config.public.endpoint).href,
|
config.public.endpoint + `/games/${game}/join`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
@@ -83,7 +77,7 @@ export async function joinGame(game: string) {
|
|||||||
|
|
||||||
export async function endTurn(game: string) {
|
export async function endTurn(game: string) {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
await $fetch(new URL(`games/${game}/endTurn`, config.public.endpoint).href, {
|
await $fetch(config.public.endpoint + `/games/${game}/endTurn`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
});
|
});
|
||||||
@@ -92,8 +86,7 @@ export async function endTurn(game: string) {
|
|||||||
export async function lockCard(game: string, cardId: string) {
|
export async function lockCard(game: string, cardId: string) {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
await $fetch(
|
await $fetch(
|
||||||
new URL(`games/${game}/turn/lockCard/${cardId}`, config.public.endpoint)
|
config.public.endpoint + `/games/${game}/turn/lockCard/${cardId}`,
|
||||||
.href,
|
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
@@ -104,8 +97,7 @@ export async function lockCard(game: string, cardId: string) {
|
|||||||
export async function buyCard(game: string, cardId: string) {
|
export async function buyCard(game: string, cardId: string) {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
await $fetch(
|
await $fetch(
|
||||||
new URL(`games/${game}/turn/buyCard/${cardId}`, config.public.endpoint)
|
config.public.endpoint + `/games/${game}/turn/buyCard/${cardId}`,
|
||||||
.href,
|
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
@@ -113,11 +105,93 @@ export async function buyCard(game: string, cardId: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function buyGem(game: string) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
await $fetch(config.public.endpoint + `/games/${game}/turn/buyGem`, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function useToken(
|
||||||
|
game: string,
|
||||||
|
tokenId: string,
|
||||||
|
cardId?: string,
|
||||||
|
playerId?: string
|
||||||
|
) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
await $fetch(
|
||||||
|
config.public.endpoint + `/games/${game}/turn/useToken/${tokenId}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
body: { cardId, playerId },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function lockEffect(game: string, effectId: string) {
|
export async function lockEffect(game: string, effectId: string) {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
await $fetch(
|
await $fetch(
|
||||||
new URL(`games/${game}/turn/lockEffect/${effectId}`, config.public.endpoint)
|
config.public.endpoint + `/games/${game}/turn/lockEffect/${effectId}`,
|
||||||
.href,
|
{
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function makeDiscard(
|
||||||
|
game: string,
|
||||||
|
playerId: string,
|
||||||
|
amount: number = 1
|
||||||
|
) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
await $fetch(
|
||||||
|
config.public.endpoint + `/games/${game}/turn/makeDiscard/${playerId}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function damagePlayer(
|
||||||
|
game: string,
|
||||||
|
playerId: string,
|
||||||
|
amount: number = 1
|
||||||
|
) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
await $fetch(
|
||||||
|
config.public.endpoint + `/games/${game}/turn/damagePlayer/${playerId}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
body: { amount },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function damageChampion(
|
||||||
|
game: string,
|
||||||
|
playerId: string,
|
||||||
|
cardId: string
|
||||||
|
) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
await $fetch(
|
||||||
|
config.public.endpoint + `/games/${game}/turn/damageChampion/${cardId}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
body: { playerId },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function discardCard(game: string, cardId: string) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
await $fetch(
|
||||||
|
config.public.endpoint + `/games/${game}/turn/discardCard/${cardId}`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
|
|||||||
+57
-1
@@ -1,3 +1,56 @@
|
|||||||
|
export enum CardEffects {
|
||||||
|
//Base
|
||||||
|
GOLD = "gold",
|
||||||
|
DAMAGE = "damage",
|
||||||
|
HEAL = "heal",
|
||||||
|
PREPARE = "prepare",
|
||||||
|
STUN = "stun",
|
||||||
|
SACRIFICE = "sacrifice",
|
||||||
|
DRAW = "draw",
|
||||||
|
DRAW_AND_DISCARD = "draw_and_discard",
|
||||||
|
MAKE_DISCARD = "make_discard",
|
||||||
|
RESTACK_DISCARDED_CHAMPION = "restack_discarded_champion",
|
||||||
|
RESTACK_DISCARDED_CARD = "restack_discarded_card",
|
||||||
|
STACK_NEXT_ACTION_BOUGHT = "stack_next_action_bought",
|
||||||
|
STACK_NEXT_CARD_BOUGHT = "stack_next_card_bought",
|
||||||
|
PLAY_NEXT_CARD_BOUGHT = "play_next_card_bought",
|
||||||
|
|
||||||
|
//DLCs
|
||||||
|
BUY_FOR_FREE = "buy_for_free",
|
||||||
|
|
||||||
|
//Journeys
|
||||||
|
DAMAGE_ALL_CHAMPIONS = "damage_all_champions",
|
||||||
|
|
||||||
|
//Journeys: Hunters
|
||||||
|
DISCARD_X_AND_DRAW_X = "discard_x_and_draw_x",
|
||||||
|
PREPARE_ANOTHER_CHAMPION = "prepare_another_champion",
|
||||||
|
//Journey: Travelers
|
||||||
|
PREPARE_ALL_CHAMPIONS = "prepare_all_champions",
|
||||||
|
CHEAPER_CHAMPION = "cheaper_champion",
|
||||||
|
CHEAPER_CHAMPIONS_PASSIVE = "cheaper_champions_passive",
|
||||||
|
CHEAPER_ACTION = "cheaper_action",
|
||||||
|
CONTROL_OPPOSING_CHAMPION_PASSIVE = "control_opposing_champion_passive",
|
||||||
|
|
||||||
|
RESTACK_DISCARDED_ACTION = "restack_discarded_action",
|
||||||
|
|
||||||
|
//Ancestry
|
||||||
|
KEEP_IN_HAND = "keep_in_hand",
|
||||||
|
BUY_GEM_FOR_FREE = "buy_gem_for_free",
|
||||||
|
CHEAPER_SKILLS_PASSIVE = "cheaper_skills_passive",
|
||||||
|
CHEAPER_CARD_IF_HIGHER_PRICE = "cheaper_card_if_higher_price",
|
||||||
|
PICK_FACTION = "pick_faction",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CardType {
|
||||||
|
HERO = "hero",
|
||||||
|
HERO_ABILITY = "hero_ability",
|
||||||
|
CURRENCY = "currency",
|
||||||
|
WEAPON = "weapon",
|
||||||
|
CHAMPION = "champion",
|
||||||
|
ACTION = "action",
|
||||||
|
ITEM = "item",
|
||||||
|
}
|
||||||
|
|
||||||
export interface GameObject {
|
export interface GameObject {
|
||||||
_id: string;
|
_id: string;
|
||||||
}
|
}
|
||||||
@@ -47,8 +100,11 @@ export interface Container extends GameObject {
|
|||||||
shuffling?: boolean;
|
shuffling?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Effect extends GameObject {}
|
export interface Effect extends GameObject {
|
||||||
|
effect: CardEffects;
|
||||||
|
}
|
||||||
export interface Card extends GameObject {
|
export interface Card extends GameObject {
|
||||||
cardId: number;
|
cardId: number;
|
||||||
|
cardType: CardType;
|
||||||
effects: Effect[];
|
effects: Effect[];
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -4,9 +4,8 @@ export default defineNuxtConfig({
|
|||||||
ssr: false,
|
ssr: false,
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
endpoint: "http://localhost:8765", // can be overridden by NUXT_PUBLIC_ENDPOINT environment variable
|
endpoint: process.env.NUXT_PUBLIC_ENDPOINT, // can be overridden by NUXT_PUBLIC_ENDPOINT environment variable
|
||||||
discordLoginEndpoint:
|
discordLoginEndpoint: process.env.NUXT_PUBLIC_DISCORD_LOGIN_ENDPOINT,
|
||||||
"https://discord.com/oauth2/authorize?client_id=460020057867681792&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8765%2Flogin&scope=identify",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modules: ["@pinia/nuxt", "@pinia-plugin-persistedstate/nuxt"],
|
modules: ["@pinia/nuxt", "@pinia-plugin-persistedstate/nuxt"],
|
||||||
|
|||||||
+119
-11
@@ -1,9 +1,15 @@
|
|||||||
<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, useToken } from "~/netcode";
|
||||||
import { delay } from "~/netcode/events";
|
import { delay } from "~/netcode/events";
|
||||||
import { type Container, type Game, type Player } from "~/netcode/interfaces";
|
import {
|
||||||
|
type Container,
|
||||||
|
type Effect,
|
||||||
|
type Game,
|
||||||
|
type Player,
|
||||||
|
} from "~/netcode/interfaces";
|
||||||
import { playSound } from "~/helpers/audioHelpers";
|
import { playSound } from "~/helpers/audioHelpers";
|
||||||
|
import TokenComponent from "~/components/tokens/TokenComponent.vue";
|
||||||
|
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
@@ -17,19 +23,15 @@ const {
|
|||||||
pending,
|
pending,
|
||||||
error,
|
error,
|
||||||
refresh,
|
refresh,
|
||||||
} = await useFetch<Game>(
|
} = await useFetch<Game>(config.public.endpoint + `/games/${gameId}`, {
|
||||||
new URL(`/games/${gameId}`, config.public.endpoint).href,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(game);
|
|
||||||
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,
|
||||||
@@ -147,6 +149,20 @@ eventSource.addEventListener("message", async (message) => {
|
|||||||
current_game.currentTurn.cardsLocked.push(data.card);
|
current_game.currentTurn.cardsLocked.push(data.card);
|
||||||
await delay(settingsStore.animationSpeed);
|
await delay(settingsStore.animationSpeed);
|
||||||
});
|
});
|
||||||
|
} else if (data.type == "destroyCard") {
|
||||||
|
const fromContainer = containers.value[data.from as string];
|
||||||
|
queueAction(async () => {
|
||||||
|
const index = fromContainer.cards.findIndex(
|
||||||
|
(c) => !c || c._id == data.card
|
||||||
|
);
|
||||||
|
fromContainer.cards.splice(index, 1);
|
||||||
|
await delay(settingsStore.animationSpeed);
|
||||||
|
});
|
||||||
|
|
||||||
|
queueAction(async () => {
|
||||||
|
playSound("/sounds/destroy.mp3");
|
||||||
|
await delay(settingsStore.animationSpeed);
|
||||||
|
});
|
||||||
} else if (data.type == "joinGame") {
|
} else if (data.type == "joinGame") {
|
||||||
queueAction(async () => {
|
queueAction(async () => {
|
||||||
playSound("/sounds/teleport.mp3");
|
playSound("/sounds/teleport.mp3");
|
||||||
@@ -157,6 +173,14 @@ eventSource.addEventListener("message", async (message) => {
|
|||||||
current_game.currentTurn.lockedEffects.push(data.effect);
|
current_game.currentTurn.lockedEffects.push(data.effect);
|
||||||
await delay(settingsStore.animationSpeed);
|
await delay(settingsStore.animationSpeed);
|
||||||
});
|
});
|
||||||
|
} else if (data.type == "unlockEffect") {
|
||||||
|
queueAction(async () => {
|
||||||
|
const effectIndex = current_game.currentTurn.lockedEffects.indexOf(
|
||||||
|
data.effect
|
||||||
|
);
|
||||||
|
current_game.currentTurn.lockedEffects.splice(effectIndex, 1);
|
||||||
|
await delay(settingsStore.animationSpeed);
|
||||||
|
});
|
||||||
} else if (data.type == "currentTurn.updateGold") {
|
} else if (data.type == "currentTurn.updateGold") {
|
||||||
queueAction(async () => {
|
queueAction(async () => {
|
||||||
console.log(`adding gold amount : ` + data.operation);
|
console.log(`adding gold amount : ` + data.operation);
|
||||||
@@ -172,6 +196,14 @@ eventSource.addEventListener("message", async (message) => {
|
|||||||
current_game.currentTurn.tokens.push(data.token);
|
current_game.currentTurn.tokens.push(data.token);
|
||||||
await delay(settingsStore.animationSpeed);
|
await delay(settingsStore.animationSpeed);
|
||||||
});
|
});
|
||||||
|
} else if (data.type == "currentTurn.useToken") {
|
||||||
|
queueAction(async () => {
|
||||||
|
const index = current_game.currentTurn.tokens.findIndex(
|
||||||
|
(t) => t._id == data.tokenId
|
||||||
|
);
|
||||||
|
current_game.currentTurn.tokens.splice(index, 1);
|
||||||
|
await delay(settingsStore.animationSpeed);
|
||||||
|
});
|
||||||
} else if (data.type == "team.updateHealth") {
|
} else if (data.type == "team.updateHealth") {
|
||||||
queueAction(async () => {
|
queueAction(async () => {
|
||||||
const team = game.value?.teams.find((t) => t._id == data.team);
|
const team = game.value?.teams.find((t) => t._id == data.team);
|
||||||
@@ -181,6 +213,56 @@ eventSource.addEventListener("message", async (message) => {
|
|||||||
console.log(`adding health amount : ` + data.operation);
|
console.log(`adding health amount : ` + data.operation);
|
||||||
team.health = data.health;
|
team.health = data.health;
|
||||||
});
|
});
|
||||||
|
} else if (data.type == "player.addFuckUMarker") {
|
||||||
|
queueAction(async () => {
|
||||||
|
console.log(`adding fuckUMarkers : ` + data.operation);
|
||||||
|
const targetPlayer = playerList.value.find((p) => p._id == data.playerId);
|
||||||
|
if (!targetPlayer) {
|
||||||
|
throw new Error("Cannot add fuckU Markers : player not found");
|
||||||
|
}
|
||||||
|
targetPlayer.fuckUMarkers = data.fuckUMarkers;
|
||||||
|
});
|
||||||
|
} else if (data.type == "player.removeFuckUMarker") {
|
||||||
|
queueAction(async () => {
|
||||||
|
console.log(`adding fuckUMarkers : ` + data.operation);
|
||||||
|
const targetPlayer = playerList.value.find((p) => p._id == data.playerId);
|
||||||
|
if (!targetPlayer) {
|
||||||
|
throw new Error("Cannot remove fuckU Markers : player not found");
|
||||||
|
}
|
||||||
|
targetPlayer.fuckUMarkers = data.fuckUMarkers;
|
||||||
|
});
|
||||||
|
} else if (data.type == "player.addDiscardMarker") {
|
||||||
|
queueAction(async () => {
|
||||||
|
console.log(`adding discardMarkers : ` + data.operation);
|
||||||
|
const targetPlayer = playerList.value.find((p) => p._id == data.playerId);
|
||||||
|
if (!targetPlayer) {
|
||||||
|
throw new Error("Cannot add discard Markers : player not found");
|
||||||
|
}
|
||||||
|
targetPlayer.discardMarkers = data.discardMarkers;
|
||||||
|
});
|
||||||
|
} else if (data.type == "player.removeDiscardMarker") {
|
||||||
|
queueAction(async () => {
|
||||||
|
console.log(`adding fuckUMarkers : ` + data.operation);
|
||||||
|
const targetPlayer = playerList.value.find((p) => p._id == data.playerId);
|
||||||
|
if (!targetPlayer) {
|
||||||
|
throw new Error("Cannot remove discard Markers : player not found");
|
||||||
|
}
|
||||||
|
targetPlayer.discardMarkers = data.discardMarkers;
|
||||||
|
});
|
||||||
|
} else if (data.type == "killTeam") {
|
||||||
|
queueAction(async () => {
|
||||||
|
const index = game.value?.teams.findIndex((t) => t._id == data.teamId);
|
||||||
|
if (!index) {
|
||||||
|
console.error("team index not found. Desyncs might happen");
|
||||||
|
await refresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
game.value?.teams.splice(index, 1);
|
||||||
|
});
|
||||||
|
} else if (data.type == "endGame") {
|
||||||
|
queueAction(async () => {
|
||||||
|
alert("game Ended");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -245,6 +327,14 @@ function focusPlayer(p: Player) {
|
|||||||
// focusPlayer(first_player)
|
// focusPlayer(first_player)
|
||||||
// })
|
// })
|
||||||
|
|
||||||
|
function useTokenClick(t: Effect) {
|
||||||
|
console.log(t);
|
||||||
|
useToken(game.value!._id, t._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDiscard() {
|
||||||
|
discard_unwrapped.value = true;
|
||||||
|
}
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
});
|
});
|
||||||
@@ -272,7 +362,12 @@ onUnmounted(() => {
|
|||||||
:class="{ invisible: discard_unwrapped }"
|
:class="{ invisible: discard_unwrapped }"
|
||||||
@click="() => (discard_unwrapped = true)"
|
@click="() => (discard_unwrapped = true)"
|
||||||
>
|
>
|
||||||
<CardsGrouped :container="focusedPlayer.discard" :wrapped="true">
|
<CardsGrouped
|
||||||
|
:container="focusedPlayer.discard"
|
||||||
|
:wrapped="true"
|
||||||
|
@card-click="showDiscard"
|
||||||
|
:no-effects="true"
|
||||||
|
>
|
||||||
</CardsGrouped>
|
</CardsGrouped>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -284,7 +379,13 @@ onUnmounted(() => {
|
|||||||
:fire_gems="game.fireGems"
|
:fire_gems="game.fireGems"
|
||||||
:marketStack="game.marketStack"
|
:marketStack="game.marketStack"
|
||||||
></MarketCards>
|
></MarketCards>
|
||||||
<div id="turnStats">{{ game.currentTurn.tokens }}</div>
|
<div>
|
||||||
|
<TokenComponent
|
||||||
|
:token="t"
|
||||||
|
v-for="t in game.currentTurn.tokens"
|
||||||
|
@useToken="useTokenClick"
|
||||||
|
></TokenComponent>
|
||||||
|
</div>
|
||||||
<div id="player_list_container">
|
<div id="player_list_container">
|
||||||
<div id="player_list">
|
<div id="player_list">
|
||||||
<PlayerListElement
|
<PlayerListElement
|
||||||
@@ -309,6 +410,13 @@ onUnmounted(() => {
|
|||||||
></PlayerSlot>
|
></PlayerSlot>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
|
<DiscardOverlay
|
||||||
|
v-show="discard_unwrapped"
|
||||||
|
:game="game"
|
||||||
|
:player="focusedPlayer"
|
||||||
|
:discard="focusedPlayer.discard"
|
||||||
|
@close="discard_unwrapped = false"
|
||||||
|
></DiscardOverlay>
|
||||||
<ResultDialog
|
<ResultDialog
|
||||||
:isVisible="showResults"
|
:isVisible="showResults"
|
||||||
:won="isWinner"
|
:won="isWinner"
|
||||||
@@ -432,7 +540,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
.slide-left-enter-active,
|
.slide-left-enter-active,
|
||||||
.slide-left-leave-active {
|
.slide-left-leave-active {
|
||||||
transition: all v-bind("turnChangeAnimationSpeed") ease-out;
|
transition: all v-bind("settingsStore.animationSpeed * 2 + 'ms'") ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-left-enter-from {
|
.slide-left-enter-from {
|
||||||
|
|||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user