Fără Descriere

Idleman 1ebd190593 fix(characters): Mamie's yarn lays a taut, anchored wool bridge instead of a falling trail 1 săptămână în urmă
assets 1ebd190593 fix(characters): Mamie's yarn lays a taut, anchored wool bridge instead of a falling trail 1 săptămână în urmă
scripts 2c5970ab29 perf(terrain): detach search in three phases, one search per tick, leaner chunk creation 1 săptămână în urmă
src 1ebd190593 fix(characters): Mamie's yarn lays a taut, anchored wool bridge instead of a falling trail 1 săptămână în urmă
tests 1ebd190593 fix(characters): Mamie's yarn lays a taut, anchored wool bridge instead of a falling trail 1 săptămână în urmă
.gitignore 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
CLAUDE.md 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
electron-builder.yml 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
electron.vite.config.ts 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
eslint.config.mjs 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
package-lock.json 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
package.json 2c5970ab29 perf(terrain): detach search in three phases, one search per tick, leaner chunk creation 1 săptămână în urmă
readme.md 1ebd190593 fix(characters): Mamie's yarn lays a taut, anchored wool bridge instead of a falling trail 1 săptămână în urmă
tsconfig.base.json 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
tsconfig.json 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
tsconfig.node.json 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
tsconfig.web.json 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă
vitest.config.ts 083d43faff chore: scaffold M1 (destructible terrain, authoritative server, tooling) 1 săptămână în urmă

readme.md

GOÛTERMAGEDDON

Jeu de baston 2D en temps réel, exclusivement LAN, de 3 à 8 joueurs en chacun pour soi. Le décor (un goûter géant) est entièrement destructible. Le but : tenir la cerise le plus longtemps possible pendant que la crème anglaise monte.

Ce fichier sert de référence pour le game design et l'architecture. Lis-le avant toute modification. Si une décision le contredit, mets-le à jour dans le même commit.


1. Principes non négociables

  1. Parties courtes. Une manche dure 3 minutes, une partie 4 manches (environ 12 minutes). La relance se fait en un clic.
  2. On attaque le sol, pas les joueurs. La destruction du décor est la mécanique centrale. Une feature qui ne passe pas par le terrain doit le justifier.
  3. LAN uniquement. Pas de compte, pas de matchmaking en ligne, pas de backend distant. On découvre les parties par broadcast UDP.
  4. Lisibilité et humour. DA enfantine et drôle. Chaque action a un retour visuel et sonore exagéré.
  5. Tout est piloté par les données. Cartes, matériaux, personnages et objets sont décrits dans des fichiers de données. Ajouter une carte ne demande aucun code. ---

2. Stack technique

Rôle Choix
Langage TypeScript (strict, ESM)
Runtime / desktop Node.js + Electron
Build / dev electron-vite (HMR sur le renderer)
Rendu 2D PixiJS v8
Physique planck.js (port de Box2D, déterministe et sans dépendance)
Réseau ws (WebSocket sur TCP local) + dgram (UDP broadcast pour la découverte)
Sérialisation msgpackr (binaire, dès le départ)
Tests vitest (logique de shared/ et server/)
Packaging electron-builder (cible Windows en priorité)
UI des menus DOM + CSS simple, pas de framework

Pas de nouvelle dépendance sans justification dans le message de commit.

Commandes

npm install
npm run dev            # Electron en dev (HMR renderer)
npm run server         # serveur headless en Node pur (tests, bots, debug)
                       #   ex. : npm run server -- --name="Test" --round=30 --rounds=2 --pause=5
npm test               # vitest
npm run bench          # benchmarks vitest (budget terrain §5)
npm run bench:node     # même budget mesuré sous Node, sans le biais du lanceur de tests
npm run gen:maps       # régénère les PNG placeholder des cartes intégrées
npm run lint           # eslint + tsc --noEmit
npm run build          # build de prod
npm run dist           # installeur Windows (electron-builder)

3. Architecture

goutermageddon/
├── CLAUDE.md
├── package.json
├── electron.vite.config.ts
├── src/
│   ├── main/        # Process principal Electron : fenêtres, découverte LAN, lancement du serveur
│   ├── preload/     # Pont sécurisé (contextIsolation) : API minimale exposée au renderer
│   ├── renderer/    # Client : rendu PixiJS, inputs, UI, interpolation, sons
│   ├── server/      # Simulation autoritaire : Node pur, AUCUNE dépendance Electron/DOM
│   └── shared/      # Code commun : terrain, matériaux, protocole, constantes, RNG, types
├── assets/
│   ├── maps/        # Une carte par dossier (voir §6)
│   ├── characters/  # Sprites et données des personnages
│   ├── materials/   # Textures des matériaux
│   ├── items/
│   └── audio/
└── tests/

