Merge pull request 'feat: routing first draft, sound for erika, game rules rewrite' (#20) from feat/route-sound-rules into main
release-tag / release-image (push) Successful in 1m0s

Reviewed-on: legonzaur/heron-realms-front#20
This commit was merged in pull request #20.
This commit is contained in:
2024-05-12 12:53:27 +00:00
11 changed files with 391 additions and 131 deletions
+4 -1
View File
@@ -1,6 +1,9 @@
name: release-tag
on: push
on:
push:
branches:
- main
jobs:
release-image:
+4 -16
View File
@@ -28,16 +28,9 @@ onMounted(async () => {
</script>
<template>
<div id="game_status">
<template v-if="goStore.self && !goStore.current_game?.started">
<input @change="setReady(($event.target as HTMLInputElement).checked)" type="checkbox" id="ready" />
<label for="ready">Ready</label>
</template>
<div class="root_dom_div">
<NuxtPage />
</div>
<LoginView v-if="goStore.context === 'login'"></LoginView>
<GameBoard v-if="goStore.context != 'lobby' && goStore.context != 'login'"></GameBoard>
<LobbyView v-if="goStore.context == 'lobby'"></LobbyView>
</template>
<style>
@@ -55,12 +48,7 @@ body {
user-select: none;
}
#game_status {
position: fixed;
z-index: 10;
top: 0;
left: 50%;
transform: translateX(-50%);
background-color: rgba(255, 255, 255, .75);
.root_dom_div {
height: 100%;
}
</style>
+62 -39
View File
@@ -1,16 +1,18 @@
<script setup lang="ts">
import goStore, { netcode_create_game, join_game } from '~/netcode';
import { playSound } from '~/helpers/audioHelpers';
import { ref } from 'vue';
const erika_alive = ref(true);
const router = useRouter();
function killErika() {
playSound('/sounds/splat1.mp3')
erika_alive.value = false;
}
</script>
<template>
<div class="background">
<div class="title">
@@ -28,16 +30,20 @@ function killErika() {
</div>
<div class="overlay" v-if="erika_alive">
<img class="overlay-image" src="https://static.wikia.nocookie.net/umineko/images/9/9d/PC_Erika.Casual.Sprite_36.png" alt="pétasse" />
<div class="speech-bubble">
<div class="erika-zone">
<img @click="router.push({ path: 'rules'})" class="overlay-image" src="https://static.wikia.nocookie.net/umineko/images/9/9d/PC_Erika.Casual.Sprite_36.png" alt="pétasse" />
<div class="overlay-button">
<button class="kill-erika-button" @click.stop="killErika()">Tuer Erika</button>
</div>
</div>
<div class="speech-bubble" @click.prevent.self>
<p>Ohoho!<br>Je suis Erika Furudo, la détective la plus forte du monde !</p>
<p>Je vais vous montrer comment jouer à Héron Realms !</p>
<p>Cliquez moi-dessus pour afficher les <span style="color: red">règles</span>.</p>
<p>Ou sinon, commencez direct en créant une partie ou rejoingnant une partie existante.</p>
</div>
<div class="overlay-button">
<button class="kill-erika-button" @click="killErika()">Tuer Erika</button>
</div>
</div>
@@ -126,13 +132,57 @@ function killErika() {
}
}
.overlay-image {
.overlay {
height: 100%;
width: 100%;
position: absolute;
pointer-events: none;
}
.erika-zone {
position: absolute;
top: 60%;
right: 20px;
z-index: 1;
display: flex;
justify-content: right;
z-index: 2;
height: 40%;
width: 100%;
object-fit: cover;
.overlay-image {
position: absolute;
height: 100%;
cursor: pointer;
pointer-events: all;
}
.overlay-button {
position: absolute;
z-index: 1;
width: 300px;
box-sizing: border-box;
pointer-events: none;
.kill-erika-button {
display: none;
pointer-events: all;
background-color: #f44336;
border: none;
color: white;
text-align: center;
text-decoration: none;
font-family: 'Roboto', sans-serif;
font-size: 16px;
margin: auto;
cursor: pointer;
border-radius: 12px;
padding: 10px 24px;
height: 50px;
transition-duration: 0.4s;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}
}
}
.speech-bubble {
@@ -152,38 +202,11 @@ function killErika() {
line-height: 1.5;
}
.overlay-button {
position: absolute;
top: 49%;
right: 20px;
z-index: 1;
width: 300px;
height: 300px;
padding-top: 50px;
.kill-erika-button {
display: none;
background-color: #f44336;
border: none;
color: white;
text-align: center;
text-decoration: none;
font-family: 'Roboto', sans-serif;
font-size: 16px;
margin: auto;
cursor: pointer;
border-radius: 12px;
padding: 10px 24px;
height: 50px;
transition-duration: 0.4s;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}
}
.overlay-button:hover {
.erika-zone:hover {
.overlay-button {
.kill-erika-button {
display: inline-block;
}
}
}
</style>
+222 -20
View File
@@ -2,37 +2,77 @@
import type { Card } from '~/netcode/Card';
const router = useRouter();
</script>
<template>
<div>
<div class="background">
<div class="overlay-2">
<div class="erika-zone-2">
<img @click="router.push({ path: '/'})" class="overlay-image" src="https://static.wikia.nocookie.net/umineko/images/9/9d/PC_Erika.Casual.Sprite_36.png" alt="pétasse" />
</div>
</div>
<div class="text-content">
<h1>Règles de Héron Realms</h1>
<section>
<p>Héron Realms, c'est un jeu de ~~héron building~~ deck-building. Dans celui-ci, tout le monde part du même deck de base.</p>
<p></p>
<p>Héron Realms, c'est un jeu de <span style="text-decoration:line-through;">héron building</span> deck-building.</p>
<p>Tout le monde part d'un même deck de base.</p>
<p>Le but du jeu est de l'enrichir pour obtenir de quoi gagner.</p>
<p>Et comme la violence est une solution universelle, ça se traduit en battre ses adversaires.</p>
<p>&#8203;</p>
<p>Et quoi de mieux pour faire ça que des cartes colorées?</p>
<div class="card-row">
<CardPreview :data="{card_id: 6}"></CardPreview>
<CardPreview :data="{card_id: 19}"></CardPreview>
<CardPreview :data="{card_id: 36}"></CardPreview>
<CardPreview :data="{card_id: 46}"></CardPreview>
</div>
<p>&#8203;</p>
<hr>
<p>&#8203;</p>
<h2>Objectif</h2>
<p>L'unique objectif du jeu est d'être le dernier en lice. Pour y arriver, il faut descendre les Points de Vie de vos adversaires à 0 en partant de 50.</p>
<p>
Le moyen principal d'y arriver, c'est de faire des <span style="color: red">Dégats</span>. Tout le reste des mécaniques du jeu sont là pour vous faciliter la tâche,
Le moyen principal d'y arriver, c'est de faire des <span style="color: red">Dégats</span> <img src="/images/damage.png" class="icon_img" draggable="false" />. Tout le reste des mécaniques du jeu sont là pour vous faciliter la tâche,
ou pour mettre des bâtons dans les roues de vos adversaires.
</p>
<p></p>
<p>&#8203;</p>
<hr>
<h2>Deckbuilding</h2>
<p>Comme c'est un jeu de "deckbuilding", on va expliquer ce qu'est le "deck" et ce qui nous permet de le "build".</p>
<div class="card-row">
<CardPreview></CardPreview>
</div>
<p>&#8203;</p>
<p>
Le deck d'un joueur est-ce qui constitue sa pioche, qui lui permettra ensuite de constituer sa main
(la main est préparée à la fin du tour et permet de jouer le tour d'après).
</p>
<p>
Quand la pioche est vide, elle est recyclée à partir de sa défausse.
La défausse est ce qui contient les cartes déjà jouées mais aussi celles qui sont achetées, et qui sera mélangée pour former la nouvelle pioche.
</p>
<p>
Tout le monde commence avec 10 cartes:
La défausse est ce qui contient les cartes déjà jouées. Mais aussi celles qui sont achetées! Elle finit par être mélangée pour former la nouvelle pioche.
</p>
<p>&#8203;</p>
<hr>
<h2>Deck de base</h2>
<p>
Tout le monde commence avec 10 cartes divisées en 4 types:
</p>
<ul>
<li>7 or + 1 carte qui vaut 2 d'or;</li>
<li>1 carte à 1 de dégats et 1 carte à 2 de dégats.</li>
<li>7x Or (1)</li>
<li>1x Or (2)</li>
<li>1x Dégats (1)</li>
<li>1x Dégats (2)</li>
</ul>
<div class="card-row">
<CardPreview :data="{card_id: 56}"></CardPreview>
<CardPreview :data="{card_id: 59}"></CardPreview>
<CardPreview :data="{card_id: 58}"></CardPreview>
<CardPreview :data="{card_id: 57}"></CardPreview>
</div>
<p>
La pioche est formée au hasard: on peut donc avoir une main de départ avec 5 or et 2 dégats, ou 3 or et 4 dégats, ou 4 or et 3 dégats, etc.
</p>
@@ -41,41 +81,203 @@ import type { Card } from '~/netcode/Card';
l'ordre qu'on souhaite. 1 de dégats et 2 de dégats me donnent 3 ressources de dégats que je split comme je veux ensuite. Pareil pour l'or et pour le soin, qui sont les 3 ressources
principales du jeu.
</p>
<p></p>
<p>&#8203;</p>
<hr>
<h2>Types de cartes</h2>
<p>
Les cartes sont de 3 catégories: objets, actions et champions.
Les cartes sont divisées en 3 catégories: objets, actions et champions.
</p>
<div class="card-row">
<CardPreview :data="{card_id: 56}"></CardPreview>
<CardPreview :data="{card_id: 52}"></CardPreview>
<CardPreview :data="{card_id: 43}"></CardPreview>
</div>
<p>&#8203;</p>
<h3>Objets & actions</h3>
<div class="card-row">
<CardPreview :data="{card_id: 56}"></CardPreview>
<CardPreview :data="{card_id: 52}"></CardPreview>
</div>
<p>
Par simplicité, disons que les objets et les actions sont pareil: on les joue, ils font un effet disponible durant le tour actuel, et ils sont défaussés.
</p>
<h3>Champions (& gardes)</h3>
<div class="card-row">
<CardPreview :data="{card_id: 43}"></CardPreview>
</div>
<p>
Les champions sont un peu plus complexes: ils restent sur le terrain, et on peut les utiliser à chaque tour (en les mobilisant). Ils ont une défense et il ne meurent
que si ils sont descendus à 0 durant un tour donné. Je ne peux pas faire un peu de dégats pendant un tour, et un peu le tour d'après pour les tuer.
Les champions sont un peu plus complexes: ils restent sur le terrain, et on peut les utiliser à chaque tour (en les mobilisant).
</p>
<p>Ils ont une défense <img src="/images/champion-shield.png" style="height: 30px" draggable="false" /> indiquée en bas à droite et ils ne sont assommés (défaussés) que si on leur fait des dégats = défense.</p>
<p>Ces dégats doivent être faits d'un coup. Je ne peux pas faire un peu de dégats pendant un tour, et un peu le tour d'après pour les tuer.</p>
<div class="card-row">
<CardPreview :data="{card_id: 49}"></CardPreview>
</div>
<p>
Par ailleurs, certains champions sont des Gardes <img src="/images/champion-shield-guard.png" style="height: 30px" draggable="false" /> (ils ont un bouclier noir), ce qui veut dire qu'on est obligés de les tuer pour faire des dégats à des champions non-gardes ou au joueur.
</p>
<p>
Par ailleurs, certains champions sont des Gardes, ce qui veut dire qu'on est obligés de les tuer pour faire des dégats à des champions non-gardes ou au joueur.
(J'en profite pour préciser qu'on peut en effet, sans garde, choisir d'attaquer le joueur directement ou ses champions.)
Incidentellement, ça veut dire qu'avec seulement 2 de dégats, je ne peux rien faire à un joueur avec un garde à 3 de défense.
</p>
<p></p>
<p></p>
<p>&#8203;</p>
<hr>
<h2>Building</h2>
<p>
Passons au "building": on peut acheter des cartes pour les ajouter à notre défausse qui, si vous avez suivi, constituera la prochaine pioche.
Avec le jeu de base, <span style="color: yellow">l'or</span> sert exclusivement à accomplir ce but: acheter des cartes.
Avec le jeu de base, <span style="color: darkgoldenrod">l'or</span> <img src="/images/gold.png" class="icon_img" draggable="false" /> sert exclusivement à accomplir ce but: acheter des cartes.
</p>
<div class="card-row">
<CardPreview :data="{card_id: 6}"></CardPreview>
<CardPreview :data="{card_id: 19}"></CardPreview>
<CardPreview :data="{card_id: 36}"></CardPreview>
<CardPreview :data="{card_id: 46}"></CardPreview>
</div>
<p>
Un marché est mis à disposition de tous les joueurs, mettant à disposition 5 cartes (pas ici parce que j'ai pas la place).
</p>
<p>
Un marché est mis à disposition de tous les joueurs, mettant à disposition les cartes du marché. Ces cartes ont chacune un coût indiqué en haut à droite.
Ces cartes ont chacune un coût indiqué en haut à droite.
Il faut au moins autant d'or que ce qui est indiqué pour acheter une carte. Une carte achetée laisse la place à une nouvelle carte directement, qu'on peut
décider d'acheter immédiatement.
décider d'acheter juste après (tant qu'on a l'argent).
</p>
<p>
Il ne nous reste plus qu'à comprendre ce que font les cartes du marché, puisqu'elles sont plus complexes (et bien plus fortes) que votre deck de base.
Prenons des exemples!
</p>
<hr>
<h2>Couleurs</h2>
<p>
La première chose qui ne vous échappera pas, c'est qu'on a 4 couleurs différentes pour les cartes.
</p>
<p>&#8203;</p>
<div class="card-row">
<CardPreview :data="{card_id: 13}"></CardPreview>
<CardPreview :data="{card_id: 3}"></CardPreview>
<CardPreview :data="{card_id: 1}"></CardPreview>
</div>
<p>&#8203;</p>
<p>Les cartes <span style="color: darkgoldenrod">jaunes</span> sont les cartes Empire.</p>
<p>Elles font beaucoup de soin, et activent souvent des effets de pioche.</p>
<p>&#8203;</p>
<div class="card-row">
<CardPreview :data="{card_id: 39}"></CardPreview>
<CardPreview :data="{card_id: 34}"></CardPreview>
<CardPreview :data="{card_id: 40}"></CardPreview>
</div>
<p>&#8203;</p>
<p>Les cartes <span style="color: red">rouges</span> sont les cartes Necros.</p>
<p>Elles sont les seules à pouvoir sacrifier des cartes pour les sortir de son deck de façon permanente.</p>
<p>&#8203;</p>
<div class="card-row">
<CardPreview :data="{card_id: 52}"></CardPreview>
<CardPreview :data="{card_id: 46}"></CardPreview>
<CardPreview :data="{card_id: 47}"></CardPreview>
</div>
<p>&#8203;</p>
<p>Les cartes <span style="color: darkgreen">vertes</span> sont les cartes Sauvages.</p>
<p>Elles font beaucoup de dégats, et forcent vos adversaires à jouer moins de cartes.</p>
<p>&#8203;</p>
<div class="card-row">
<CardPreview :data="{card_id: 24}"></CardPreview>
<CardPreview :data="{card_id: 25}"></CardPreview>
<CardPreview :data="{card_id: 19}"></CardPreview>
</div>
<p>&#8203;</p>
<p>Les cartes <span style="color: darkblue">bleues</span> sont les cartes Guilde.</p>
<p>Elles donnent beaucoup d'or, assomment les champions adverses et manipulent votre pioche.</p>
<p>&#8203;</p>
<hr>
<h2>Déclencheurs</h2>
<p>Les effets de cartes peuvent se déclencher pour 3 raisons différentes.</p>
<div class="card-row">
<CardPreview :data="{card_id: 14}"></CardPreview>
</div>
<p>Sur cette carte, on peut voir les trois en même temps.</p>
<p>La 1ère ligne s'active tout le temps.</p>
<p>La 2ème ligne est une capacité Allié: il faut une autre carte de la même couleur en jeu pour pouvoir en profiter.</p>
<p>La 3ème ligne est un Effet Poubelle (différent du sacrifice des cartes rouges): vous pouvez retirer cette carte de votre jeu pour bénéficier, en plus des autres effets, de l'effet marqué. Mais la carte sera défaussée à tout jamais.</p>
<p>&#8203;</p>
<hr>
<h2>Effets</h2>
<p>Une variété d'effets peuvent être inclus dans les cartes.</p>
<p>Il y en a plein et les expliquer ici serait relou, alors je vous laisse poser la question en vocal si vous n'en comprenez pas un.</p>
<p>&#8203;</p>
<hr>
<h2>Activer les cartes dans la version en ligne</h2>
<p>Il suffit de cliquer sur la carte que vous voulez activer dans votre main pour la "LOCK".</p>
<p>Une carte "LOCKED" est considérée en jeu, & peut activer des effets Allié pour d'autres cartes, etc.</p>
<p>Si vous avez des choix à faire sur la carte, un menu apparaîtra pour vous laisser choisir.</p>
<p>Pour faire des dommages, il y aura un bouton "Damage" sur vos ennemis. Pareil sur leurs champions.</p>
<p>Quand vous avez un effet "Sacrifice", vous pouvez fouiller votre défausse pour choisir la carte à sacrifier. Vous pouvez également sacrifier une carte de la main qui n'est pas encore locked!</p>
</section>
</div>
</div>
</template>
<style>
.background {
background-color: #f0f0f0;
background-image: url('images/bg-darkgrass.png');
background-size: auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100vw;;
}
.text-content {
background-color: rgba(255, 255, 255, .75);
padding: 1em;
border-radius: 1em;
max-width: 800px;
margin-top: 2em;
text-align: left;
h1 {
text-align: center;
font-size: 2em;
margin-bottom: 1.5em;
}
p {
margin: 1em 0;
font-size: 1.2em;
}
}
.card-row {
display: flex;
justify-content: center;
gap: 1em;
}
.overlay-2 {
height: 00px;
width: 100%;
position: sticky;
z-index: 2;
top: 0;
pointer-events: none;
.erika-zone-2 {
position: absolute;
top: 300px;
display: flex;
justify-content: right;
z-index: 2;
height: 300px;
width: 100%;
object-fit: cover;
.overlay-image {
position: sticky;
height: 100%;
cursor: pointer;
pointer-events: all;
}
}
}
</style>
+4
View File
@@ -0,0 +1,4 @@
export function playSound(soundUrl: string): void {
const audio = new Audio(soundUrl);
audio.play();
}
+1 -1
View File
@@ -25,7 +25,7 @@ export class GameObjectRegister {
readonly turns: Map<string, Turn>;
readonly games: Map<string, Game>
context: "lobby" | "login" | "pregame" | "game" = "login";
context: "lobby" | "login" | "pregame" | "game" | "rules" = "login";
private readonly objects: Map<string, GameObjectNetcode>;
current_game: Ref<Game | null>
lobby: Ref<Lobby | null>;
+37
View File
@@ -0,0 +1,37 @@
<script setup lang="ts">
import goStore, { setReady } from "@/netcode";
</script>
<template>
<div class="root_page_div">
<div id="game_status">
<template v-if="goStore.self && !goStore.current_game?.started">
<input @change="setReady(($event.target as HTMLInputElement).checked)" type="checkbox" id="ready" />
<label for="ready">Ready</label>
</template>
</div>
<LoginView v-if="goStore.context === 'login'"></LoginView>
<GameBoard v-if="goStore.context != 'lobby' && goStore.context != 'login'"></GameBoard>
<LobbyView v-if="goStore.context == 'lobby'"></LobbyView>
<RulesDisplay v-if="goStore.context == 'rules'"></RulesDisplay>
</div>
</template>
<style>
.root_page_div {
height: 100%;
}
#game_status {
position: fixed;
z-index: 10;
top: 0;
left: 50%;
transform: translateX(-50%);
background-color: rgba(255, 255, 255, .75);
}
</style>
+3
View File
@@ -0,0 +1,3 @@
<template>
<RulesDisplay></RulesDisplay>
</template>
Binary file not shown.
Binary file not shown.
Binary file not shown.