feat: cartes Événement (pioche partagée de 18 cartes, 6 effets, synergies, ciblage)

- Nouveau server/src/game/events.js : 6 événements (Récolte exceptionnelle,
  Épidémie, Levée des troupes, Accord royal, Complot découvert, Grande fête
  au château), deck PARTAGÉ de 18 cartes (3 exemplaires de chacun) mélangé
  au démarrage de la partie.
- Room.js : buyEventDraw() (coût fixe 1 point dans chacune des 4 jauges,
  effet immédiat contrairement aux objets/bâtiments — pas de conflit de
  stock possible), playEventCard() (choix de jouer une carte de sa main,
  max 1 par tour, avec ciblage optionnel), _resolvePendingEventPlays()
  (résolution de TOUS les événements du tour AVANT le calcul des gains de
  cartes personnage, défausse systématique, protection "Complot découvert"
  pour soi-même uniquement). Les 6 effets incluent leurs synergies avec les
  objets/bâtiments existants (Charrue, Trousse+Église pour Épidémie,
  Trébuchet, Sceau Royal, Château Royal → pioche bonus).
- IMPORTANT : le ciblage de joueur (Accord royal, variante Trousse
  d'Épidémie) transite par teamIndex (identifiant public) et non socketId
  (jamais exposé au client via getPlayersInfo/getStateFor) — le serveur
  résout teamIndex -> joueur réel au moment de la résolution.
- Nouvel event socket 'buy-event-draw' (effet immédiat) et 'play-event'
  (résolution différée), eventResults inclus dans round-result.
- Client : events.js (miroir), EventCardObject.js (carte épurée façon
  CardObject, badge rouge "!" pour les événements à effet négatif).
  BootScene génère les textures plein cadre (méthode _generateClippedTexture
  généralisée, réutilisée avec les personnages).
- GameScene : bouton de pioche à droite du bouton de confirmation (état
  grisé selon jauges/main pleine/pioche déjà tentée), seconde main de cartes
  événement (max 2) au-dessus de ce bouton, sélection d'une carte pour la
  jouer avec séquence de ciblage interactive (surbrillance des slots
  adversaires cliquables) pour Accord royal et la variante Trousse
  d'Épidémie.
- 61 nouveaux tests unitaires serveur (events.js + Room.js : pioche, choix
  de jouer, résolution des 6 effets avec/sans synergie, Complot découvert,
  ordre événements-avant-personnages, deck partagé).
- Nouveaux helpers et tests d'intégration E2E Playwright (e2e/tests/events.spec.js)
  pour la pioche, la limite de main à 2, et les 6 effets — certains
  scénarios restent probabilistes (pioche aléatoire dans le deck partagé) et
  peuvent nécessiter une stabilisation supplémentaire du timing.
This commit is contained in:
Kevin L'Hereec committed 2026-08-21 17:05:50 +02:00
1 parent 9139989fa7
commit 1726f8f96b
17 files changed
+1952 -16

No files matched your search

+19 -8
View File
@@ -3,6 +3,7 @@ import { CARD_W, CARD_H, RADIUS } from '../objects/cardLayout'
import { ITEM_CARD_W, ITEM_IMAGE_H, ITEM_RADIUS } from '../objects/itemLayout'
import { ITEMS } from '../items'
import { BUILDINGS } from '../buildings'
import { EVENTS } from '../events'
const CARD_IMAGES = [
{ id: 'queen', file: 'reine.png' },
@@ -58,12 +59,17 @@ export class BootScene extends Phaser.Scene {
BUILDINGS.forEach(({ id, image }) => {
this.load.image(`raw-building-${id}`, `/buildings/${image}`)
})
// Charger les images brutes des cartes Événement
EVENTS.forEach(({ id, image }) => {
this.load.image(`raw-event-${id}`, `/events/${image}`)
})
}
create() {
// Pour chaque personnage, générer une texture clippée via Canvas 2D
CARD_IMAGES.forEach(({ id }) => {
this._generateClippedTexture(id)
this._generateClippedTexture(`raw-${id}`, `card-${id}`)
})
// Pour chaque objet, générer une texture portrait (zone haute uniquement,
@@ -77,18 +83,23 @@ export class BootScene extends Phaser.Scene {
this._generatePortraitTexture(`raw-building-${id}`, `building-${id}`)
})
// Pour chaque carte Événement, texture plein cadre (mêmes proportions
// que les cartes personnage) — la mécanique diffère mais le rendu visuel
// se veut cohérent avec le style épuré de CardObject.
EVENTS.forEach(({ id }) => {
this._generateClippedTexture(`raw-event-${id}`, `event-${id}`)
})
this.scene.start('GameScene')
}
/**
* Crée une texture `card-{id}` de dimensions CARD_W × CARD_H
* avec l'image clippée en rectangle arrondi via Canvas 2D natif.
* Pas de masque Phaser, pas de RenderTexture — juste du canvas.
* Crée une texture `outKey` de dimensions CARD_W × CARD_H avec l'image
* clippée en rectangle arrondi via Canvas 2D natif (pas de masque Phaser,
* pas de RenderTexture). Réutilisée pour les personnages ET les cartes
* Événement, qui partagent le même format "portrait plein cadre".
*/
_generateClippedTexture(id) {
const rawKey = `raw-${id}`
const outKey = `card-${id}`
_generateClippedTexture(rawKey, outKey) {
if (!this.textures.exists(rawKey)) return
if (this.textures.exists(outKey)) return