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

+39 -1
View File
@@ -99,6 +99,42 @@ io.on('connection', (socket) => {
})
})
// ─── Piocher une carte Événement ─────────────────────────────────────────────
socket.on('buy-event-draw', ({ roomId }) => {
const room = rooms.get(roomId)
if (!room) return
const result = room.buyEventDraw(socket.id)
if (!result.ok) {
socket.emit('error', { message: result.error })
return
}
// Contrairement aux objets/bâtiments, la pioche est immédiate (pas de
// conflit de stock possible) : chaque joueur reçoit son état à jour tout
// de suite (seul le joueur ayant pioché voit la nouvelle carte dans
// myEventHand, les autres voient juste ses jauges diminuer).
room.players.forEach(player => {
io.to(player.socketId).emit('game-state', room.getStateFor(player.socketId))
})
})
// ─── Choisir de jouer une carte Événement ce tour ────────────────────────────
socket.on('play-event', ({ roomId, eventId, targets }) => {
const room = rooms.get(roomId)
if (!room) return
const result = room.playEventCard(socket.id, eventId, targets)
if (!result.ok) {
socket.emit('error', { message: result.error })
return
}
room.players.forEach(player => {
io.to(player.socketId).emit('game-state', room.getStateFor(player.socketId))
})
})
// ─── Jouer une carte ─────────────────────────────────────────────────────────
socket.on('play-card', ({ roomId, cardId }) => {
const room = rooms.get(roomId)
@@ -128,12 +164,14 @@ io.on('connection', (socket) => {
const roundResult = room.resolveRound()
console.log(`[${roomId}] Tour ${room.currentTurn} résolu`)
// Révéler les cartes jouées, les gains par attribut, et les objets/bâtiments achetés
// Révéler les cartes jouées, les gains par attribut, les objets/bâtiments
// achetés, et les résultats des cartes Événement jouées ce tour.
io.to(roomId).emit('round-result', {
playedCards: roundResult.playedCards,
gains: roundResult.gains,
boughtItems: roundResult.boughtItems,
boughtBuildings: roundResult.boughtBuildings,
eventResults: roundResult.eventResults,
message: roundResult.message
})