From b46cb707cd8fa83b6b40b1f0c27b238b7e23afd4 Mon Sep 17 00:00:00 2001 From: "kevin.lhereec" Date: Mon, 17 Aug 2026 12:07:47 +0200 Subject: [PATCH] feat: draw one card after each turn with slide-in animation --- client/src/game/scenes/GameScene.js | 77 ++++++++++++++++++++++++++++- server/src/game/Room.js | 23 ++++++--- server/src/game/cards.js | 12 +++-- 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/client/src/game/scenes/GameScene.js b/client/src/game/scenes/GameScene.js index fb1c08c..cc4a3f0 100644 --- a/client/src/game/scenes/GameScene.js +++ b/client/src/game/scenes/GameScene.js @@ -363,7 +363,12 @@ export class GameScene extends Phaser.Scene { if (this.playSlots.length !== state.players.length) { this._buildSlots(state.players.length) } - this._buildHand(state.hand) + // Si ce n'est pas le premier tour, animer la pioche avant d'afficher la main + if (state.currentTurn > 1) { + this._animateDraw(state.hand) + } else { + this._buildHand(state.hand) + } } if (state.phase === 'waiting') { @@ -391,6 +396,76 @@ export class GameScene extends Phaser.Scene { }) } + // ─── Animation pioche ──────────────────────────────────────────────────────── + + /** + * Affiche d'abord les cartes déjà en main, puis fait tomber la nouvelle + * (toujours la dernière du tableau, ajoutée par nextTurn côté serveur) + */ + _animateDraw(cards) { + const { width, height } = this.scale + + // Afficher immédiatement les cartes déjà en main (toutes sauf la dernière) + const existing = cards.slice(0, -1) + const newCard = cards[cards.length - 1] + + this._buildHand(existing) + + if (!newCard) return + + // Calculer la position finale de la nouvelle carte dans la main complète + const handY = height - BOTTOM_MARGIN - 110 + const spacing = Math.min(150, (width * 0.85) / cards.length) + const totalW = spacing * (cards.length - 1) + const startX = width / 2 - totalW / 2 + const finalX = startX + (cards.length - 1) * spacing + + // Créer la carte hors écran (au-dessus) + const card = new CardObject(this, finalX, -200, newCard) + card.setDepth(50) + card.on('select', this._onCardSelected, this) + card.setAlpha(0) + + // Animation : fade + slide vers la position finale + this.tweens.add({ + targets: card, + y: handY, + alpha: 1, + duration: 400, + ease: 'Back.Out', + delay: 100, + onComplete: () => { + card._baseY = handY + card.setDepth(0) + this.handCards.push(card) + // Recalculer le spacing de toutes les cartes maintenant qu'on en a une de plus + this._repositionHand() + } + }) + } + + /** Repositionne toutes les cartes en main avec le bon espacement */ + _repositionHand() { + const { width, height } = this.scale + const handY = height - BOTTOM_MARGIN - 110 + const n = this.handCards.length + const spacing = Math.min(150, (width * 0.85) / n) + const totalW = spacing * (n - 1) + const startX = width / 2 - totalW / 2 + + this.handCards.forEach((card, i) => { + const targetX = startX + i * spacing + card._baseY = handY + this.tweens.add({ + targets: card, + x: targetX, + y: handY, + duration: 250, + ease: 'Power2' + }) + }) + } + // ─── Utilitaires ───────────────────────────────────────────────────────────── _darken(hex, factor) { diff --git a/server/src/game/Room.js b/server/src/game/Room.js index aeae8e6..7e3ca89 100644 --- a/server/src/game/Room.js +++ b/server/src/game/Room.js @@ -37,7 +37,8 @@ class Room { name, teamIndex: this.players.length, gauges: { food: 0, war: 0, health: 0, diplo: 0 }, - hand: [] + hand: [], + deck: [] // cartes restantes à piocher }) return true } @@ -49,9 +50,10 @@ class Room { // ─── Démarrage ────────────────────────────────────────────────────────────── startGame() { - const hands = dealHands(this.players.length, HAND_SIZE) + const dealt = dealHands(this.players.length, HAND_SIZE) this.players.forEach((p, i) => { - p.hand = hands[i] + p.hand = dealt[i].hand + p.deck = dealt[i].deck p.gauges = { food: 0, war: 0, health: 0, diplo: 0 } }) this.currentTurn = 1 @@ -144,6 +146,13 @@ class Room { // ─── Tour suivant ──────────────────────────────────────────────────────────── nextTurn() { + // Chaque joueur pioche une carte depuis son deck personnel + this.players.forEach(p => { + if (p.deck.length > 0) { + p.hand.push(p.deck.shift()) + } + }) + this.currentTurn++ if (this.isGameOver()) { this.phase = 'end' @@ -153,8 +162,9 @@ class Room { } isGameOver() { - const allEmpty = this.players.every(p => p.hand.length === 0) - return allEmpty || this.currentTurn >= this.maxTurns + // Partie terminée quand plus personne n'a de carte en main ET plus de deck + const allEmpty = this.players.every(p => p.hand.length === 0 && p.deck.length === 0) + return allEmpty || this.currentTurn > this.maxTurns } // ─── Données publiques ─────────────────────────────────────────────────────── @@ -165,7 +175,8 @@ class Room { teamIndex: p.teamIndex, gauges: { ...p.gauges }, score: ATTRS.reduce((sum, a) => sum + p.gauges[a], 0), - handSize: p.hand.length + handSize: p.hand.length, + deckSize: p.deck.length })) } diff --git a/server/src/game/cards.js b/server/src/game/cards.js index adb6332..37bae12 100644 --- a/server/src/game/cards.js +++ b/server/src/game/cards.js @@ -165,13 +165,19 @@ function shuffle(arr) { return a } +/** + * Pour chaque joueur : retourne { hand, deck } (hand = les N premières cartes, deck = le reste) + */ function dealHands(playerCount, handSize = 4) { - const hands = [] + const result = [] for (let i = 0; i < playerCount; i++) { const deck = shuffle(getDeckForTeam(i)) - hands.push(deck.slice(0, handSize)) + result.push({ + hand: deck.slice(0, handSize), + deck: deck.slice(handSize) // cartes restantes à piocher + }) } - return hands + return result } module.exports = { CHARACTERS, TEAM_COLORS, ATTR_COLORS, getDeckForTeam, dealHands }