Règles de dépendances (strictes)

  • shared/ ne dépend de rien d'autre que de lui-même : pas de Pixi, pas de DOM, pas d'Electron, pas de fs.
  • server/ dépend de shared/ et de Node.
  • renderer/ dépend de shared/ et de Pixi. Il ne contient aucune logique de jeu autoritaire.
  • main/ orchestre : il crée les fenêtres, lance le serveur dans un utilityProcess quand le joueur héberge, et gère la découverte UDP.

    Modèle réseau

  • L'hôte fait autorité. Le joueur qui héberge lance server/ dans un utilityProcess. Tous les clients, y compris le renderer de l'hôte, se connectent en WebSocket à IP_hôte:47100.

  • Découverte : le serveur émet un broadcast UDP sur le port 47101 toutes les secondes (nom de la partie, carte, nombre de joueurs, version). Les clients écoutent et listent les parties.

  • Simulation : pas fixe à 60 Hz côté serveur. Snapshots envoyés à 30 Hz.

  • Clients : ils envoient leurs inputs à chaque tick (bitmask des touches + direction de visée). Pas de prédiction au départ, car la latence LAN est inférieure à 5 ms. Les autres entités sont interpolées avec environ 50 ms de retard.

  • Le terrain n'est JAMAIS envoyé en entier, sauf au chargement de la carte (par son id et sa seed, chaque client charge les fichiers localement). Ensuite, on n'envoie que des événements de destruction (§5).

  • Tous les messages sont typés dans shared/protocol.ts et sérialisés avec msgpackr. Le protocole est versionné : un client d'une autre version est refusé avec un message clair.

    Aléatoire

Toute la logique aléatoire (objets, Magicien Raté, mutateurs) passe par un RNG à seed (shared/rng.ts, mulberry32 ou équivalent) côté serveur. Math.random() est interdit dans shared/ et server/.


4. Game design

Déroulement d'une manche

  • La cerise apparaît au sommet de la carte (position définie par la carte).
  • Tant qu'un joueur la tient, il marque 1 point par seconde. S'il est touché, il la lâche et elle roule (objet physique).
  • La crème anglaise monte en continu. La vitesse est définie par la carte et accélère dans les 30 dernières secondes.
  • Un joueur qui tombe dans la crème réapparaît 5 secondes plus tard sur une cuillère-catapulte qui le renvoie sur le terrain.
  • Des objets tombent du ciel à intervalles aléatoires :
    • Bougie : explosive, à lancer.
    • Smarties : boost de vitesse pendant 5 secondes.
    • Fouet électrique : secoue tout le terrain, ce qui fait trembler la gelée et détache les blocs fragilisés.
  • Il n'y a pas de barre de vie. Un coup fait reculer et peut assommer brièvement. On meurt uniquement en tombant dans la crème ou sous un bloc qui s'écrase.

    Partie

  • 4 manches. Le score total décide du vainqueur.

  • Rattrapage : avant chaque manche, le dernier du classement choisit la carte (parmi la rotation, §6) et impose un personnage au premier.

  • Écran de fin avec trophées : Le Glouton, Le Kamikaze, Le Promoteur Immobilier, Pas Sa Faute, etc. Ils sont calculés à partir des stats de la partie (server/stats.ts).

    Matériaux

Ils sont définis dans assets/materials/materials.json. L'id 0 correspond au vide.

Id Matériau Résistance Particularité
1 Génoise faible Matériau de base
2 Chocolat forte Résiste aux explosions
3 Chantilly très faible Rebondissante, amortit les chutes
4 Gelée moyenne Glissante, transmet les secousses
5 Biscuit moyenne Cassant : se détache en gros blocs
6 Caramel forte Collant : ralentit les déplacements
7 Laine faible Créée par Mamie Tricot, inflammable
8 Génoise détrempée très faible Créée par Bébé Pompier, s'affaisse lentement
255 Assiette indestructible Point d'ancrage (§5)

Chaque matériau a les champs suivants : hardness, friction, restitution, density, flammable, color, texture, particles (type de miettes), sfx.

Personnages

