fix: cartes Événement illisibles + objets/bâtiments adversaires au-dessus de leur carte
Deux problèmes de lisibilité/placement signalés : 1. Le texte des cartes Événement (nom + description) restait illisible malgré le passage précédent de l'échelle 0.55 à 0.8 — le composant dédié EventCardObject affichait une grande carte réduite, écrasant systématiquement le texte quelle que soit l'échelle choisie. Fix : suppression complète d'EventCardObject, remplacé par le même pattern que les objets/bâtiments (ItemBoardToken, nouveau kind='event') : un petit jeton compact (46x46) toujours visible sur le plateau, avec la carte détaillée affichée en TAILLE RÉELLE (police 13-16px, jamais réduite) au survol de la souris, et un bouton "JOUER" (au lieu d'"ACHETER") pour confirmer. ItemBoardToken généralisé pour supporter ce 3e kind (pas de badge de coût affiché — déjà payé à la pioche — mais badge "!" repris pour les événements à effet négatif type Épidémie). Extraction de client/src/game/objects/tokenLayout.js (ICON_SIZE) dans un module SANS dépendance à Phaser, pour que eventHandLayout.js (et ses tests Vitest) puisse l'importer sans jamais charger Phaser — qui échoue à l'import en environnement jsdom (canvas natif requis, absent de jsdom). 2. Les mini-cartes objets/bâtiments déjà achetés par un ADVERSAIRE étaient affichées SOUS son slot de jeu, visuellement en dessous/après sa carte jouée. Déplacées AU-DESSUS de son slot pour les mettre davantage en évidence, comme posées sur sa carte plutôt que cachées dessous. - 43 tests unitaires client mis à jour (eventHandLayout.test.js migré vers ICON_SIZE au lieu de CARD_W/CARD_H/scale, désormais indépendant de Phaser). - e2e/tests/helpers.js : playEventCard adapté au nouveau flux survol->bouton JOUER (au lieu d'un simple clic direct sur la carte) ; drawEventUntil corrigé pour ne jamais défausser une carte 'royal_accord' sans fournir ses 2 cibles de ciblage séquentiel obligatoire (le jeu sans cible bloquait silencieusement la séquence, sans jamais émettre 'play-event', rendant la boucle de pioche très peu fiable). - Vérification ciblée via un test E2E existant (pas de nouvelle suite E2E lourde, conformément à la demande de privilégier les tests unitaires) : le nouveau flux jeton->survol->JOUER->résolution fonctionne bout en bout (Récolte exceptionnelle +2 Nourriture confirmé après résolution).
This commit is contained in:
1 parent
45eb9be60d
commit
1a91a0f876
7 files changed
+316
-309
No files matched your search
@@ -2,10 +2,9 @@ import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
eventDrawButtonPosition,
|
||||
eventHandGeometry,
|
||||
eventCardPosition,
|
||||
EVENT_CARD_SCALE
|
||||
eventCardPosition
|
||||
} from '../../src/game/objects/eventHandLayout'
|
||||
import { CARD_W, CARD_H } from '../../src/game/objects/cardLayout'
|
||||
import { ICON_SIZE } from '../../src/game/objects/tokenLayout'
|
||||
|
||||
// Dimensions de canvas typiques utilisées dans les tests (cohérentes avec
|
||||
// GameFactory.js : width = innerWidth, height = innerHeight - 60).
|
||||
@@ -13,13 +12,6 @@ const CANVAS_W = 1920
|
||||
const CANVAS_H = 1080
|
||||
|
||||
describe('game/objects/eventHandLayout', () => {
|
||||
describe('EVENT_CARD_SCALE', () => {
|
||||
it('reste suffisamment proche de 1 pour garder le texte lisible (pas 0.55 comme l\'ancien placement)', () => {
|
||||
expect(EVENT_CARD_SCALE).toBeGreaterThanOrEqual(0.7)
|
||||
expect(EVENT_CARD_SCALE).toBeLessThanOrEqual(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('eventDrawButtonPosition', () => {
|
||||
it('positionne le bouton dans le quart bas-droit du canvas', () => {
|
||||
const { x, y } = eventDrawButtonPosition(CANVAS_W, CANVAS_H)
|
||||
@@ -36,13 +28,12 @@ describe('game/objects/eventHandLayout', () => {
|
||||
expect(y + buttonHeight / 2).toBeLessThanOrEqual(CANVAS_H)
|
||||
})
|
||||
|
||||
it('est positionné strictement au-dessus de la main de cartes événement (pas de chevauchement)', () => {
|
||||
it('est positionné strictement au-dessus de la main de jetons événement (pas de chevauchement)', () => {
|
||||
const buttonHeight = 30
|
||||
const { y: btnY } = eventDrawButtonPosition(CANVAS_W, CANVAS_H)
|
||||
const { y: handY } = eventHandGeometry(CANVAS_W, CANVAS_H, 1)
|
||||
const cardH = CARD_H * EVENT_CARD_SCALE
|
||||
const buttonBottom = btnY + buttonHeight / 2
|
||||
const handTop = handY - cardH / 2
|
||||
const handTop = handY - ICON_SIZE / 2
|
||||
expect(buttonBottom).toBeLessThanOrEqual(handTop)
|
||||
})
|
||||
|
||||
@@ -74,22 +65,20 @@ describe('game/objects/eventHandLayout', () => {
|
||||
expect(y).toBeGreaterThan(CANVAS_H / 2)
|
||||
})
|
||||
|
||||
it('la dernière carte est ancrée sur le bord droit du canvas (avec la marge)', () => {
|
||||
const { startX, spacing, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
const cardW = CARD_W * scale
|
||||
const lastCardCenterX = startX + spacing // index 1 = dernière carte pour cardCount=2
|
||||
const lastCardRightEdge = lastCardCenterX + cardW / 2
|
||||
expect(lastCardRightEdge).toBeLessThanOrEqual(CANVAS_W)
|
||||
expect(lastCardRightEdge).toBeGreaterThan(CANVAS_W - 40) // proche du bord (marge ~20px)
|
||||
it('le dernier jeton est ancré sur le bord droit du canvas (avec la marge)', () => {
|
||||
const { startX, spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
const lastCenterX = startX + spacing // index 1 = dernier jeton pour cardCount=2
|
||||
const lastRightEdge = lastCenterX + ICON_SIZE / 2
|
||||
expect(lastRightEdge).toBeLessThanOrEqual(CANVAS_W)
|
||||
expect(lastRightEdge).toBeGreaterThan(CANVAS_W - 40) // proche du bord (marge ~20px)
|
||||
})
|
||||
|
||||
it('avec 1 carte, elle est ancrée sur le bord droit (pas de décalage vers la gauche)', () => {
|
||||
it('avec 1 jeton, il est ancré sur le bord droit (pas de décalage vers la gauche)', () => {
|
||||
const { startX } = eventHandGeometry(CANVAS_W, CANVAS_H, 1)
|
||||
const { startX: startXTwo } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
// La position de la DERNIÈRE carte doit être identique quel que soit
|
||||
// le nombre de cartes (ancrage à droite stable) — avec 1 carte,
|
||||
// startX doit correspondre à la position de la dernière carte du cas à 2.
|
||||
const { spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
const { startX: startXTwo, spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
// La position du DERNIER jeton doit être identique quel que soit le
|
||||
// nombre de jetons (ancrage à droite stable) — avec 1 jeton, startX
|
||||
// doit correspondre à la position du dernier jeton du cas à 2.
|
||||
expect(startX).toBeCloseTo(startXTwo + spacing, 5)
|
||||
})
|
||||
|
||||
@@ -100,27 +89,24 @@ describe('game/objects/eventHandLayout', () => {
|
||||
expect(Number.isFinite(spacing)).toBe(true)
|
||||
})
|
||||
|
||||
it('l\'espacement entre cartes est toujours positif et couvre au moins la largeur d\'une carte', () => {
|
||||
const { spacing, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
expect(spacing).toBeGreaterThan(CARD_W * scale)
|
||||
it('l\'espacement entre jetons est toujours positif et couvre au moins la largeur d\'un jeton', () => {
|
||||
const { spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
expect(spacing).toBeGreaterThan(ICON_SIZE)
|
||||
})
|
||||
|
||||
it('les 2 cartes ne se chevauchent jamais (distance entre centres >= largeur d\'une carte)', () => {
|
||||
const { spacing, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
const cardW = CARD_W * scale
|
||||
expect(spacing).toBeGreaterThanOrEqual(cardW)
|
||||
it('les 2 jetons ne se chevauchent jamais (distance entre centres >= largeur d\'un jeton)', () => {
|
||||
const { spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
expect(spacing).toBeGreaterThanOrEqual(ICON_SIZE)
|
||||
})
|
||||
|
||||
it('reste entièrement dans le canvas sur une petite résolution (1280x720)', () => {
|
||||
const { startX, y, spacing, scale } = eventHandGeometry(1280, 720, 2)
|
||||
const cardW = CARD_W * scale
|
||||
const cardH = CARD_H * scale
|
||||
const firstCardLeftEdge = startX - cardW / 2
|
||||
const lastCardRightEdge = startX + spacing + cardW / 2
|
||||
expect(firstCardLeftEdge).toBeGreaterThanOrEqual(0)
|
||||
expect(lastCardRightEdge).toBeLessThanOrEqual(1280)
|
||||
expect(y + cardH / 2).toBeLessThanOrEqual(720)
|
||||
expect(y - cardH / 2).toBeGreaterThanOrEqual(0)
|
||||
const { startX, y, spacing } = eventHandGeometry(1280, 720, 2)
|
||||
const firstLeftEdge = startX - ICON_SIZE / 2
|
||||
const lastRightEdge = startX + spacing + ICON_SIZE / 2
|
||||
expect(firstLeftEdge).toBeGreaterThanOrEqual(0)
|
||||
expect(lastRightEdge).toBeLessThanOrEqual(1280)
|
||||
expect(y + ICON_SIZE / 2).toBeLessThanOrEqual(720)
|
||||
expect(y - ICON_SIZE / 2).toBeGreaterThanOrEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -133,7 +119,7 @@ describe('game/objects/eventHandLayout', () => {
|
||||
expect(pos1).toEqual({ x: startX + spacing, y })
|
||||
})
|
||||
|
||||
it('les positions de 2 cartes sont distinctes', () => {
|
||||
it('les positions de 2 jetons sont distinctes', () => {
|
||||
const pos0 = eventCardPosition(CANVAS_W, CANVAS_H, 2, 0)
|
||||
const pos1 = eventCardPosition(CANVAS_W, CANVAS_H, 2, 1)
|
||||
expect(pos0.x).not.toBe(pos1.x)
|
||||
@@ -142,13 +128,12 @@ describe('game/objects/eventHandLayout', () => {
|
||||
|
||||
describe('non-régression : séparation avec la légende d\'attributs (bas-gauche)', () => {
|
||||
it('la zone événement (bas-droite) ne recouvre pas la zone légende (bas-gauche, x proche de 0)', () => {
|
||||
const { startX, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
const cardW = CARD_W * scale
|
||||
const firstCardLeftEdge = startX - cardW / 2
|
||||
const { startX } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
||||
const firstLeftEdge = startX - ICON_SIZE / 2
|
||||
// La légende occupe le coin bas-gauche (x ancré à 10px du bord gauche,
|
||||
// largeur de texte ~180px max) — la zone événement doit rester dans
|
||||
// la moitié droite du canvas pour ne jamais s'en approcher.
|
||||
expect(firstCardLeftEdge).toBeGreaterThan(CANVAS_W / 2)
|
||||
expect(firstLeftEdge).toBeGreaterThan(CANVAS_W / 2)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user