From 91a1a9f4f042d5429d5486e12bff78e671b71f2c Mon Sep 17 00:00:00 2001 From: "kevin.lhereec" Date: Mon, 17 Aug 2026 11:54:23 +0200 Subject: [PATCH] feat: gauge scoring system (subtract left neighbor per attribute) --- client/src/game/scenes/GameScene.js | 155 +++++++++++------------- client/src/stores/game.js | 37 +++--- client/src/views/GameView.vue | 175 +++++++++++++++++++++++----- server/src/game/Room.js | 129 ++++++++++---------- server/src/index.js | 4 +- 5 files changed, 297 insertions(+), 203 deletions(-) diff --git a/client/src/game/scenes/GameScene.js b/client/src/game/scenes/GameScene.js index 97e4323..9909669 100644 --- a/client/src/game/scenes/GameScene.js +++ b/client/src/game/scenes/GameScene.js @@ -17,12 +17,6 @@ const ATTR_COLORS = { diplo: 0x3399ee } -const ATTR_LABELS = { - food: 'Nourriture', - war: 'Guerre', - health: 'Santé', - diplo: 'Diplomatie' -} export class GameScene extends Phaser.Scene { constructor() { @@ -184,104 +178,96 @@ export class GameScene extends Phaser.Scene { // ─── Résolution visuelle ───────────────────────────────────────────────────── - _showPlayedCards(playedCards) { - // Nettoyer + /** + * Affiche les cartes jouées et les gains obtenus (soustraction voisin gauche) + * @param {Array} playedCards - cartes jouées dans l'ordre des joueurs + * @param {Array} gains - gains[i] = { food, war, health, diplo } pour le joueur i + */ + _showPlayedCards(playedCards, gains = []) { this.resolveObjects.forEach(o => o.destroy()) this.resolveObjects = [] + const ATTRS_POS = [ + { key: 'food', label: 'NOU', dx: -28, dy: -20 }, + { key: 'war', label: 'GUE', dx: 28, dy: -20 }, + { key: 'health', label: 'SAN', dx: -28, dy: 30 }, + { key: 'diplo', label: 'DIP', dx: 28, dy: 30 } + ] + playedCards.forEach((entry, i) => { const slot = this.playSlots[i] if (!slot) return const teamColor = TEAM_COLORS[entry.teamIndex] ?? 0x888888 + const gain = gains[i] ?? {} - // Mini carte de résolution + // ── Fond carte ────────────────────────────────────────── const g = this.add.graphics() - g.fillStyle(this._darken(teamColor, 0.3)) - g.fillRoundedRect(-55, -85, 110, 170, 6) + g.fillStyle(this._darken(teamColor, 0.25)) + g.fillRoundedRect(-55, -90, 110, 180, 6) g.lineStyle(2, teamColor) - g.strokeRoundedRect(-55, -85, 110, 170, 6) + g.strokeRoundedRect(-55, -90, 110, 180, 6) g.x = slot.x g.y = slot.y this.resolveObjects.push(g) - // Nom du joueur - const pName = this.add.text(slot.x, slot.y - 72, entry.playerName, { - fontSize: '10px', fontFamily: 'Georgia', color: '#ffffff', fontStyle: 'bold' - }).setOrigin(0.5) - this.resolveObjects.push(pName) - - // Nom de la carte - const cName = this.add.text(slot.x, slot.y - 56, entry.cardName, { - fontSize: '13px', fontFamily: 'Georgia', color: '#d4a732', fontStyle: 'bold' - }).setOrigin(0.5) - this.resolveObjects.push(cName) - - // 4 attributs - const attrs = [ - { key: 'food', label: 'NOU', cx: slot.x - 28, cy: slot.y - 20 }, - { key: 'war', label: 'GUE', cx: slot.x + 28, cy: slot.y - 20 }, - { key: 'health', label: 'SAN', cx: slot.x - 28, cy: slot.y + 40 }, - { key: 'diplo', label: 'DIP', cx: slot.x + 28, cy: slot.y + 40 } - ] - - attrs.forEach(({ key, label, cx, cy }) => { - const c = this.add.graphics() - c.fillStyle(ATTR_COLORS[key], 0.85) - c.fillRoundedRect(-20, -18, 40, 36, 4) - c.x = cx - c.y = cy - this.resolveObjects.push(c) - - const v = this.add.text(cx, cy - 4, String(entry[key]), { - fontSize: '14px', fontFamily: 'Georgia', fontStyle: 'bold', - color: '#ffffff', stroke: '#000000', strokeThickness: 2 + // ── Nom du joueur ──────────────────────────────────────── + this.resolveObjects.push( + this.add.text(slot.x, slot.y - 78, entry.playerName, { + fontSize: '10px', fontFamily: 'Georgia', color: '#ffffff', fontStyle: 'bold' }).setOrigin(0.5) - this.resolveObjects.push(v) + ) - const l = this.add.text(cx, cy + 10, label, { - fontSize: '7px', fontFamily: 'Arial', color: '#ffffff' + // ── Nom de la carte ────────────────────────────────────── + this.resolveObjects.push( + this.add.text(slot.x, slot.y - 62, entry.cardName, { + fontSize: '12px', fontFamily: 'Georgia', color: '#d4a732', fontStyle: 'bold' }).setOrigin(0.5) - this.resolveObjects.push(l) + ) + + // ── 4 badges attribut + gain ───────────────────────────── + ATTRS_POS.forEach(({ key, label, dx, dy }) => { + const cx = slot.x + dx + const cy = slot.y + dy + const cardVal = entry[key] ?? 0 + const gainVal = gain[key] ?? 0 + + // Badge fond coloré + const badge = this.add.graphics() + badge.fillStyle(ATTR_COLORS[key], gainVal > 0 ? 1.0 : 0.45) + badge.fillRoundedRect(-22, -20, 44, 40, 4) + badge.x = cx + badge.y = cy + this.resolveObjects.push(badge) + + // Valeur de la carte + this.resolveObjects.push( + this.add.text(cx, cy - 7, String(cardVal), { + fontSize: '14px', fontFamily: 'Georgia', fontStyle: 'bold', + color: '#ffffff', stroke: '#000000', strokeThickness: 2 + }).setOrigin(0.5) + ) + + // Label abrégé + this.resolveObjects.push( + this.add.text(cx, cy + 5, label, { + fontSize: '7px', fontFamily: 'Arial', color: '#ffffffcc' + }).setOrigin(0.5) + ) + + // Gain (affiché en vert si > 0) + if (gainVal > 0) { + this.resolveObjects.push( + this.add.text(cx, cy + 16, `+${gainVal}`, { + fontSize: '10px', fontFamily: 'Georgia', fontStyle: 'bold', + color: '#ffffff', stroke: '#000000', strokeThickness: 2 + }).setOrigin(0.5) + ) + } }) }) } - _showAttrResults(attrResults) { - const { width, height } = this.scale - const attrs = ['food', 'war', 'health', 'diplo'] - const boxY = height / 2 + 80 - - attrs.forEach((attr, i) => { - const r = attrResults[attr] - const x = width / 2 - 300 + i * 200 - - const g = this.add.graphics() - g.fillStyle(ATTR_COLORS[attr], 0.15) - g.fillRoundedRect(-90, -20, 180, 40, 6) - g.lineStyle(1, ATTR_COLORS[attr], 0.6) - g.strokeRoundedRect(-90, -20, 180, 40, 6) - g.x = x - g.y = boxY - this.resolveObjects.push(g) - - const label = ATTR_LABELS[attr] - const winText = r.winner === 'tie' - ? `Égalité` - : `${r.winner} +${r.value}` - - const t1 = this.add.text(x, boxY - 8, label, { - fontSize: '11px', fontFamily: 'Georgia', color: '#' + ATTR_COLORS[attr].toString(16).padStart(6, '0') - }).setOrigin(0.5) - this.resolveObjects.push(t1) - - const t2 = this.add.text(x, boxY + 8, winText, { - fontSize: '12px', fontFamily: 'Georgia', color: '#ffffff', fontStyle: 'bold' - }).setOrigin(0.5) - this.resolveObjects.push(t2) - }) - } - // ─── Socket listeners ──────────────────────────────────────────────────────── _setupSocketListeners() { @@ -307,10 +293,9 @@ export class GameScene extends Phaser.Scene { } }) - this.socket.on('round-result', ({ playedCards, attrResults }) => { + this.socket.on('round-result', ({ playedCards, gains }) => { this.phaseText.setText('Résolution du tour...') - this._showPlayedCards(playedCards) - this._showAttrResults(attrResults) + this._showPlayedCards(playedCards, gains) }) } diff --git a/client/src/stores/game.js b/client/src/stores/game.js index 2e56cd7..3d4a3d5 100644 --- a/client/src/stores/game.js +++ b/client/src/stores/game.js @@ -2,13 +2,13 @@ import { defineStore } from 'pinia' import { ref } from 'vue' export const useGameStore = defineStore('game', () => { - const playerName = ref('') - const playerId = ref('') - const players = ref([]) - const currentTurn = ref(0) - const maxTurns = ref(10) - const phase = ref('idle') // idle | selecting | waiting | resolve | end - const hand = ref([]) + const playerName = ref('') + const players = ref([]) // { name, teamIndex, gauges:{food,war,health,diplo}, score, handSize } + const currentTurn = ref(0) + const maxTurns = ref(12) + const phase = ref('idle') + const hand = ref([]) + const myTeamIndex = ref(0) const selectedCard = ref(null) function setPlayer(name) { @@ -16,25 +16,20 @@ export const useGameStore = defineStore('game', () => { } function updateState(state) { - players.value = state.players + players.value = state.players currentTurn.value = state.currentTurn - maxTurns.value = state.maxTurns - phase.value = state.phase - hand.value = state.hand || hand.value + maxTurns.value = state.maxTurns + phase.value = state.phase + myTeamIndex.value = state.myTeamIndex ?? 0 + if (state.hand !== undefined) hand.value = state.hand } - function selectCard(card) { - selectedCard.value = card - } - - function clearSelection() { - selectedCard.value = null - } + function selectCard(card) { selectedCard.value = card } + function clearSelection() { selectedCard.value = null } return { - playerName, playerId, - players, currentTurn, maxTurns, - phase, hand, selectedCard, + playerName, players, currentTurn, maxTurns, + phase, hand, myTeamIndex, selectedCard, setPlayer, updateState, selectCard, clearSelection } }) diff --git a/client/src/views/GameView.vue b/client/src/views/GameView.vue index 1b2dbc5..d42e0dc 100644 --- a/client/src/views/GameView.vue +++ b/client/src/views/GameView.vue @@ -1,21 +1,41 @@