Ils sont définis dans src/shared/characters/ : une classe par personnage qui étend Character, avec les stats dans assets/characters/<id>/character.json.

Personnage Attaque Spécial Défaut
Castor Glouton Coup de dents Mange le décor en avançant (creuse) Grossit en mangeant, jusqu'à passer à travers le sol
Mamie Tricot Aiguilles Lance des pelotes qui créent ponts et filets (matériau Laine) Lente, et ses constructions brûlent
Pyromanchot Pétards Glissade sur le ventre avec explosion à l'arrivée 1 pétard sur 3 lui explose dessus
Gégé le Ver Coup de tête Nage dans le terrain et surgit sous les pieds Lent et vulnérable à découvert
Bébé Pompier Jet d'eau (repousse) Détrempe le terrain (Génoise → Génoise détrempée) Le recul le propulse en arrière
Captain Prout Coup de cape Jetpack par petites poussées, laisse un nuage toxique (vision floue + toux) Chaque poussée s'entend
Le Chat Coup de patte Pousse blocs, objets et joueurs assommés du bord Se lèche la patte au hasard (1 s)
Magicien Raté Baguette Sort aléatoire : terrain en gelée, échange de position, lapin géant, « pfft » Tirage pondéré : plus il est en tête, plus ses sorts ratent

API commune : onAttack(), onSpecial(), onTick(dt), onHit(), et les stats (speed, jump, mass, size, cooldowns).


5. Terrain destructible (cœur technique)

Représentation

  • Le terrain est un bitmap de matériaux : Uint8Array de taille width × height (une carte typique fait 2048 × 1024 px).
  • Il est découpé en morceaux de 64 × 64 px, chacun avec un drapeau dirty.
  • Côté serveur : chaque morceau modifié reconstruit ses colliders planck (marching squares, puis simplification Douglas-Peucker, puis chain shapes statiques).
  • Côté client : chaque morceau modifié reconstruit sa texture Pixi. La texture du matériau est échantillonnée en coordonnées monde, et une passe de contour donne l'effet feutre.

    Opérations

Toutes les modifications passent par des opérations déterministes en arithmétique entière définies dans shared/terrain/ops.ts :

type TerrainOp =
  | { kind: 'carve';   x: number; y: number; r: number; power: number }       // explosion, morsure
  | { kind: 'fill';    x: number; y: number; r: number; material: number }    // laine de Mamie
  | { kind: 'convert'; x: number; y: number; r: number; from: number; to: number } // détrempage
  • power est comparé à la hardness de chaque matériau. Le chocolat résiste à un petit pétard.
  • Le serveur applique l'op, puis la diffuse telle quelle avec un numéro de séquence. Chaque client applique les ops dans l'ordre et obtient un bitmap identique au bit près.
  • Ne jamais introduire de flottant dans l'application d'une op.

    Blocs qui se détachent

  • Règle : tout pixel solide qui n'est plus relié (4-connexité) à un pixel Assiette (ancre) tombe.

  • Après chaque op, le serveur lance un flood-fill à partir des pixels voisins de la zone modifiée pour trouver les composantes non ancrées.

  • Chaque composante détachée est retirée du bitmap et devient un corps dynamique planck (FallingChunk) avec son masque de pixels. Le serveur diffuse un événement detach { id, x, y, w, h, mask } (masque compressé en RLE). Les composantes minuscules (MIN_CHUNK_PIXELS) s'émiettent (debris) sans corps physique.

  • Les clients retirent les mêmes pixels et affichent le bloc comme un sprite. La position et la rotation du bloc arrivent dans les snapshots.

  • Quand le bloc s'immobilise (vitesse quasi nulle pendant 0,5 s), le serveur le rastérise dans le bitmap et diffuse merge { id, x, y, w, h, patch }. Le patch est le résultat pixel par pixel, pour qu'aucun client n'ait à recalculer la rotation. Il comble jusqu'à CHUNK_GLUE_PX pixels sous le bloc pour le raccrocher au sol. Si le résultat ne serait pas ancré (bloc posé sur un joueur ou sur un autre bloc), la réintégration est retentée plus tard.

  • Les ops, detach et merge forment un seul flux d'événements séquencés (shared/terrain/events.ts) : c'est ce flux, rejoué dans l'ordre, qui garantit un bitmap identique au bit près sur tous les clients (vérifié par les tests).

  • Côté physique, un bloc est approché par des boîtes alignées sur une grille (Box2D ne fait pas collisionner deux chaînes entre elles).

  • Un bloc qui tombe et percute un joueur avec assez d'énergie l'écrase (élimination). Les blocs mous (laine, chantilly : densité < CRUSH_MIN_DENSITY) n'écrasent pas. Un bloc qui tombe dans la crème disparaît.

    Budget de performance

  • Une op avec flood-fill et régénération des colliders doit tenir en 2 ms côté serveur. Si ce n'est pas le cas, on optimise : flood-fill borné, bitsets, cache des composantes.

  • 60 FPS sur un PC milieu de gamme avec 8 joueurs et 20 blocs en chute.

  • Écrire un benchmark vitest pour les ops et le flood-fill dès la première étape.

  • Mesurer sous Node (npm run bench:node). Les benchmarks vitest sont pessimistes sur les boucles chaudes : le lanceur de tests transforme chaque lecture d'une constante importée en accès de propriété. Dans les modules de terrain, les boucles chaudes lisent donc des copies locales des constantes (const EMPTY = MATERIAL_EMPTY).

  • État mesuré (Node, JIT chaud) :

    • un coup ordinaire (op, recherche de détachement, colliders, pas physique) : environ 0,1 ms en moyenne, ~1 ms au p99 ;
    • un coup unique qui détache un étage entier (~90 000 px) : ~5 ms pour tout le tick, dont ~2 ms de recherche, ~1 ms de détachement et ~1,5 ms de colliders.

