Files
Kevin L'Hereec 6a1c800253 feat: système d'objets achetables (boutique + effets permanents) et refonte visuelle
- Nouveaux personnages illustrés (12 PNG remplacés dans client/public/cards)
- 4 objets achetables (Trébuchet, Charrue, Trousse du Médecin, Sceau Royal)
  avec effets permanents (double_gain, guaranteed_gain, copy_best_in_game),
  stock unique par partie, priorité d'achat tournante entre joueurs
- Boutique en colonne à gauche du plateau : icônes compactes, carte détaillée
  agrandie au survol (taille découplée du displayScale du jeton), bouton
  ACHETER actif/grisé selon la jauge du joueur, retrait immédiat de la
  colonne une fois l'objet acheté (les autres remontent pour combler)
- Objets achetés affichés "posés sur la table" : à côté de la main pour soi,
  sous le slot de jeu pour les adversaires
- Icône du premier joueur (priorité d'achat) dans le HUD Vue
- Refonte des cartes personnage (CardObject) : portrait plein cadre épuré,
  valeurs d'attribut discrètes aux 4 coins
- Fix removePlayer : nettoyage de pendingBuys/pendingPlays et recalage de
  firstPlayerIndex à la déconnexion d'un joueur
- 77 tests unitaires serveur (node:test) : cards, items, Room, RoomManager
- README mis à jour avec les règles complètes du jeu
- .gitignore : exclusion de .idea/ et .serena/ (artefacts d'outils locaux)
2026-08-21 14:53:58 +02:00

61 lines
2.0 KiB
JavaScript

const { test, describe } = require('node:test')
const assert = require('node:assert/strict')
const { ITEMS, getItemById } = require('../../src/game/items')
describe('items.js', () => {
test('expose exactement 4 objets', () => {
assert.equal(ITEMS.length, 4)
})
test('chaque objet a un id unique', () => {
const ids = ITEMS.map(i => i.id)
assert.equal(new Set(ids).size, ids.length)
})
test('chaque objet est lié à un attribut valide et a un coût positif', () => {
const validAttrs = ['food', 'war', 'health', 'diplo']
for (const item of ITEMS) {
assert.ok(validAttrs.includes(item.attr), `${item.id}.attr doit être un attribut valide`)
assert.ok(item.cost > 0, `${item.id}.cost doit être positif`)
}
})
test('chaque objet a un effect reconnu par Room._applyItemEffects', () => {
const knownEffects = ['double_gain', 'guaranteed_gain', 'copy_best_in_game']
for (const item of ITEMS) {
assert.ok(knownEffects.includes(item.effect), `${item.id}.effect '${item.effect}' inconnu`)
}
})
test('guaranteed_gain (Trousse du Médecin) définit bien effectValue', () => {
const medkit = getItemById('medkit')
assert.ok(medkit)
assert.equal(medkit.effect, 'guaranteed_gain')
assert.equal(typeof medkit.effectValue, 'number')
})
test('aucun attribut n\'est couvert par 2 objets différents (pas de conflit de cumul)', () => {
const attrs = ITEMS.map(i => i.attr)
assert.equal(new Set(attrs).size, attrs.length)
})
describe('getItemById', () => {
test('retourne l\'objet correspondant à un id existant', () => {
const trebuchet = getItemById('trebuchet')
assert.ok(trebuchet)
assert.equal(trebuchet.name, 'Trébuchet')
assert.equal(trebuchet.attr, 'war')
})
test('retourne null pour un id inconnu', () => {
assert.equal(getItemById('does-not-exist'), null)
})
test('retourne null pour undefined/null', () => {
assert.equal(getItemById(undefined), null)
assert.equal(getItemById(null), null)
})
})
})