Feat : card distribute

This commit is contained in:
2024-06-13 22:39:46 -04:00
parent 4a56f3ed62
commit a22c6e1352
11 changed files with 565 additions and 487 deletions
+120 -90
View File
@@ -1,23 +1,23 @@
import noise from "~/assets/shaders/noise.frag?raw"
import noise from "~/assets/shaders/noise.frag?raw";
import resolveLygia from "~/assets/workers/resolve-lygia.esm"
import resolveLygia from "~/assets/workers/resolve-lygia.esm";
let interval: number
let timeout: number
let interval: number;
let timeout: number;
const shaderOptions = { quality: 1, framerate: 1000 / 60 }
const screen = { width: 0, height: 0, devicePixelRatio: 0 }
const shaderOptions = { quality: 1, framerate: 1000 / 60 };
const screen = { width: 0, height: 0, devicePixelRatio: 0 };
const _colorStart = [0, 0, 0]
const _colorEnd = [0, 0, 0]
const _colorStart = [0, 0, 0];
const _colorEnd = [0, 0, 0];
let fragmentShader = resolveLygia(noise);
let canvas: OffscreenCanvas
let canvas: OffscreenCanvas;
let render: (time: number) => void
let render: (time: number) => void;
let must_resize = false
let must_resize = false;
async function main(canvas: OffscreenCanvas) {
const gl = canvas.getContext("webgl");
@@ -25,36 +25,47 @@ async function main(canvas: OffscreenCanvas) {
return;
}
// setup GLSL program
const program = gl.createProgram()
if (!program) { return }
const program = gl.createProgram();
if (!program) {
return;
}
const fragment = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragment) { return }
gl.shaderSource(fragment, fragmentShader)
if (!fragment) {
return;
}
gl.shaderSource(fragment, fragmentShader);
gl.compileShader(fragment);
if (!gl.getShaderParameter(fragment, gl.COMPILE_STATUS)) {
const lastError = gl.getShaderInfoLog(fragment);
console.error(lastError)
console.error(lastError);
gl.deleteShader(fragment);
return
return;
}
const vertex = gl.createShader(gl.VERTEX_SHADER);
if (!vertex) { return }
gl.shaderSource(vertex, `attribute vec4 a_position;void main() {gl_Position = a_position;}`)
if (!vertex) {
return;
}
gl.shaderSource(
vertex,
`attribute vec4 a_position;void main() {gl_Position = a_position;}`
);
gl.compileShader(vertex);
if (!gl.getShaderParameter(vertex, gl.COMPILE_STATUS)) {
const lastError = gl.getShaderInfoLog(vertex);
console.error(lastError)
console.error(lastError);
gl.deleteShader(vertex);
return
return;
}
gl.attachShader(program, fragment)
gl.attachShader(program, vertex)
gl.linkProgram(program)
gl.attachShader(program, fragment);
gl.attachShader(program, vertex);
gl.linkProgram(program);
const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) { return }
if (!linked) {
return;
}
// const program = webglUtils.createProgramFromSources(gl, [vs, fragmentShader2]);
// look up where the vertex data needs to go.
@@ -75,21 +86,30 @@ async function main(canvas: OffscreenCanvas) {
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// fill it with a 2 triangles that cover clipspace
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, // first triangle
1, -1,
-1, 1,
-1, 1, // second triangle
1, -1,
1, 1,
]), gl.STATIC_DRAW);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1,
-1, // first triangle
1,
-1,
-1,
1,
-1,
1, // second triangle
1,
-1,
1,
1,
]),
gl.STATIC_DRAW
);
const gl2 = gl
const gl2 = gl;
function render(time: number) {
if (must_resize) {
canvas.width = screen.width * shaderOptions.quality / 10
canvas.height = screen.height * shaderOptions.quality / 10
canvas.width = (screen.width * shaderOptions.quality) / 10;
canvas.height = (screen.height * shaderOptions.quality) / 10;
must_resize = false;
}
@@ -113,21 +133,29 @@ async function main(canvas: OffscreenCanvas) {
gl2.FLOAT, // the data is 32bit floats
false, // don't normalize the data
0, // 0 = move forward size * sizeof(type) each iteration to get the next position
0, // start at the beginning of the buffer
0 // start at the beginning of the buffer
);
const secondRatio = screen.width / screen.height
const secondRatio = screen.width / screen.height;
if (secondRatio < 1) {
const half = canvas.height / 2
gl2.uniform2f(resolutionLocation, canvas.width, canvas.height * secondRatio);
gl2.uniform2f(offset, 0, -half + ((half) / (1 / secondRatio)));
const half = canvas.height / 2;
gl2.uniform2f(
resolutionLocation,
canvas.width,
canvas.height * secondRatio
);
gl2.uniform2f(offset, 0, -half + half / (1 / secondRatio));
} else {
const half = canvas.width / 2
gl2.uniform2f(resolutionLocation, canvas.width / secondRatio, canvas.height);
gl2.uniform2f(offset, -half + ((half) / secondRatio), 0);
const half = canvas.width / 2;
gl2.uniform2f(
resolutionLocation,
canvas.width / secondRatio,
canvas.height
);
gl2.uniform2f(offset, -half + half / secondRatio, 0);
}
gl2.uniform1i(quality, shaderOptions.quality)
gl2.uniform1i(quality, shaderOptions.quality);
gl2.uniform3f(colorStart, _colorStart[0], _colorStart[1], _colorStart[2]);
gl2.uniform3f(colorEnd, _colorEnd[0], _colorEnd[1], _colorEnd[2]);
@@ -135,80 +163,82 @@ async function main(canvas: OffscreenCanvas) {
gl2.drawArrays(
gl2.TRIANGLES,
0, // offset
6, // num vertices to process
6 // num vertices to process
);
if (timeout) {
self.clearTimeout(timeout)
self.clearTimeout(timeout);
timeout = self.setTimeout(() => {
requestAnimationFrame(render)
}, 1000 / shaderOptions.framerate)
requestAnimationFrame(render);
}, 1000 / shaderOptions.framerate);
}
}
requestAnimationFrame(render)
requestAnimationFrame(render);
// self.clearInterval(interval)
// interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate)
self.clearTimeout(timeout)
self.clearTimeout(timeout);
if (shaderOptions.framerate == 0) {
return render
return render;
}
timeout = self.setTimeout(() => {
requestAnimationFrame(render)
}, 1000 / shaderOptions.framerate)
return render
requestAnimationFrame(render);
}, 1000 / shaderOptions.framerate);
return render;
}
self.addEventListener("message", (e) => {
const data = e.data
const data = e.data;
if (data.command == "initShader") {
canvas = data.canvas
shaderOptions.quality = data.shaderOptions.quality
shaderOptions.framerate = data.shaderOptions.framerate
canvas = data.canvas;
shaderOptions.quality = data.shaderOptions.quality;
shaderOptions.framerate = data.shaderOptions.framerate;
if (shaderOptions.framerate == 65) {
shaderOptions.framerate == Infinity
shaderOptions.framerate == Infinity;
}
screen.devicePixelRatio = data.screen.devicePixelRatio
screen.width = data.screen.width
screen.height = data.screen.height
loadShader()
screen.devicePixelRatio = data.screen.devicePixelRatio;
screen.width = data.screen.width;
screen.height = data.screen.height;
loadShader();
} else if (data.command == "killShader") {
console.log("killShader")
self.clearInterval(interval)
self.clearTimeout(timeout)
}
else if (data.command == "resumeShader") {
self.clearInterval(interval)
self.clearTimeout(timeout)
shaderOptions.framerate = data.shaderOptions.framerate
console.log("killShader");
self.clearInterval(interval);
self.clearTimeout(timeout);
} else if (data.command == "resumeShader") {
self.clearInterval(interval);
self.clearTimeout(timeout);
shaderOptions.framerate = data.shaderOptions.framerate;
if (shaderOptions.framerate == 65) {
shaderOptions.framerate == Infinity
shaderOptions.framerate == Infinity;
}
timeout = self.setTimeout(() => {
requestAnimationFrame(render)
}, 1000 / shaderOptions.framerate)
requestAnimationFrame(render);
}, 1000 / shaderOptions.framerate);
// interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate)
} else if (data.command == "recolorShader") {
_colorStart[0] = data.colorStart[0]
_colorStart[1] = data.colorStart[1]
_colorStart[2] = data.colorStart[2]
_colorEnd[0] = data.colorEnd[0]
_colorEnd[1] = data.colorEnd[1]
_colorEnd[2] = data.colorEnd[2]
_colorStart[0] = data.colorStart[0];
_colorStart[1] = data.colorStart[1];
_colorStart[2] = data.colorStart[2];
_colorEnd[0] = data.colorEnd[0];
_colorEnd[1] = data.colorEnd[1];
_colorEnd[2] = data.colorEnd[2];
} else if (data.command == "resizeShader") {
shaderOptions.quality = data.shaderOptions.quality
screen.devicePixelRatio = data.screen.devicePixelRatio
screen.width = data.screen.width
screen.height = data.screen.height
must_resize = true
shaderOptions.quality = data.shaderOptions.quality;
screen.devicePixelRatio = data.screen.devicePixelRatio;
screen.width = data.screen.width;
screen.height = data.screen.height;
must_resize = true;
}
})
});
async function loadShader() {
if (!canvas) { return }
if (!canvas) {
return;
}
// let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + shaderOptions.quality);
const temp = await main(canvas);
if (temp) {
render = temp
render = temp;
}
postMessage("ready");
}
+26 -16
View File
@@ -3,7 +3,9 @@ const settingsStore = useSettingsStore()
import gsap from 'gsap';
import ShaderWorker from '~/assets/workers/ShaderWorker?worker'
import goStore from '~/netcode';
import type { Game } from '~/netcode/interfaces';
const emit = defineEmits(["ready"])
const worker = new ShaderWorker()
const canvas = ref<HTMLCanvasElement | null>(null)
@@ -27,7 +29,11 @@ const _colorEnd = reactive(props.colorEnd)
function initShader() {
if (!canvas.value) { return }
return new Promise<void>((resolve, reject) => {
if (!canvas.value) {
reject("canvas is null");
return
}
const offscreen = canvas.value?.transferControlToOffscreen()
worker.postMessage({
command: "initShader",
@@ -39,6 +45,13 @@ function initShader() {
killShader()
}
resizeShader()
worker.onmessage = (e) => {
if (e.data == "ready") {
resolve()
}
}
})
}
function recolorShader() {
@@ -83,15 +96,15 @@ watch(() => [settingsStore.shaderQuality], () => {
resizeShader()
})
watch(() => [goStore.current_game?.current_turn], () => {
if (goStore.current_game?.current_turn != goStore.self?.team) {
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .2, 2: .3 })
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 })
} else {
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .3, 2: 0 })
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 })
}
}, { immediate: true })
// watch(() => [props.game.currentTurn], () => {
// if (props.game.teams.find() != goStore.self?.team) {
// gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .2, 2: .3 })
// gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 })
// } else {
// gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .3, 2: 0 })
// gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 })
// }
// }, { immediate: true })
watch([_colorEnd, _colorStart], () => {
@@ -99,18 +112,16 @@ watch([_colorEnd, _colorStart], () => {
})
onMounted(async () => {
nextTick(() => {
recolorShader()
initShader()
await initShader()
window.addEventListener("resize", resizeShader, true)
})
emit("ready")
})
onUnmounted(() => {
window.removeEventListener("resize", resizeShader)
killShader()
})
</script>
<template>
@@ -124,7 +135,6 @@ onUnmounted(() => {
position: absolute;
height: 100%;
width: 100%;
image-rendering: smooth;
}
#background.pixelated {
+1 -1
View File
@@ -9,13 +9,13 @@ export interface Props {
tilted?: boolean
}
const props = withDefaults(defineProps<Props>(), {
wrapped: () => false,
tilted: () => true,
locked: () => true
})
const emit = defineEmits(["click"])
const settingsStore = useSettingsStore()
+34 -8
View File
@@ -1,24 +1,50 @@
<script setup lang="ts">
import type { Card } from '~/netcode/Card';
import { containerEvents } from '~/netcode/events';
import type { Container } from '~/netcode/interfaces';
import type { CardParser } from '~/netcode/interfaces/cards';
export interface Props {
stack: (Card | null)[]
container: Container
}
const props = withDefaults(defineProps<Props>(), {
stack: () => [],
})
const props = defineProps<Props>()
function populateContainer() {
}
function shuffleContainer() {
}
function hideCards(cards: CardParser[]) {
}
function showCards(cards: CardParser[]) {
}
function getCardsPosition(cards: string) {
}
containerEvents.set(props.container._id, { populateContainer, shuffleContainer, getCardsPosition, hideCards, showCards })
onUnmounted(() => {
containerEvents.delete(props.container._id)
})
</script>
<template>
<div class="cards_container">
<template v-if="props.stack.length > 0" v-for="c in props.stack">
<CardPreview :card_id="c?.card_id" :wrapped="true">
<template v-if="props.container.cards.length > 0" v-for="c in props.container.cards">
<CardPreview :card_id="c?.cardId" :wrapped="true">
<slot></slot>
</CardPreview>
</template>
<template v-if="props.stack.length == 0">
<template v-else>
<div class="empty">
<img :class="`card_image`" :src="'/cards/background.png'" draggable="false">
</div>
+17
View File
@@ -0,0 +1,17 @@
<script setup lang="ts">
</script>
<template>
<div id="loading_screen">Loading...</div>
</template>
<style scoped>
#loading_screen {
left: 0;
position: absolute;
height: 100%;
width: 100%;
background: white;
}
</style>
+72 -83
View File
@@ -1,92 +1,88 @@
<script setup lang="ts">
import goStore, {
useEffect,
playCard,
playAllCards,
discard,
lockCard,
heal,
damage_champion,
damage_team,
endTurn
} from "~/netcode";
import { CardType, type Card } from "~/netcode/Card";
import type { Player } from "~/netcode/Player";
import { CardEffects, type Effect, EffectType } from "@/netcode/Effect";
import type { Team } from "~/netcode/Team";
import { endTurn } from "~/netcode";
import type { Game, Player } from "~/netcode/interfaces";
import { CardEffects, type CardParser } from "~/netcode/interfaces/cards";
import { useClientSideStore } from "~/stores/turn";
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))
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 isPlayerTurn = computed(() => props.player.team === goStore.current_game?.current_turn);
const isSelf = computed(() => props.player.uuid === goStore.self?.uuid);
const isSelfTurn = computed(() => goStore.self?.team === goStore.current_game?.current_turn)
const isPlayerEnemy = computed(() => goStore.self?.team !== props.player.team)
const warningText = ref("Effets restants!")
const clientStore = useClientSideStore()
const make_discard = (target: Player) =>
clientStore.findUseEffect(CardEffects.MAKE_DISCARD, target);
const sacrifice = (target: Card) => clientStore.findUseEffect(CardEffects.SACRIFICE, target);
const stun = (target: Card) => clientStore.findUseEffect(CardEffects.STUN, target)
// 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: Card) => clientStore.findUseEffect(CardEffects.PREPARE, 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 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 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()
}
// 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 damage_team_all(team: Team) {
for (let i = 0; i < (goStore.self?.turn?.damage_reserve ?? 0); i++) {
damage_team(team)
}
}
// 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 check_for_guard(player: Player) {
// return player.board.some(c => c.guard === true)
// }
</script>
@@ -105,7 +101,7 @@ function check_for_guard(player: Player) {
<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)"
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)">
@@ -122,12 +118,12 @@ function check_for_guard(player: Player) {
:disabled="!clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.MAKE_DISCARD) && e.usable)">
make Discard
</button>
</div>
</div> -->
<br />
<div class="cards_container">
<!-- HAND -->
<template v-if="props.player != goStore.self">
<!-- <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"
@@ -135,14 +131,15 @@ function check_for_guard(player: Player) {
DISCARD
</button>
</CardPreview>
</template>
</template> -->
<!-- BOARD -->
<CardPreview :card_id="c.card_id" v-for="c in props.player.board" @click="lockCard(c)"
:locked="props.player.turn && c.uuid in props.player.turn.cards_locked">
<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 && 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">
@@ -164,7 +161,7 @@ function check_for_guard(player: Player) {
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 -->
// 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)">
@@ -193,7 +190,7 @@ function check_for_guard(player: Player) {
@click.stop="stun(c)" :disabled="(check_for_guard(props.player) && !c.guard)">
STUN
</button>
</template>
</template> -->
</CardPreview>
</div>
</div>
@@ -272,19 +269,15 @@ function check_for_guard(player: Player) {
"player_stats"
"player";
transition: all 1s;
<<<<<<< HEAD
/* margin-bottom: -75px;
padding-bottom: 75px; */
}
.discard_overlay {
grid-template-areas:
"player_stats player_stats"
"stack player";
=======justify-items: center;
/* margin-bottom: -75px;
padding-bottom: 75px; */
>>>>>>>feat/panels-visuals
justify-items: center;
}
#current_player_board {
@@ -379,14 +372,10 @@ function check_for_guard(player: Player) {
}
#player_banner.big {
<<<<<<< HEAD padding: 0;
padding: 0;
left: 50%;
transform: translateX(-50%);
=======
/* padding: 0;
left: 50%;
transform: translateX(-50%); */
>>>>>>>feat/panels-visuals max-width: min-content;
max-width: min-content;
border: 5px solid black;
border-radius: 10px;
filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, .60));
+12
View File
@@ -0,0 +1,12 @@
import type { Container } from "./interfaces";
import type { CardParser } from "./interfaces/cards";
export type ContainerHandler = {
populateContainer: () => void;
shuffleContainer: () => void;
getCardsPosition: (cards: string) => any;
hideCards: (cards: CardParser[]) => void;
showCards: (cards: CardParser[]) => void;
};
export const containerEvents = new Map<string, ContainerHandler>();
+22 -8
View File
@@ -25,28 +25,42 @@ export async function getLobby() {
export async function whoami() {
const config = useRuntimeConfig();
const req = await useFetch<User>(
const { data, pending, error, refresh } = await useFetch<User>(
new URL("/whoami", config.public.endpoint).href,
{
credentials: "include",
}
);
if (
req.error.value?.statusCode &&
[401].includes(req.error.value.statusCode)
) {
if (error.value?.statusCode && [401].includes(error.value.statusCode)) {
// await navigateTo(config.public.discordLoginEndpoint, {
// external: true,
// });
throw new Error("You are not logged in");
}
if (req.error.value) {
if (error.value) {
throw new Error(
"Something went wrong while trying to get user information"
);
}
return req.data;
return data;
}
export function createGame() {}
export async function createGame() {
const config = useRuntimeConfig();
await $fetch<User>(new URL("/games", config.public.endpoint).href, {
method: "POST",
credentials: "include",
});
}
export async function endTurn(game: string) {
const config = useRuntimeConfig();
await $fetch<User>(
new URL(`games/${game}/endTurn`, config.public.endpoint).href,
{
method: "POST",
credentials: "include",
}
);
}
+81 -101
View File
@@ -1,76 +1,95 @@
<script setup lang="ts">
import LoadingScreen from '~/components/LoadingScreen.vue';
import { subscribe } from '~/netcode';
import { type Game } from '~/netcode/interfaces';
import { containerEvents } from '~/netcode/events';
import { type Container, type Game, type Player } from '~/netcode/interfaces';
const config = useRuntimeConfig()
const route = useRoute()
const gameId = route.params.id
const { data: game, pending, error, refresh } = (await useFetch<Game>(
const { data: game, pending, error, refresh } = await useFetch<Game>(
new URL(`/games/${gameId}`, config.public.endpoint).href,
{
credentials: "include",
}
))
// as { data: Ref<Game>, pending: Ref<Boolean>, error: Ref<Error>, refresh: (opts?: unknown | undefined) => Promise<void> }
},
)
if (game.value == null) {
throw new Error()
navigateTo("/lobby");
throw new Error("game not found")
}
const containers = computed<Record<string, Container>>(() => ({
[game.value!.fireGems._id]: game.value!.fireGems,
[game.value!.market._id]: game.value!.market,
[game.value!.marketStack._id]: game.value!.marketStack,
...Object.fromEntries(
game.value!.teams.reduce(
(acc, t) => acc.concat(t.players), [] as Player[]
).map(
p => [
[p.board._id, p.board],
[p.discard._id, p.discard],
[p.hand._id, p.hand],
[p.stack._id, p.stack]
]
).flat(1)
)
}))
const eventSource = subscribe(`/games/${gameId}/subscribe`)
eventSource.addEventListener("message", (message) => {
const data = JSON.parse(message.data)
console.log(data)
if (data.type == 'start') {
throw new Error("not implemented")
} else if (data.type == "populateContainer") {
throw new Error("not implemented")
} else if (data.type == "shuffleContainer") {
containerEvents.get(data.container._id)?.shuffleContainer()
} else if (data.type == "distributeCards") {
containerEvents.get(data.from._id)?.hideCards(data.cards)
containerEvents.get(data.from._id)?.getCardsPosition(data.cards)
const fromContainer = containers.value[data.from as string]
const toContainer = containers.value[data.to as string]
for (const card of data.cards) {
const index = fromContainer.cards.findIndex(c => c._id == card._id)
fromContainer.cards.splice(index, 1)
}
for (const card of data.cards) {
toContainer.cards.push(card)
}
} else if (data.type == "endTurn") {
throw new Error("not implemented")
}
});
const authStore = useAuthStore()
const isSpectator = computed(() => authStore.logged && game.value.teams.every(t => t.players.every(p => p.user.userId !== authStore.selfUser.userId)))
// const current_players = computed<Array<Player>>(() => Array.from(goStore.current_game?.players ?? []).filter(p => p.team === goStore.current_game?.current_turn))
const playerList = computed(() => 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)
// watch(
// () => goStore.current_game?.current_turn?.uuid,
// () => {
// if (goStore.current_game?.current_turn?.uuid == goStore.self?.team?.uuid) {
// playSound('/sounds/ding.mp3')
// }
// })
// watch(
// () => goStore.current_game?.losers,
// () => {
// if (goStore.self?.uuid && goStore.current_game?.losers && goStore.current_game.losers.includes(goStore.self.uuid)) {
// showLoserScreen()
// }
// })
// watch(
// () => goStore.current_game?.ended,
// () => {
// if (goStore.current_game?.ended && goStore.self?.uuid && goStore.current_game?.losers && !goStore.current_game.losers.includes(goStore.self.uuid)) {
// showWinnerScreen()
// }
// })
// TODO : current turn sound
// watch(
// () => goStore.current_game?.players,
// (newList, oldList) => {
// if (!oldList || !newList) { return }
// if (goStore.current_game?.started) { return }
// if (newList?.length > oldList.length) {
// playSound('/sounds/teleport.mp3')
// }
// TODO : winner screen
// })
// TODO : join sound
const showResults = ref(false)
const showOptions = ref(false)
const isWinner = ref(false)
const loaded = ref(false)
function showLoserScreen() {
// playSound('/sounds/ding.mp3')
@@ -85,15 +104,16 @@ function showWinnerScreen() {
}
const discard_unwrapped = ref(false)
// const focusedPlayer = ref<Player>((goStore.current_game?.players.find(p => p.team == goStore.current_game?.current_turn) ?? goStore.current_game?.players[0])!)
const focusedPlayer = ref<Player>(selfPlayer.value ?? playerList.value[0])
// const clientStore = useClientSideStore()
// 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 sacrifice = (target: Card) => clientStore.findUseEffect(CardEffects.SACRIFICE, target);
// function focusPlayer(p: Player) {
// focusedPlayer.value = p
// }
function focusPlayer(p: Player) {
focusedPlayer.value = p
}
// watch<Team>(() => goStore.current_game!.current_turn, (new_team, old_team) => {
// if (focusedPlayer.value.team != old_team) { return }
@@ -103,85 +123,45 @@ const discard_unwrapped = ref(false)
// }
// focusPlayer(first_player)
// })
onUnmounted(() => {
eventSource.close()
})
</script>
<template>
<div @contextmenu.prevent="false" id="game_board">
<AnimatedBackground></AnimatedBackground>
<div id="game_status">
<template v-if="authStore.logged && game?.started">
<input @click.prevent="setReady(($event.target as HTMLInputElement).checked)" type="checkbox" id="ready"
:checked="goStore.self?.ready" />
<label for="ready">Ready</label>
</template>
</div>
<AnimatedBackground @ready="loaded = true"></AnimatedBackground>
<div id="stack_discard">
<div id="stack">
<template v-for="p in current_players">
<CardsGrouped :stack="p.stack_pile">
<CardsGrouped :container="focusedPlayer.stack">
</CardsGrouped>
</template>
</div>
<div id="discard" :class="{ invisible: discard_unwrapped }" @click="() => discard_unwrapped = true">
<template v-for="p in current_players">
<CardsGrouped :stack="p.discard_pile" :visible="!discard_unwrapped">
<CardsGrouped :container="focusedPlayer.discard">
</CardsGrouped>
</template>
<template v-for="p in current_players">
<div class="discard_overlay" v-if="discard_unwrapped">
<CardPreview :tilted="false" v-if="discard_unwrapped" :card_id="c.card_id"
v-for="c in p.discard_pile">
<template
v-if="goStore.self?.turn && c && p == goStore.self && game.current_turn == goStore.self.team">
<button
v-if="clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.SACRIFICE) && e.usable)"
@click.stop="sacrifice(c)">
SACRIFICE
</button>
<button
v-if="c.card_type == CardType.CHAMPION && clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.RESTACK_DISCARDED_CHAMPION) && e.usable)"
@click.stop="restack_discarded_champion(c)">
RESTACK_DISCARDED_CHAMPION
</button>
<button
v-if="clientStore.activatedClientside.find((e: Effect) => (e.effect == CardEffects.RESTACK_DISCARDED_CARD) && e.usable)"
@click.stop="restack_discarded_card(c)">
RESTACK_DISCARDED_CARD
</button>
</template>
</CardPreview>
</div>
<div :id="`discard_unwrapped_bg`" :class="{ unwrapped: discard_unwrapped }"
@click.stop="() => { discard_unwrapped = false; console.log(discard_unwrapped); }"></div>
</template>
</div>
</div>
<MarketCards></MarketCards>
<!-- <MarketCards></MarketCards> -->
<div id="player_list_container">
<div id="player_list">
<PlayerListElement v-for="p in game.players" :player="p" @click="focusPlayer(p)"
:focused="focusedPlayer">
</PlayerListElement>
<!-- <PlayerListElement v-for="p in playerList" :player="p" @click="focusPlayer(p)" :focused="focusedPlayer">
</PlayerListElement> -->
</div>
</div>
<div id="current_player">
<!-- <AnimatedBackground :color-start="goStore.self == p ? [.0, .3, .3] : undefined"
:color-end="goStore.self == p ? [.0, 1, 1] : undefined" /> -->
<PlayerSlot v-if="focusedPlayer" :player="focusedPlayer"></PlayerSlot>
<PlayerSlot v-if="focusedPlayer" :player="focusedPlayer" :game="game!"></PlayerSlot>
</div>
<ResultDialog :isVisible="showResults" :won="isWinner" :players="game.players" @close="showResults = false">
<ResultDialog :isVisible="showResults" :won="isWinner" :players="playerList" @close="showResults = false">
</ResultDialog>
<GlobalOverlay></GlobalOverlay>
<SelfView></SelfView>
<!-- <GlobalOverlay></GlobalOverlay>
<SelfView></SelfView> -->
<LoadingScreen v-if="!loaded"></LoadingScreen>
</div>
</template>
+7 -7
View File
@@ -2,24 +2,24 @@ import { defineStore } from "pinia";
enum CardStyles {
HERON = "heron",
ORIGINAL = "original"
ORIGINAL = "original",
}
export const useSettingsStore = defineStore("settingstore", {
state: () => ({
cardStyle: CardStyles.HERON,
shaderQuality: 3,
shaderFramerate: 0,
shaderPixelated: true
shaderFramerate: 10,
shaderPixelated: true,
}),
actions: {
switchCardStyles() {
if (this.cardStyle === CardStyles.HERON) {
this.cardStyle = CardStyles.ORIGINAL
this.cardStyle = CardStyles.ORIGINAL;
} else {
this.cardStyle = CardStyles.HERON
}
this.cardStyle = CardStyles.HERON;
}
},
persist: true
},
persist: true,
});
+50 -50
View File
@@ -1,55 +1,55 @@
import { defineStore } from "pinia";
import goStore, { useEffect } from "~/netcode";
import type { Card } from "~/netcode/Card";
import { CardEffects, type Effect } from "~/netcode/Effect";
import type { Player } from "~/netcode/Player";
// import goStore, { useEffect } from "~/netcode";
// import type { Card } from "~/netcode/Card";
// import { CardEffects, type Effect } from "~/netcode/Effect";
// import type { Player } from "~/netcode/Player";
watch(
() => goStore.current_game?.current_turn?.uuid,
() => {
useClientSideStore().activatedClientside = []
})
// watch(
// () => goStore.current_game?.current_turn?.uuid,
// () => {
// useClientSideStore().activatedClientside = []
// })
export const useClientSideStore = defineStore("clientside", {
state: () => ({
activatedClientside: [] as Effect[],
effectsHandled: new Set([
CardEffects.MAKE_DISCARD,
CardEffects.PLAY_NEXT_CARD_BOUGHT,
CardEffects.RESTACK_DISCARDED_CARD,
CardEffects.RESTACK_DISCARDED_CHAMPION,
CardEffects.SACRIFICE,
CardEffects.STACK_NEXT_ACTION_BOUGHT,
CardEffects.STACK_NEXT_CARD_BOUGHT,
CardEffects.STUN,
CardEffects.PREPARE
]),
}),
getters: {
maskedServerEffects(state) {
return goStore.self?.turn?.effects_availables.filter(e => !state.activatedClientside.includes(e));
},
},
actions: {
activateEffect(effect: Effect) {
const amount =
goStore.self?.turn?.effects_availables.filter((e) => e == effect)
.length ?? 0;
for (let i = 0; i < amount; i++) {
this.activatedClientside.push(effect);
}
},
useEffect(effect: Effect, target: Player | Card) {
const index = this.activatedClientside.indexOf(effect);
if (index > -1) {
this.activatedClientside.splice(index, 1);
useEffect(effect, target);
}
},
findUseEffect(cardEffect: CardEffects, target: Player | Card) {
const effect = this.activatedClientside.find(e => (e.effect == cardEffect) && e.usable)
if (!effect) { return }
this.useEffect(effect, target)
}
},
// state: () => ({
// activatedClientside: [] as Effect[],
// effectsHandled: new Set([
// CardEffects.MAKE_DISCARD,
// CardEffects.PLAY_NEXT_CARD_BOUGHT,
// CardEffects.RESTACK_DISCARDED_CARD,
// CardEffects.RESTACK_DISCARDED_CHAMPION,
// CardEffects.SACRIFICE,
// CardEffects.STACK_NEXT_ACTION_BOUGHT,
// CardEffects.STACK_NEXT_CARD_BOUGHT,
// CardEffects.STUN,
// CardEffects.PREPARE
// ]),
// }),
// getters: {
// maskedServerEffects(state) {
// return goStore.self?.turn?.effects_availables.filter(e => !state.activatedClientside.includes(e));
// },
// },
// actions: {
// activateEffect(effect: Effect) {
// const amount =
// goStore.self?.turn?.effects_availables.filter((e) => e == effect)
// .length ?? 0;
// for (let i = 0; i < amount; i++) {
// this.activatedClientside.push(effect);
// }
// },
// useEffect(effect: Effect, target: Player | Card) {
// const index = this.activatedClientside.indexOf(effect);
// if (index > -1) {
// this.activatedClientside.splice(index, 1);
// useEffect(effect, target);
// }
// },
// findUseEffect(cardEffect: CardEffects, target: Player | Card) {
// const effect = this.activatedClientside.find(e => (e.effect == cardEffect) && e.usable)
// if (!effect) { return }
// this.useEffect(effect, target)
// }
// },
});