Ce cas extrême reste au-dessus de 2 ms. Il faudrait étaler le travail sur plusieurs ticks, ou ajouter un cache de connectivité entre morceaux.

6. Cartes

Une carte = un dossier dans assets/maps/<id>/. Ajouter une carte ne demande aucun code.

assets/maps/gateau-etages/
├── map.json        # métadonnées et règles
├── terrain.png     # masque des matériaux (une couleur exacte = un matériau)
├── background.png  # décor de fond (non destructible, parallaxe possible)
└── thumb.png       # vignette pour le menu

map.json

{
  "id": "gateau-etages",
  "name": "Le Gâteau à Étages",
  "size": [2048, 1024],
  "materialPalette": {           // couleur du PNG -> id de matériau
    "#F2C46D": 1, "#5A3A22": 2, "#FFFFFF": 3, "#D93A5B": 4,
    "#C8964B": 5, "#000000": 0, "#8899AA": 255
  },
  "spawns": [[300, 200], [1700, 200], [600, 400], [1400, 400]],
  "cherrySpawn": [1024, 80],
  "custard": { "startY": 1000, "speed": 2.5, "finalRushSpeed": 8 },
  "items": { "interval": [6, 12], "weights": { "candle": 3, "smarties": 2, "whisk": 1 } },
  "music": "birthday-flute",
  "modifiers": []                // règles spéciales de la carte (voir ci-dessous)
}

Validation

shared/maps/loader.ts valide chaque carte au chargement :

  • chaque couleur du PNG appartient à la palette ;
  • au moins une zone d'Assiette existe ;
  • les points d'apparition sont dans le vide et au-dessus d'un sol. Une carte invalide est rejetée avec un message explicite. On ne la charge jamais à moitié.

Cartes prévues

Id Nom Particularité
gateau-etages Le Gâteau à Étages Carte de référence : étages de génoise et chocolat, cerise au sommet
mille-feuille Le Mille-Feuille Couches horizontales alternées dures et molles : on creuse vers le bas facilement, sur le côté difficilement
roule Le Roulé Spirale de génoise et de confiture (gelée) : on tourne en rond, ça s'effondre en anneaux
piece-montee La Pièce Montée Pyramide de choux reliés par du caramel collant. Retirer un chou fait tomber tous ceux du dessus
tarte-fruits La Tarte aux Fruits Fond en biscuit cassant et fruits en gelée. Modificateur : le fond se fissure au hasard
crepes La Pile de Crêpes Couches très fines superposées, et une pile qui penche. Modificateur : vent latéral
  • modifiers est une liste d'ids de règles spéciales implémentées dans server/modifiers/ (par exemple random-cracks, wind). Une carte peut en combiner plusieurs.
  • Rotation : la liste des cartes jouables est configurée par l'hôte dans le lobby (toutes, sélection, ou aléatoire). Le dernier du classement choisit parmi cette rotation.
  • Plus tard : un éditeur de cartes simple dans l'app (peinture des matériaux au pinceau, placement des spawns), qui exporte au format ci-dessus. ---

