- Nouveau server/src/game/buildings.js : 2 bâtiments, chacun couvrant 2 attributs, coût réparti sur les deux jauges, garantit +1 par tour sur chaque attribut, avec bonus de synergie +1 si l'objet correspondant est aussi possédé (Église+Trousse/Sceau, Château+Charrue/Trébuchet). - Room.js : buyBuilding() (validation coût multi-jauges), résolution des achats en attente avec la même priorité/stock unique que les objets, _applyBuildingEffects() (garantie de base + synergie, jamais de recul par rapport aux gains déjà boostés par un objet). getStateFor/getPlayersInfo exposent buildings/myBuildings/availableBuildings/pendingBuyBuildingId. - Nouvel event socket 'buy-building', boughtBuildings inclus dans round-result. - Client : buildings.js (miroir), BootScene génère les textures portrait des bâtiments (méthode _generatePortraitTexture généralisée, réutilisée avec les objets). ItemBoardToken généralisé (kind='item'|'building', hAnchor 'left'|'right') pour afficher indifféremment un objet ou un bâtiment, avec coût détaillé par jauge affiché sur la carte agrandie pour les bâtiments. - GameScene : boutique de bâtiments en colonne à DROITE du plateau (symétrique à la boutique d'objets à gauche), mes bâtiments possédés affichés à GAUCHE de ma main (symétrique à mes objets à droite), bâtiments des adversaires affichés sous leurs objets au niveau de leur slot. - Fix : la carte détaillée héritait par défaut d'un ancrage "s'étend vers la droite" adapté à la boutique d'objets (bord gauche du canvas) mais cassait la boutique de bâtiments (bord droit) — le bouton ACHETER se retrouvait hors du canvas et donc injoignable au clic. Ajout du paramètre hAnchor. - 26 nouveaux tests unitaires serveur (buildings.js + Room.js : validations d'achat multi-jauges, priorité/stock unique, effets de base et synergie). - 6 nouveaux tests E2E Playwright (boutique bâtiments : affichage, position, carte détaillée dans les limites du canvas, coût affiché, achat complet). - Fix flakiness des tests E2E d'achat (item-shop + building-shop) : le mode 'min' de playCard garantit désormais que l'adversaire joue sa carte la plus faible sur l'attribut ciblé, au lieu de simplement jouer un autre attribut (insuffisant, le gain dépend de la comparaison au voisin gauche).
Regnum
Jeu de cartes en ligne multijoueur (2-4 joueurs). Collecte des points en jouant des cartes de royaume simultanément pendant X tours.
Stack
- Frontend : Vue 3 + Phaser 3 + Pinia
- Backend : Node.js + Express + Socket.io
- Temps réel : WebSockets
Structure
regnum/
├── client/ # Vue 3 + Phaser 3
│ ├── src/
│ │ ├── views/ # HomeView, LobbyView, GameView
│ │ ├── game/ # Scènes Phaser (Boot, Game) + objets + items
│ │ ├── stores/ # Pinia store
│ │ ├── services/# Socket.io client
│ │ └── utils/ # Fonctions pures partagées (HUD, etc.)
│ └── test/unit/ # Tests unitaires Vitest (store, utils)
├── server/ # Node.js
│ ├── src/
│ │ ├── game/ # Room, RoomManager, cards, items
│ │ └── index.js # Serveur Express + Socket.io
│ └── test/game/ # Tests unitaires node:test (moteur de jeu)
└── e2e/ # Tests d'intégration Playwright (client+serveur réels)
└── tests/
Lancement
Serveur
cd server
npm install
npm run dev
Client
cd client
npm install
npm run dev
Ouvrir http://localhost:5173
Tests
Le projet a 3 niveaux de tests, à exécuter indépendamment :
1. Tests unitaires — serveur (moteur de jeu)
Logique pure de Room.js / cards.js / items.js (règles, gains, achats, effets permanents), avec le test runner natif de Node.js :
cd server
npm test # exécution unique
npm run test:watch # mode watch
2. Tests unitaires — client (store, utilitaires)
Store Pinia et fonctions pures du HUD (hudUtils.js), avec Vitest :
cd client
npm run test:unit # exécution unique
npm run test:unit:watch # mode watch
3. Tests d'intégration E2E (client + serveur réels)
Parcours complets (créer/rejoindre une salle, jouer des tours, acheter un objet, fin de partie) pilotés dans un vrai navigateur Chromium via Playwright, avec de vrais clients Socket.io. webServer dans e2e/playwright.config.js démarre automatiquement le serveur et le client si besoin.
cd e2e
npm install # première fois seulement
npx playwright install chromium # première fois seulement, si besoin
npm test # exécution unique
npm run test:ui # mode interactif (debug)
Règles
Principe général
- 2 à 4 joueurs par partie, chacun avec son propre deck des mêmes 12 personnages (seule la couleur d'équipe change).
- Chaque joueur commence avec une main de 4 cartes ; le reste du deck (8 cartes) est pioché une carte par tour.
- La partie dure 12 tours. Elle se termine avant si tous les joueurs n'ont plus ni carte en main ni carte à piocher.
- Chaque tour, tous les joueurs choisissent simultanément une carte (cachée), puis les cartes sont révélées et résolues en même temps.
Les 4 jauges
Chaque personnage possède 4 attributs (valeurs de 1 à 6), qui correspondent à 4 jauges cumulées par le joueur pendant toute la partie :
| Jauge | Icône |
|---|---|
| Nourriture | 🟡 |
| Guerre | 🔴 |
| Santé | 🟢 |
| Diplomatie | 🔵 |
Résolution d'un tour
Pour chaque attribut, un joueur compare la valeur de sa carte jouée à celle de son voisin de gauche (ordre des joueurs dans la partie, en boucle) :
gain = max(0, ma_valeur - valeur_du_voisin_de_gauche)
Le gain (jamais négatif) est ajouté à la jauge correspondante du joueur. Le score total d'un joueur est la somme de ses 4 jauges.
Les 12 personnages
Reine, Prince, Boucher, Médecin, Apothicaire, Fou du Roi, Assassin, Paysan, Bourreau, Chevalier, Marchand, Évêque — chacun avec un profil d'attributs différent (ex : le Paysan est fort en Nourriture mais faible en Diplomatie, l'Assassin est très fort en Guerre mais faible partout ailleurs).
Objets achetables
Pendant la phase de sélection, un joueur peut dépenser des points d'une jauge pour acheter un objet, qui procure un effet permanent pour le reste de la partie tant qu'il le possède. Chaque objet coûte 3 points et n'existe qu'en un seul exemplaire pour toute la partie (premier arrivé, premier servi) :
| Objet | Jauge liée | Effet |
|---|---|---|
| Trébuchet | 🔴 Guerre | Double le gain de Guerre à chaque tour |
| Charrue | 🟡 Nourriture | Double le gain de Nourriture à chaque tour |
| Trousse du Médecin | 🟢 Santé | Garantit +2 en Santé à chaque tour, quel que soit le résultat |
| Sceau Royal | 🔵 Diplomatie | Copie le meilleur gain de Diplomatie parmi tous les autres joueurs |
En cas de conflit (plusieurs joueurs achètent le même objet le même tour), c'est le joueur le plus proche du "premier joueur" du tour (rôle qui tourne entre les joueurs à chaque tour) qui l'obtient ; les autres conservent leurs points.
Fin de partie
Le joueur avec le score total (somme des 4 jauges) le plus élevé après 12 tours remporte la partie.