7. Direction artistique et son

  • Style dessin d'enfant amélioré : gros contours au feutre (noir, 3 à 4 px), couleurs saturées, textures de crayon de couleur sur les personnages.
  • Un terrain appétissant : chaque matériau a sa texture en motif répété et ses propres particules (miettes, copeaux, éclaboussures).
  • Personnages : ronds, grosse tête, pattes courtes, yeux très expressifs. Squash and stretch sur chaque saut, impact et atterrissage.
  • Effets : secousse d'écran proportionnelle à l'énergie, freeze-frame de 50 ms sur les gros impacts, nombreuses particules.
  • Son : mastication, « splotch », « crrrac » de biscuit, prouts variés. Musique : chanson d'anniversaire à la flûte à bec qui accélère avec la montée de la crème.
  • Placeholders autorisés pour le développement : formes géométriques colorées. La logique ne dépend jamais des assets finaux. ---

8. Conventions de code

  • Identifiants, fichiers et commits en anglais. Textes affichés en français, centralisés dans renderer/i18n/fr.ts.
  • TypeScript strict: true, pas de any sans commentaire justificatif.
  • Fonctions pures dans shared/ dès que possible : c'est ce qui se teste.
  • Toute logique de shared/terrain/ et shared/maps/ est couverte par des tests vitest.
  • Pas de valeurs magiques : elles vont dans shared/constants.ts ou dans les données (JSON).
  • Commits petits et ciblés. Format : feat(terrain): ..., fix(net): ..., chore: ....
  • Electron : contextIsolation: true, nodeIntegration: false. Le preload expose une API minimale et typée (window.lan.discover(), window.lan.host(), etc.). ---

9. Feuille de route

Chaque étape doit être jouable avant de passer à la suivante.

M1 : Terrain destructible en solo

  • Structure du projet (electron-vite, TS, Pixi, planck, vitest).
  • Bitmap des matériaux, morceaux de 64 px, rendu Pixi, colliders par morceau.
  • Chargement de la carte gateau-etages (avec un PNG de test).
  • Un personnage générique qui court, saute et creuse au clic (carve).
  • Tests : ops déterministes, marching squares, benchmark.
  • Terminé quand : on peut creuser le gâteau à 60 FPS et c'est agréable.
  • Fait en avance (choix de l'équipe) : le serveur tourne déjà dans un utilityProcess avec WebSocket, msgpackr et interpolation (le renderer ne doit pas porter la simulation, §3). Le Castor Glouton (M5) est jouable, avec un écran titre minimal (pseudo et choix du personnage), une première passe de DA procédurale (textures de matériaux, contour feutre, intérieur des trous, persos animés, onomatopées) et un décor de cuisine.

    M2 : Blocs qui tombent

  • Flood-fill vers l'ancre Assiette, FallingChunk, réintégration du bloc immobile, écrasement.

  • Terminé quand : creuser sous un étage le fait tomber et l'étage se pose proprement.

  • État : implémenté et testé (détachement, chute, réintégration, écrasement, cohérence serveur/clients). Recherche en trois phases (largeur, profondeur vers le bas, puis remplissage par lignes), une seule recherche par tick, mémoire de travail partagée : voir §5 pour les mesures. Le décrochage d'un étage entier reste à ~5 ms par tick, au-dessus des 2 ms.

    M3 : Multijoueur LAN

  • Serveur dans un utilityProcess, mode headless, WebSocket, protocole msgpackr.

  • Découverte UDP, lobby (liste des parties, choix du pseudo, prêt).

  • Inputs vers le serveur, snapshots et ops vers les clients, interpolation.

  • Test de cohérence : hash du bitmap comparé entre serveur et clients toutes les 5 secondes, avec un log d'erreur en cas d'écart.

  • Terminé quand : 4 PC en LAN creusent le même gâteau sans aucune différence.

  • État : implémenté. Le serveur s'annonce sur chaque sous-réseau et en local. L'écran « Les goûters du coin » liste les parties, permet d'en créer une ou d'en rejoindre une par adresse. Pendant l'échauffement, chacun se déclare prêt ; quand tout le monde l'est, un compte à rebours de 3 s lance la partie sur une carte neuve. Le hash est vérifié toutes les 5 s et un écart est loggé côté serveur. Testé avec plusieurs clients en tests automatiques et avec 2 instances sur le même PC. Reste à valider sur 4 vrais PC (pare-feu Windows à autoriser au premier lancement).

    M4 : Règles de jeu

  • Cerise, score, crème qui monte, cuillère de réapparition, objets, manches, partie, écran de score.

  • Terminé quand : une partie complète se joue du lobby aux trophées.

  • État : implémenté.

    • Échauffement : la carte sert de salle d'attente. Pas de chrono, de cerise ni d'objets, et la crème ne monte pas, mais on peut y tomber.
    • Mort : tomber dans la crème ou être écrasé par un bloc. On revient 5 s plus tard par la cuillère, lancée depuis une colonne de vide vers le point d'apparition le plus proche.
    • Objets : Smarties et fouet agissent au ramassage. La bougie se garde et se lance avec F ; son explosion touche tout le monde, le lanceur compris.
    • Trophées (server/stats.ts) : Le Glouton, Le Kamikaze, Le Promoteur Immobilier, Pas Sa Faute, L'Artificier, Le Cueilleur.
    • Reste en M7 : le rattrapage (le dernier choisit la carte et impose un personnage).

      M5 : Trois personnages

  • Castor Glouton (déjà jouable depuis M1), Mamie Tricot, Pyromanchot (le trio creuser / construire / exploser).

  • Écran de sélection des personnages.

  • État : implémenté.

    • Mamie Tricot : ses aiguilles assomment longtemps. Sa pelote reste reliée par un fil au sol sous ses pieds (le fil est dessiné en vol). Là où elle touche le décor, le fil est posé tendu en Laine, avec une boule collée à l'impact : un pont tenu par ses deux bouts. Une pelote qui ne touche rien se défait. Elle traverse les joueurs, et aucune laine n'est jamais posée dans un joueur. Toute explosion brûle la laine dans WOOL_BURN_RADIUS_FACTOR fois son rayon.
    • Pyromanchot : ses pétards explosent au contact, sauf un sur trois qui lui explose dessus. Sa glissade sur le ventre se termine par une explosion qui l'épargne.
    • Objets lancés : bougie, pétards et pelotes partagent un même système de projectiles côté serveur.
    • Écran de sélection : une fiche par personnage (attaque, spécial, défaut) avec des barres Vitesse / Saut / Poids lues dans character.json.

      M6 : Plusieurs cartes

  • Loader validé, rotation configurable dans le lobby, choix par le dernier, modifiers.

  • Cartes mille-feuille et piece-montee en plus de gateau-etages.

  • État : implémenté.

    • Validation : au démarrage, le serveur valide chaque dossier de assets/maps/, modificateurs inconnus compris. Une carte invalide est écartée avec un message explicite dans le journal.
    • Rotation : l'hôte (👑, le premier arrivé) la règle dans le panneau d'échauffement : toutes dans l'ordre, au hasard, ou une sélection. L'échauffement se fait sur la première carte de la rotation.
    • Choix par le dernier : pendant l'écran de fin de manche, le dernier choisit la carte suivante parmi la rotation (logique dans server/rotation.ts). Sans choix, la rotation décide.
    • Modificateurs (server/modifiers/) : wind (rafales latérales, indicateur dans le bandeau) et random-cracks (fissures en zigzag dans le biscuit). Ils ne jouent que pendant les manches. Pour les tester dès maintenant, random-cracks est sur le Mille-Feuille et wind sur la Pièce Montée ; à redistribuer sur la Tarte et les Crêpes en M7.
    • Cartes : générées par npm run gen:maps. Sur la Pièce Montée, chaque chou n'est collé qu'à celui du dessous, donc en retirer un fait tomber toute la colonne au-dessus.

      M7 et suite

  • Les 5 autres personnages, les 3 autres cartes, le rattrapage complet, les trophées, la DA finale, l'audio, l'éditeur de cartes, l'installeur Windows.

10. Pour Claude : comment travailler sur ce projet

  • Commence toujours par identifier l'étape en cours (§9) et ne travaille pas sur une étape future sans qu'on te le demande.
  • Avant de toucher au terrain ou au réseau, relis le §5 et le §3 : le déterminisme des ops et l'autorité du serveur sont les deux invariants qui ne doivent jamais casser.
  • Après une modification de shared/ ou server/, lance npm test et npm run lint.
  • Si une demande contredit ce document, signale-le et propose de mettre ce fichier à jour plutôt que de diverger silencieusement.
  • En cas de doute sur une question de game feel (vitesse, gravité, puissance), mets la valeur dans les constantes ou les données et indique-la pour qu'on la règle en jouant.