Consolidate plans into docs/PLAN.md and document dev loop
Delete autonomous_plan.md (fully shipped), PLAN_playtest.md (all P1-P7 done), PLAN_missions.md and PLAN_leveldesign.md (partial — engine done, UI polish + 3 final missions + recurring demands + transformer visuals remain). The surviving TODO list lives in docs/PLAN.md. Also add the 6-step dev loop to CLAUDE.md (take next topic → implement → tests → UI test → docs → commit).
This commit is contained in:
parent
eba81400a8
commit
bd1763f372
7 changed files with 106 additions and 721 deletions
20
CLAUDE.md
20
CLAUDE.md
|
|
@ -29,7 +29,25 @@ Tout `Control` (ColorRect, Label…) a `MouseFilter = Stop` par defaut. Quand un
|
|||
|
||||
### Plans
|
||||
|
||||
Les fichiers de plan doivent etre rediges a la racine du workspace (ex: `/workspace/PLAN_juice.md`), **pas** dans `.claude/plans/` car ce dossier a une taille limitee.
|
||||
Les plans vivants (travail restant, features a faire) vont dans
|
||||
[`docs/PLAN.md`](docs/PLAN.md). Pour un brouillon ad-hoc, ecrire a la racine
|
||||
du workspace (ex: `/workspace/PLAN_<sujet>.md`), **pas** dans
|
||||
`.claude/plans/` (taille limitee). Une fois implemente, supprimer le
|
||||
fichier ; si partiel, consolider le restant dans `docs/PLAN.md`.
|
||||
|
||||
### Boucle de developpement
|
||||
|
||||
Pour chaque sujet pris dans `docs/PLAN.md` :
|
||||
|
||||
1. **Prendre le sujet suivant** dans le plan (ordre de priorite).
|
||||
2. **Implementer** (moteur + presentation selon le cas).
|
||||
3. **Ajouter des tests unitaires** si applicable (`chessistics-tests/`).
|
||||
4. **Tester l'UI/UX** de la fonctionnalite dans le jeu si applicable
|
||||
(harness + quick save/load pour reprendre un checkpoint).
|
||||
5. **Mettre a jour la documentation** (README, CLAUDE.md, GDD) si
|
||||
necessaire et **retirer le sujet du plan** (ou annoter ce qui reste).
|
||||
6. **Commit** (un commit par sujet, message en anglais, sans co-author
|
||||
Claude).
|
||||
|
||||
## Harnais d'automatisation (Claude peut jouer tout seul)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
# Plan : Level Design - Batiments de Transformation
|
||||
|
||||
## Concept
|
||||
|
||||
Les batiments de **transformation** consomment une ressource en entree et produisent une ressource differente en sortie. Le joueur doit construire des chaines logistiques multi-etapes : extraction → transformation → livraison.
|
||||
|
||||
Exemple : Scierie (produit bois) → Forge (consomme bois, produit outils) → Caserne (demande outils).
|
||||
|
||||
## Modele Engine
|
||||
|
||||
### Nouveau type de cellule : Transformer
|
||||
|
||||
```
|
||||
CellType.Transformer
|
||||
```
|
||||
|
||||
Un `TransformerDef` combine une demande (input) et une production (output) :
|
||||
|
||||
```csharp
|
||||
public record TransformerDef(
|
||||
Coords Position,
|
||||
string Name,
|
||||
CargoType InputCargo, // ce qu'il consomme
|
||||
int InputRequired, // nb d'unites avant conversion
|
||||
CargoType OutputCargo, // ce qu'il produit
|
||||
int OutputAmount // nb d'unites produites par cycle
|
||||
);
|
||||
```
|
||||
|
||||
### Logique de production
|
||||
|
||||
Chaque tour, le `TransferResolver` livre du cargo au transformateur comme a une demande normale. Quand le buffer d'entree atteint `InputRequired`, le transformateur :
|
||||
1. Vide son buffer d'entree
|
||||
2. Remplit son buffer de sortie avec `OutputAmount` unites de `OutputCargo`
|
||||
3. Le buffer de sortie est distribue aux pieces adjacentes (comme une production classique)
|
||||
|
||||
Cela cree un rythme : accumulation → conversion → distribution.
|
||||
|
||||
### Changements au TerrainPatch
|
||||
|
||||
Ajouter le type `"transformer"` dans le JSON :
|
||||
|
||||
```json
|
||||
{
|
||||
"col": 3, "row": 2,
|
||||
"type": "transformer",
|
||||
"transformer": {
|
||||
"name": "Forge",
|
||||
"inputCargo": "wood",
|
||||
"inputRequired": 2,
|
||||
"outputCargo": "tools",
|
||||
"outputAmount": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Nouveaux types de cargo
|
||||
|
||||
Ajouter au `CargoType` enum :
|
||||
- `Tools` (outils) — bois transforme
|
||||
- `Arms` (armes) — pierre transformee
|
||||
- `Gold` (or) — ressource rare de fin de campagne
|
||||
|
||||
## Evolution de la campagne (10 missions)
|
||||
|
||||
### Mission 1 : Premier Convoi (4x4)
|
||||
- **Deverrouille** : Pion
|
||||
- **Batiments** : Scierie → Depot Royal
|
||||
- **Objectif** : livrer 3 bois — apprendre le placement de base
|
||||
|
||||
### Mission 2 : Forger les Tours (6x6)
|
||||
- **Deverrouille** : Tour
|
||||
- **Batiments** : +Carriere, +Caserne (bois), +Forge (pierre)
|
||||
- **Objectif** : livrer bois a la caserne ET pierre a la forge
|
||||
- **Defi** : gerer deux routes independantes
|
||||
|
||||
### Mission 3 : Le Col (6x6)
|
||||
- **Deverrouille** : Cavalier
|
||||
- **Batiments** : mur bloquant + Depot (bois)
|
||||
- **Objectif** : traverser les murs avec des cavaliers
|
||||
- **Les demands precedentes restent actives**
|
||||
|
||||
### Mission 4 : Le Carrefour (8x8)
|
||||
- **Deverrouille** : Fou
|
||||
- **Batiments** : +Chateau (bois), +Forge Royale (pierre), murs
|
||||
- **Objectif** : routes diagonales avec les fous
|
||||
|
||||
### Mission 5 : La Forge (8x8) -- TRANSFORMATION
|
||||
- **Deverrouille** : aucun (nouveau type de cargo : Outils)
|
||||
- **Batiments** : Forge *transformee* en transformateur (bois → outils)
|
||||
- **Objectif** : livrer des outils a un nouveau batiment (Armurerie)
|
||||
- **Defi** : la route bois existante doit continuer, PLUS une branche vers la forge qui produit des outils
|
||||
|
||||
### Mission 6 : La Dame Blanche (10x10)
|
||||
- **Deverrouille** : Dame
|
||||
- **Batiments** : +Scierie Nord, +Grand Chantier (bois), +Arsenal (pierre)
|
||||
- **Objectif** : routes longue distance avec la dame
|
||||
|
||||
### Mission 7 : L'Armurerie (10x10) -- TRANSFORMATION
|
||||
- **Nouveau cargo** : Armes
|
||||
- **Batiments** : Armurerie (transforme pierre → armes)
|
||||
- **Objectif** : livrer des armes a une Garnison
|
||||
|
||||
### Mission 8 : Le Comptoir (12x12)
|
||||
- **Batiments** : Comptoir (transforme outils → or)
|
||||
- **Objectif** : livrer de l'or au Tresor Royal
|
||||
- **Defi** : chaine a 3 etapes : bois → outils → or
|
||||
|
||||
### Mission 9 : L'Expansion Finale (14x14)
|
||||
- **Batiments** : multiples transformateurs, murs complexes
|
||||
- **Objectif** : maintenir toutes les chaines tout en s'etendant
|
||||
- **Defi** : gestion de la congestion (risque de collisions)
|
||||
|
||||
### Mission 10 : Le Couronnement (14x14)
|
||||
- **Batiments** : Cathedrale (demande or + armes + outils)
|
||||
- **Objectif** : livrer les 3 types de cargo transforme
|
||||
- **Defi** : orchestrer l'ensemble des chaines logistiques simultanement
|
||||
|
||||
## Demands recurrentes (futur)
|
||||
|
||||
Pour que le joueur doive "preserver ses automatisations", les demands pourraient devenir recurrentes :
|
||||
- Un batiment de demande **consomme** N unites par tour
|
||||
- S'il n'est plus approvisionne, il passe en etat "en penurie"
|
||||
- Condition de mission : **aucun** batiment en penurie pendant X tours consecutifs
|
||||
|
||||
Cela force le joueur a maintenir ses routes existantes quand le terrain s'agrandit, au lieu de tout reconstruire.
|
||||
|
||||
## Implementation par phases
|
||||
|
||||
### Phase 1 : Modele Transformer
|
||||
- Ajouter `CellType.Transformer` et `TransformerDef`
|
||||
- Ajouter `TransformerState` avec buffers input/output
|
||||
- Integrer dans `BoardState`
|
||||
|
||||
### Phase 2 : Logique de conversion
|
||||
- Modifier `TurnExecutor` : sous-phase "transformation" entre production et transferts
|
||||
- Le transformateur agit comme une demande (recoit) ET une production (emet)
|
||||
|
||||
### Phase 3 : Nouveaux cargos
|
||||
- Ajouter `Tools`, `Arms`, `Gold` a `CargoType`
|
||||
- Couleurs visuelles pour chaque cargo
|
||||
- Mise a jour du `CampaignLoader` pour parser les transformateurs
|
||||
|
||||
### Phase 4 : Visuels
|
||||
- Couleur de cellule pour les transformateurs (ex: orange cuivre)
|
||||
- Animation de conversion (flash input → flash output)
|
||||
- Icones de cargo dans les pieces
|
||||
|
||||
### Phase 5 : Missions 5-10
|
||||
- Ecrire les donnees JSON des missions 5 a 10
|
||||
- Tester la solvabilite de chaque mission
|
||||
- Equilibrer les quantites (input/output ratios)
|
||||
|
||||
### Phase 6 (optionnel) : Demands recurrentes
|
||||
- Modifier `DemandState` pour tracker la consommation par tour
|
||||
- Ajouter un flag "en penurie"
|
||||
- Condition de victoire : pas de penurie pendant N tours
|
||||
371
PLAN_missions.md
371
PLAN_missions.md
|
|
@ -1,371 +0,0 @@
|
|||
# Plan : Puzzle → Logistique incrémentale (Missions)
|
||||
|
||||
## Vision
|
||||
|
||||
Remplacer la série de niveaux indépendants par un plateau persistant où les **missions** s'enchaînent. Chaque mission complétée débloque une extension du terrain (nouvelles cases, productions, demandes, murs) sans casser la solution en place. Le jeu devient un jeu de **logistique** et non de **puzzle**.
|
||||
|
||||
### Fil narratif (lore GDD §12)
|
||||
|
||||
Les pions manquent d'un roi. Mission après mission, ils construisent des pièces de plus en plus puissantes pour atteindre des ressources plus lointaines, jusqu'à fabriquer le roi — qui exécute tout le monde.
|
||||
|
||||
Chaque mission débloque de nouveaux **types de pièces** : on commence avec les Pions seuls, puis les Tours, les Fous, les Cavaliers, et enfin la Dame. Le lore justifie la progression mécanique.
|
||||
|
||||
---
|
||||
|
||||
## Concepts clés
|
||||
|
||||
| Ancien | Nouveau |
|
||||
|--------|---------|
|
||||
| Level (plateau isolé) | **Campaign** : un plateau persistant avec N missions |
|
||||
| Victoire → charger le niveau suivant | Victoire → le plateau **s'étend**, la mission suivante se débloque |
|
||||
| Stock fixe par niveau | Stock cumulé : chaque mission donne un budget additionnel |
|
||||
| Reset complet entre niveaux | Les pièces posées et les productions **restent actives** |
|
||||
| Stop (retour en Edit) | **Supprimé** — la simulation tourne en continu, le joueur édite en temps réel |
|
||||
| Toutes pièces disponibles dès le début | Pièces **débloquées progressivement** par mission (lore) |
|
||||
| Pièces à niveau fixe | **Niveaux de pièces** (I, II, III) débloqués par la progression |
|
||||
|
||||
### Règle d'or : pas de régression
|
||||
Les murs ne peuvent apparaître que sur les **nouvelles cases** débloquées par la mission. Le terrain existant ne change jamais de façon bloquante.
|
||||
|
||||
---
|
||||
|
||||
## Changements de game design
|
||||
|
||||
### Suppression du Stop — édition en temps réel
|
||||
|
||||
Il n'y a plus de phase Edit séparée. La simulation tourne en continu. Le joueur peut :
|
||||
- **Pause** (barre espace) : l'animation termine le tour en cours puis se fige.
|
||||
- **Placer/retirer des pièces** à tout moment (en pause ou pendant que ça tourne).
|
||||
- **Réoptimiser** les missions précédentes — essentiel pour un jeu de logistique.
|
||||
|
||||
Conséquence engine : `PlacePieceCommand` et `RemovePieceCommand` fonctionnent quel que soit le `SimPhase` (plus de guard `phase == Edit`).
|
||||
|
||||
### Auto-pause pendant le placement
|
||||
|
||||
Sélectionner un type de pièce à placer met automatiquement la simulation en pause. La pause est levée quand le placement est confirmé (clic arrivée) ou annulé (Échap). Le joueur ne pense pas "pause" — il pense "placement". C'est transparent.
|
||||
|
||||
### Retirer une pièce — touche Suppr
|
||||
|
||||
Le clic droit est réservé au pan de caméra. Pour retirer une pièce :
|
||||
- Clic gauche sur la pièce → sélection + panneau de détail
|
||||
- **Touche Suppr** ou **bouton [Retirer]** dans le panneau de détail → retourne au stock
|
||||
|
||||
### Collisions → retour au stock + pause auto
|
||||
|
||||
Quand une pièce est détruite par collision, elle retourne dans le stock du joueur (au lieu d'être perdue). Cela évite un soft-lock où le joueur n'a plus assez de pièces pour résoudre la mission.
|
||||
|
||||
La simulation se met en **pause automatique** sur collision :
|
||||
- La caméra effectue un **pan + zoom** vers la zone de collision.
|
||||
- Une **notification** apparaît dans un coin de l'écran pour expliciter ce qui s'est passé (ex: "Tour II détruite par Dame — retournée au stock").
|
||||
- Le joueur peut reprendre la simulation (Espace) après avoir pris connaissance de la situation.
|
||||
|
||||
### Undo (Ctrl+Z)
|
||||
|
||||
Annule le dernier placement ou retrait de pièce. L'architecture event-sourcing de l'engine rend l'implémentation naturelle : on conserve un historique de commandes et on les rejoue sans la dernière. Essentiel pour l'itération rapide sur un réseau en temps réel.
|
||||
|
||||
### Productions — pas d'intervalle, production variable
|
||||
|
||||
Chaque bâtiment de production a un champ `amount` (1 à 4) : il produit ce nombre de cargaisons **à chaque tour**. Le buffer max = `amount`. La surproduction non récupérée est écrasée au tour suivant (perdue). Pas de champ `interval`.
|
||||
|
||||
### Niveaux de pièces (I, II, III)
|
||||
|
||||
Chaque type de pièce a un niveau qui affecte :
|
||||
- **Portée** : Tour I = 1 case, Tour II = 2 cases, Tour III = 3 cases
|
||||
- **Priorité de transfert** : à statut social égal, le niveau départage
|
||||
- **Collisions** : à statut égal, le niveau supérieur survit
|
||||
|
||||
Les missions débloquent des niveaux supérieurs progressivement.
|
||||
|
||||
### Drag & drop de pièces
|
||||
|
||||
Le joueur peut glisser une pièce déjà placée pour déplacer son point de départ (les arrivées possibles se recalculent). Quality-of-life essentiel quand on itère sur un réseau en temps réel.
|
||||
|
||||
---
|
||||
|
||||
## Architecture (Black-Box Sim)
|
||||
|
||||
### Nouveaux modèles (engine)
|
||||
|
||||
```
|
||||
CampaignDef # remplace la liste de LevelDef
|
||||
├── name: string
|
||||
├── initialWidth / initialHeight
|
||||
├── missions: MissionDef[]
|
||||
|
||||
MissionDef
|
||||
├── id: int
|
||||
├── name: string
|
||||
├── description: string
|
||||
├── terrainPatch: TerrainPatch # extension du plateau
|
||||
├── stock: PieceStock[] # stock additionnel offert
|
||||
├── demands: DemandDef[] # nouveaux objectifs
|
||||
├── unlockedPieces: PieceKind[] # types de pièces débloqués par cette mission
|
||||
├── unlockedLevels: PieceUpgrade[] # niveaux de pièces débloqués
|
||||
│
|
||||
PieceUpgrade
|
||||
├── kind: PieceKind
|
||||
├── level: int # ex: Tour niveau 2
|
||||
|
||||
TerrainPatch
|
||||
├── newWidth / newHeight # nouvelle taille du plateau (>= précédente)
|
||||
├── cells: PatchCell[] # cases ajoutées/modifiées
|
||||
│ ├── col, row
|
||||
│ └── type: Empty | Wall | Production(def) | Demand(def)
|
||||
```
|
||||
|
||||
### Nouveau state (engine)
|
||||
|
||||
```
|
||||
CampaignState
|
||||
├── campaignDef: CampaignDef
|
||||
├── currentMissionIndex: int
|
||||
├── completedMissions: int[]
|
||||
├── boardState: BoardState # persistant entre missions
|
||||
├── availablePieces: Set<(PieceKind, int level)> # pièces débloquées
|
||||
```
|
||||
|
||||
`BoardState` évolue :
|
||||
- Ajout d'un champ `missionIndex` sur chaque `DemandState` pour savoir à quelle mission appartient une demande.
|
||||
- Les demandes complétées restent dans le state (marquées `Satisfied`), les nouvelles s'ajoutent.
|
||||
- `ProductionDef` reçoit un champ `amount` (1-4) au lieu de `interval`.
|
||||
|
||||
### Modification de SimPhase
|
||||
|
||||
```
|
||||
Running ←→ Paused
|
||||
↓ ↓
|
||||
MissionComplete (la sim continue mais un overlay félicite)
|
||||
↓
|
||||
AdvanceMission → Running (terrain étendu, nouvelles demandes)
|
||||
```
|
||||
|
||||
Phases supprimées : `Edit`, `Victory`, `Defeat`.
|
||||
- Plus de `Edit` : le joueur place des pièces à tout moment.
|
||||
- Plus de `Victory` séparé : `MissionComplete` pour chaque mission, la dernière affiche un écran de fin.
|
||||
- Plus de `Defeat` : pas de deadline punitive (jeu de logistique, pas de puzzle).
|
||||
|
||||
> **Note** : la suppression des deadlines est une conséquence de la suppression du Stop. Sans pouvoir reset, un deadline qui expire sans recours serait frustrant. Les demandes ont un `amount` cible mais pas de date limite. Les métriques (tours pour compléter) servent de score optionnel.
|
||||
|
||||
### Nouvelles Commands
|
||||
|
||||
| Command | Effet |
|
||||
|---------|-------|
|
||||
| `LoadCampaignCommand` | Charge la campagne, initialise le plateau avec la mission 0, démarre Running |
|
||||
| `AdvanceMissionCommand` | Applique le `TerrainPatch` de la mission suivante, ajoute stock/demandes/pièces |
|
||||
| `MovePieceCommand` | Drag & drop : déplace une pièce existante (nouveau start + end) |
|
||||
|
||||
Commands modifiées :
|
||||
| Command | Changement |
|
||||
|---------|------------|
|
||||
| `PlacePieceCommand` | Fonctionne en Running et Paused (plus de guard Edit) |
|
||||
| `RemovePieceCommand` | Idem |
|
||||
| `PauseSimulationCommand` | Termine le tour en cours avant de figer |
|
||||
|
||||
Commands supprimées :
|
||||
| Command | Raison |
|
||||
|---------|--------|
|
||||
| `StopSimulationCommand` | Plus de Stop |
|
||||
| `StartSimulationCommand` | La sim démarre automatiquement au load |
|
||||
| `ResetLevelCommand` | Plus de reset complet |
|
||||
|
||||
### Nouveaux Events
|
||||
|
||||
| Event | Données |
|
||||
|-------|---------|
|
||||
| `MissionCompleteEvent` | missionIndex |
|
||||
| `MissionStartedEvent` | missionIndex, terrainPatch appliqué |
|
||||
| `TerrainExpandedEvent` | nouvelles cases, nouvelle taille |
|
||||
| `PieceUnlockedEvent` | kind, level |
|
||||
| `PieceMovedByPlayerEvent` | pieceId, oldStart, oldEnd, newStart, newEnd |
|
||||
| `PieceReturnedToStockEvent` | pieceId, kind, reason (collision) |
|
||||
|
||||
### Modifications du flow de victoire
|
||||
|
||||
1. `VictoryChecker` → renommé `MissionChecker`.
|
||||
2. Vérifie les demandes **de la mission courante** uniquement.
|
||||
3. Si toutes satisfaites → `MissionCompleteEvent`.
|
||||
4. La sim continue de tourner (les pièces continuent de produire/livrer).
|
||||
5. Le joueur déclenche `AdvanceMissionCommand` quand il est prêt.
|
||||
6. Dernière mission complétée → écran de fin de campagne.
|
||||
|
||||
### Collisions — retour au stock + pause auto + caméra
|
||||
|
||||
`CollisionResolver` modifié :
|
||||
- La pièce détruite émet `PieceReturnedToStockEvent` au lieu de `PieceDestroyedEvent`.
|
||||
- Le stock dans `BoardState` est incrémenté.
|
||||
- La pièce est retirée du plateau mais le joueur peut la replacer.
|
||||
|
||||
Côté présentation (Godot) :
|
||||
- La simulation se met en **pause automatique** à la détection d'une collision.
|
||||
- La caméra effectue un **pan + zoom** vers la zone de collision.
|
||||
- Une **notification** apparaît dans un coin de l'écran (ex: "Tour II détruite par Dame — retournée au stock").
|
||||
- Le joueur reprend avec Espace après avoir pris connaissance.
|
||||
|
||||
---
|
||||
|
||||
## Format JSON de campagne
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "La Quête du Roi",
|
||||
"initialWidth": 4,
|
||||
"initialHeight": 4,
|
||||
"missions": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Premier Convoi",
|
||||
"description": "Les pions découvrent une scierie. Il faut acheminer le bois.",
|
||||
"terrainPatch": {
|
||||
"newWidth": 4,
|
||||
"newHeight": 4,
|
||||
"cells": [
|
||||
{ "col": 0, "row": 0, "type": "production", "production": { "name": "Scierie", "cargo": "wood", "amount": 1 } },
|
||||
{ "col": 3, "row": 0, "type": "demand", "demand": { "name": "Dépôt", "cargo": "wood", "amount": 3 } }
|
||||
]
|
||||
},
|
||||
"unlockedPieces": ["pawn"],
|
||||
"unlockedLevels": [{ "kind": "pawn", "level": 1 }],
|
||||
"stock": [
|
||||
{ "kind": "pawn", "count": 6 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Forger les Tours",
|
||||
"description": "Les pions ont forgé des Tours. De nouveaux territoires s'ouvrent à l'est.",
|
||||
"terrainPatch": {
|
||||
"newWidth": 6,
|
||||
"newHeight": 4,
|
||||
"cells": [
|
||||
{ "col": 4, "row": 0, "type": "empty" },
|
||||
{ "col": 4, "row": 1, "type": "wall" },
|
||||
{ "col": 4, "row": 2, "type": "empty" },
|
||||
{ "col": 4, "row": 3, "type": "empty" },
|
||||
{ "col": 5, "row": 0, "type": "empty" },
|
||||
{ "col": 5, "row": 1, "type": "empty" },
|
||||
{ "col": 5, "row": 2, "type": "production", "production": { "name": "Carrière", "cargo": "stone", "amount": 1 } },
|
||||
{ "col": 5, "row": 3, "type": "demand", "demand": { "name": "Chantier", "cargo": "stone", "amount": 5 } }
|
||||
]
|
||||
},
|
||||
"unlockedPieces": ["rook"],
|
||||
"unlockedLevels": [{ "kind": "rook", "level": 1 }],
|
||||
"stock": [
|
||||
{ "kind": "pawn", "count": 2 },
|
||||
{ "kind": "rook", "count": 3 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phases d'implémentation
|
||||
|
||||
### Phase 1 — Modèles engine (pas de Godot)
|
||||
|
||||
1. Créer `CampaignDef`, `MissionDef`, `TerrainPatch`, `PatchCell`, `PieceUpgrade` dans `chessistics-engine/Model/`.
|
||||
2. Créer `CampaignState` dans `chessistics-engine/Model/`.
|
||||
3. Modifier `SimPhase` : supprimer `Edit`, `Victory`, `Defeat`. Ajouter `MissionComplete`.
|
||||
4. Ajouter `missionIndex` à `DemandState`.
|
||||
5. Ajouter `amount` à `ProductionDef` (remplace le concept d'intervalle). Buffer max = amount.
|
||||
6. Ajouter `level` à `PieceStock` et aux modèles de pièces pour supporter les niveaux I/II/III.
|
||||
|
||||
### Phase 2 — Refactor SimPhase et édition en temps réel
|
||||
|
||||
1. Supprimer les guards `phase == Edit` dans `PlacePieceCommand` et `RemovePieceCommand`.
|
||||
2. Supprimer `StopSimulationCommand`, `StartSimulationCommand`, `ResetLevelCommand`.
|
||||
3. Modifier `PauseSimulationCommand` pour terminer le tour en cours avant de figer.
|
||||
4. La sim démarre automatiquement au chargement (état initial = `Running` en pause).
|
||||
|
||||
### Phase 3 — Collisions → retour au stock + pause auto
|
||||
|
||||
1. Modifier `CollisionResolver` : la pièce détruite retourne au stock.
|
||||
2. Créer `PieceReturnedToStockEvent` (avec coordonnées de la collision pour le pan caméra).
|
||||
3. Incrémenter le stock dans `BoardState` quand une pièce est détruite.
|
||||
4. L'engine émet un event de pause automatique après une collision.
|
||||
5. Tests : vérifier qu'après collision la pièce réapparaît dans le stock et la sim est en pause.
|
||||
|
||||
### Phase 4 — Commands & Events campagne
|
||||
|
||||
1. Créer `LoadCampaignCommand` : initialise `BoardState` depuis mission 0, applique `unlockedPieces`.
|
||||
2. Créer `AdvanceMissionCommand` : applique `TerrainPatch`, ajoute stock/demandes/pièces débloquées.
|
||||
3. Créer `MissionCompleteEvent`, `MissionStartedEvent`, `TerrainExpandedEvent`, `PieceUnlockedEvent`.
|
||||
4. Renommer `VictoryChecker` → `MissionChecker` : ne vérifie que les demandes de la mission courante.
|
||||
|
||||
### Phase 5 — Drag & drop engine
|
||||
|
||||
1. Créer `MovePieceCommand` : valide le nouveau placement, met à jour start/end.
|
||||
2. Créer `PieceMovedByPlayerEvent`.
|
||||
3. Tests : déplacer une pièce en Running, vérifier qu'elle reprend sa trajectoire.
|
||||
|
||||
### Phase 6 — Production variable
|
||||
|
||||
1. Modifier la production pour utiliser `amount` (1-4) au lieu de toujours produire 1.
|
||||
2. Le buffer max = `amount`. La surproduction écrase le buffer.
|
||||
3. Tests : production amount=3 remplit 3 slots, non récupérés → écrasés au tour suivant.
|
||||
|
||||
### Phase 7 — CampaignLoader
|
||||
|
||||
1. Créer `CampaignLoader` dans `chessistics-engine/Loading/` : parse le JSON campagne.
|
||||
2. Valider la cohérence : pas de régression de taille, `unlockedPieces` cohérent.
|
||||
3. Les anciens JSON de niveaux restent compatibles (mode puzzle legacy optionnel).
|
||||
|
||||
### Phase 8 — Tests engine intégration
|
||||
|
||||
1. Test : charger une campagne, compléter mission 1, avancer, vérifier terrain étendu.
|
||||
2. Test : les pièces de la mission 1 restent en place après `AdvanceMissionCommand`.
|
||||
3. Test : `MissionCompleteEvent` émis au bon moment.
|
||||
4. Test : placer une pièce pendant Running fonctionne.
|
||||
5. Test : collision retourne la pièce au stock.
|
||||
6. Test : pièces non débloquées ne sont pas plaçables.
|
||||
7. Test : niveaux de pièces affectent portée et priorité.
|
||||
|
||||
### Phase 9 — GameSim facade
|
||||
|
||||
1. `GameSim` accepte un `CampaignDef` (mode principal) ou un `LevelDef` (legacy).
|
||||
2. Exposer `CampaignSnapshot` avec `currentMissionIndex`, `completedMissions`, `availablePieces`.
|
||||
3. Plus de `StopSimulationCommand` dans la facade.
|
||||
|
||||
### Phase 10 — Présentation Godot
|
||||
|
||||
1. `Main.cs` : charger une campagne, supprimer le flow Stop/Start.
|
||||
2. Contrôles : Espace = pause/resume, plus de bouton Stop. Barre de contrôle : `[⏸/▶] [x1] [x2] [x4] Tour: N Mission: M/T`.
|
||||
3. **Auto-pause placement** : sélectionner un type de pièce dans le stock met la sim en pause. La pause est levée quand le placement est confirmé ou annulé (Échap).
|
||||
4. **Retrait de pièce** : supprimer le clic droit pour retirer. Clic gauche sélectionne → touche Suppr ou bouton [Retirer] dans le panneau de détail.
|
||||
5. **Collision — pause + caméra + notification** :
|
||||
- Sur `PieceReturnedToStockEvent` : pause auto de la simulation.
|
||||
- Pan + zoom caméra vers la zone de collision (tween animé).
|
||||
- Notification dans un coin de l'écran (ex: "Tour II détruite par Dame — retournée au stock").
|
||||
- Le joueur reprend avec Espace.
|
||||
6. **Transition de mission** (cinématique) :
|
||||
- Titre "Nouvelle mission" en plein écran, fade-in.
|
||||
- Lock du pan/zoom joueur — la caméra se déplace pour montrer la zone de la prochaine mission.
|
||||
- Les nouvelles cases apparaissent sur le plateau avec animation d'expansion.
|
||||
- Le titre de mission se déplace vers la zone d'objectif du panneau latéral avant de disparaître (guide l'œil).
|
||||
- Unlock pan/zoom, la simulation reprend.
|
||||
7. `ObjectivePanel` : objectifs de la mission courante + badge ✓ pour les missions complétées.
|
||||
8. `PieceStockPanel` : stock cumulatif, n'affiche que les pièces débloquées.
|
||||
9. Notification `PieceUnlockedEvent` : animation de déblocage d'une nouvelle pièce.
|
||||
10. Drag & drop visuel : le joueur glisse une pièce, les destinations légales s'affichent.
|
||||
11. **Undo (Ctrl+Z)** : annule le dernier placement ou retrait. Conserver un historique de commandes côté présentation, rejouer l'état sans la dernière.
|
||||
|
||||
### Phase 11 — Données de campagne
|
||||
|
||||
1. Créer `Data/campaigns/campaign_01.json` — "La Quête du Roi".
|
||||
2. Concevoir 6-8 missions avec progression de pièces (Pion → Tour → Fou → Cavalier → Dame).
|
||||
3. Chaque mission étend le terrain sans casser les solutions précédentes.
|
||||
4. Équilibrer les stocks cumulatifs et les `amount` de production.
|
||||
|
||||
---
|
||||
|
||||
## Risques et mitigations
|
||||
|
||||
| Risque | Mitigation |
|
||||
|--------|------------|
|
||||
| Un patch de terrain casse une solution existante | Validation loader : les patches ne peuvent que **ajouter** des cases ou modifier des cases hors de la zone initiale |
|
||||
| Le stock cumulé rend les missions triviales | Playtesting + stock additionnel calibré mission par mission |
|
||||
| Placer des pièces en Running cause des bugs de timing | La commande s'applique **entre deux tours** (fin du tour courant → placement → tour suivant) |
|
||||
| Collision = perte de pièce frustrante | Retour au stock — le joueur peut replacer immédiatement |
|
||||
| Drag & drop complexe pendant la simulation | Le drag ne modifie la pièce qu'au **relâchement** (atomique), appliqué entre deux tours |
|
||||
| Niveaux de pièces explosent la complexité | Introduction graduelle : mission 1-3 = niveau I seul, niveaux II/III arrivent plus tard |
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# Plan Playtest Fixes
|
||||
|
||||
## Problèmes identifiés
|
||||
|
||||
### P1 — Superposition production/demand (Bug moteur)
|
||||
**Cause racine**: `ApplyTerrainPatch` ne nettoie pas les bâtiments existants avant d'ajouter un nouveau (Production, Demand, Transformer). Mission 3 place un demand sur (5,5) où Mission 2 avait une production → les deux coexistent dans les dictionnaires.
|
||||
**Fix**: Appeler `ClearBuildingAt(coords)` pour TOUS les types de cellules dans `ApplyTerrainPatch`.
|
||||
|
||||
### P2 — Murs sur cases existantes / pièces traversent les murs
|
||||
**Cause racine**: Mission 3 ajoute des murs sur des cases déjà jouables (2,2), (2,3), etc. Si des pièces y sont placées, elles restent et traversent le mur.
|
||||
**Fix moteur**: Quand un mur apparaît via terrain patch, retirer les pièces dont StartCell ou EndCell est sur ce mur (retour au stock).
|
||||
**Fix level design**: Redessiner mission 3 pour que les murs soient sur des cases nouvellement révélées (agrandir le plateau).
|
||||
|
||||
### P3 — Noms de bâtiments dupliqués
|
||||
**Cause**: Deux "Dépôt Royal" (mission 1 et mission 3).
|
||||
**Fix**: Renommer dans campaign_01.json. Mission 3 demand → "Avant-Poste du Col".
|
||||
|
||||
### P4 — Compteurs d'objectifs qui montent à l'infini
|
||||
**Cause**: `ObjectivePanel.UpdateProgress` affiche le current réel même quand il dépasse le required.
|
||||
**Fix**: Afficher `min(current, required)/required` et marquer visuellement les objectifs complétés. Les objectifs des missions précédentes complétées: afficher "✓" et ne plus mettre à jour.
|
||||
|
||||
### P5 — Espace sélectionne un pion au lieu de lancer la simulation
|
||||
**Cause**: Les boutons Godot ont `FocusMode = All` par défaut et capturent la touche Espace.
|
||||
**Fix**: Mettre `FocusMode = None` sur les boutons du PieceStockPanel. Ajouter un handler Espace dans Main pour toggle play/pause.
|
||||
|
||||
### P6 — Centrage du plateau
|
||||
**Cause**: Le calcul de centrage ne prend pas en compte la barre de titre (~36px en haut).
|
||||
**Fix**: Ajouter `TitleBarHeight` au calcul de l'offset caméra.
|
||||
|
||||
### P7 — Pas de narration/lore
|
||||
**Fix**: Ajouter un champ `flavor` dans MissionDef + campaign JSON. Afficher un encart narratif au démarrage de chaque mission. Un personnage parle en une phrase, ton léger et enjoué.
|
||||
|
||||
## Ordre d'implémentation
|
||||
|
||||
1. P1 + P2: Fixes moteur (ApplyTerrainPatch) + tests
|
||||
2. P3: Redesign campaign_01.json (missions 2-7, noms uniques, pas de superposition, murs sur nouvelles cases)
|
||||
3. P4: Test automatisé de validation du campaign (pas de superposition, niveaux finissables)
|
||||
4. P5: Spacebar play/pause
|
||||
5. P6: Centrage caméra
|
||||
6. P4: Objectifs capped
|
||||
7. P7: Narration
|
||||
|
|
@ -62,8 +62,6 @@ Claude Code, lancé à l'intérieur, peut donc compiler le projet, exécuter
|
|||
les tests, **démarrer une vraie instance de Godot en headless** et lire les
|
||||
captures PNG produites — sans dépendance Windows.
|
||||
|
||||
Voir [`autonomous_plan.md`](autonomous_plan.md) pour le design détaillé.
|
||||
|
||||
### Option 1 — Docker Desktop + terminal Windows (le plus simple)
|
||||
|
||||
1. Installer [Docker Desktop pour Windows](https://www.docker.com/products/docker-desktop/).
|
||||
|
|
|
|||
|
|
@ -1,149 +0,0 @@
|
|||
# Headless Linux dev container for autonomous Chessistics testing
|
||||
|
||||
## Why
|
||||
|
||||
Today the automation harness only runs on Windows because the Godot binary is
|
||||
hardcoded to `C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe`. When
|
||||
Claude Code runs inside the project's dev container (Linux / `node:20`), it
|
||||
can read source code and run `dotnet test`, but it **cannot launch the actual
|
||||
game** — there's no Godot binary, no .NET SDK, and no display server for the
|
||||
renderer.
|
||||
|
||||
The goal: make the dev container a self-contained environment where Claude
|
||||
Code can build the project, launch a real Godot instance in headless Linux
|
||||
mode, drive it via the automation harness, and read back 1280×720 PNG
|
||||
screenshots — all without any Windows dependency.
|
||||
|
||||
## Design
|
||||
|
||||
### Pieces required
|
||||
|
||||
1. **Godot 4.6.2-stable Mono for Linux** — matches the Windows editor the
|
||||
project already uses. Installed once at image build time to `/opt/godot/`
|
||||
with a symlink `/opt/godot/godot`.
|
||||
2. **.NET SDK 9.0** — the project targets `net9.0`. Installed via the
|
||||
upstream `dot.net` install script to `/usr/local/dotnet/`.
|
||||
3. **Xvfb + Mesa software GL** — a virtual framebuffer at `:99` so Godot's
|
||||
GL-compatibility renderer has somewhere to draw. `xvfb-run` wraps any
|
||||
command transparently.
|
||||
4. **Python 3** — the automation harness is stdlib-only Python.
|
||||
5. **Minimal X / audio runtime deps** — `libx11`, `libxcursor`, `libxrandr`,
|
||||
`libxi`, `libgl1`, `libgles2`, `libasound2`, `libxkbcommon0`, etc.
|
||||
Without these, Godot exits on startup with `libXext not found`-style errors.
|
||||
|
||||
### How Godot reaches the framebuffer
|
||||
|
||||
Two options considered:
|
||||
|
||||
- **(A)** Run `Xvfb :99 -screen 0 1280x720x24 &` as a background process,
|
||||
export `DISPLAY=:99`, launch Godot normally. Persistent display, shared by
|
||||
many Godot runs.
|
||||
- **(B)** Use `xvfb-run -a --server-args="-screen 0 1280x720x24"` as a prefix
|
||||
on every Godot invocation. A fresh display per launch; cleans up
|
||||
automatically on exit.
|
||||
|
||||
**Chosen: (B)**, because the automation harness already spawns Godot once per
|
||||
`Harness.launch()` and cleans up on context exit — matches the per-launch
|
||||
lifecycle naturally, no daemon to keep alive, no race on the display number.
|
||||
|
||||
A tiny wrapper `/usr/local/bin/godot-xvfb` wraps `xvfb-run … $GODOT_BIN
|
||||
"$@"`, so the harness (or a human) only has to invoke one path.
|
||||
|
||||
### Integration with the existing harness
|
||||
|
||||
`tools/automation/harness.py` currently hardcodes the Windows Godot path. We
|
||||
teach it two things:
|
||||
|
||||
1. Read `GODOT_BIN` from the environment first; fall back to the platform
|
||||
default (Windows path on Windows, `/opt/godot/godot` on Linux).
|
||||
2. On Linux, auto-prepend `["xvfb-run", "-a", "--server-args=-screen 0
|
||||
1280x720x24"]` to the Godot launch command unless `DISPLAY` is already
|
||||
set (someone has a real display, skip the wrap).
|
||||
|
||||
With those two tweaks, every existing Python helper (`smoke.py`,
|
||||
`run_game.py`, `solve_*.py`) works unchanged inside the container.
|
||||
|
||||
### Firewall considerations
|
||||
|
||||
The container's `init-firewall.sh` runs at `postStartCommand`, **after** the
|
||||
image is built, and drops all outbound traffic except to a small allowlist
|
||||
(GitHub, npmjs, Anthropic, Sentry, Statsig). Impact on our pieces:
|
||||
|
||||
- **Godot binary + .NET SDK**: downloaded during `docker build`, which runs
|
||||
_before_ the firewall exists → works unconditionally.
|
||||
- **`dotnet restore`** (runtime, e.g. after a `git pull`): needs
|
||||
`api.nuget.org`. Added to the allowlist.
|
||||
- **Godot runtime**: no outbound traffic required — the engine runs fully
|
||||
offline once installed.
|
||||
|
||||
### Build sequence inside the Dockerfile
|
||||
|
||||
As `root`, before the existing `USER node` switch:
|
||||
|
||||
```
|
||||
# 1. X/GL/audio runtime + python + xvfb
|
||||
apt-get install xvfb xauth libx11-6 libxcursor1 libxinerama1 libxrandr2 \
|
||||
libxi6 libxext6 libgl1 libglx-mesa0 libgl1-mesa-dri libglu1-mesa \
|
||||
libasound2 libxkbcommon0 libxkbcommon-x11-0 libfontconfig1 libdbus-1-3 \
|
||||
python3 python3-pip
|
||||
|
||||
# 2. .NET SDK 9.0 via upstream install script
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash -s -- \
|
||||
--channel 9.0 --install-dir /usr/local/dotnet
|
||||
ln -s /usr/local/dotnet/dotnet /usr/local/bin/dotnet
|
||||
|
||||
# 3. Godot 4.6.2-stable mono, Linux x86_64
|
||||
wget https://github.com/godotengine/godot/releases/download/${VERSION}/\
|
||||
Godot_v${VERSION}_mono_linux_x86_64.zip
|
||||
unzip … -d /opt/godot
|
||||
ln -s /opt/godot/Godot_v…/Godot_v…_mono_linux.x86_64 /opt/godot/godot
|
||||
```
|
||||
|
||||
Then:
|
||||
- `ENV GODOT_BIN=/opt/godot/godot`
|
||||
- `ENV PATH=$PATH:/opt/godot:/usr/local/dotnet`
|
||||
- Drop in `/usr/local/bin/godot-xvfb` wrapper
|
||||
|
||||
## File-by-file change list
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `.devcontainer/Dockerfile` | Add Godot / dotnet / xvfb installs before `USER node` |
|
||||
| `.devcontainer/init-firewall.sh` | Append `api.nuget.org` to the domain allowlist |
|
||||
| `.devcontainer/godot-xvfb.sh` *(new)* | `exec xvfb-run -a … "$GODOT_BIN" "$@"` |
|
||||
| `tools/automation/harness.py` | Env-aware Godot path + Linux xvfb auto-wrap |
|
||||
| `README.md` *(new)* | Windows / WSL2 launch instructions for the dev container |
|
||||
|
||||
Nothing inside `Scripts/` or `chessistics-engine/` changes. The harness
|
||||
contract (inbox/outbox/screens) is platform-agnostic already.
|
||||
|
||||
## Verification
|
||||
|
||||
After rebuild:
|
||||
|
||||
1. `docker build .devcontainer -t chessistics-dev` succeeds.
|
||||
2. `devcontainer up --workspace-folder .` starts the container and
|
||||
post-start firewall passes.
|
||||
3. Inside: `dotnet --version` → `9.0.x`, `godot --version` →
|
||||
`4.6.2.stable.mono.official.*`.
|
||||
4. `dotnet build Chessistics.csproj` → green.
|
||||
5. `dotnet test chessistics-tests/` → 102 / 102.
|
||||
6. `python3 tools/automation/smoke.py` → loads mission 1, takes PNG
|
||||
screenshots that are non-black, quits cleanly.
|
||||
7. `Read` one of the PNGs — it should show the same mission 1 UI as the
|
||||
Windows run (title bar, board, objectives, stock panel).
|
||||
|
||||
## Out of scope (explicit non-goals)
|
||||
|
||||
- **GPU acceleration**: we use Mesa software rendering. Xvfb + llvmpipe is
|
||||
enough for 1280×720 at a few FPS, which is what the harness needs.
|
||||
- **Real display forwarding** (X11 forwarding, VNC, noVNC): doable but
|
||||
unnecessary — Claude reads PNGs, not a live video feed.
|
||||
- **Multi-arch images**: we ship x86_64 only. ARM (Apple Silicon via
|
||||
Docker Desktop emulation) would need `Godot_v…_mono_linux_arm64.zip` —
|
||||
straightforward to add if needed, not done here.
|
||||
- **Shrinking the image**: Godot + .NET SDK adds ~500 MB. Worth it;
|
||||
multi-stage builds could trim later.
|
||||
- **Keeping Xvfb warm across launches**: the single-launch pattern is clean
|
||||
enough. If someone ever scripts dozens of rapid Godot starts and the
|
||||
xvfb-run startup cost shows up, revisit approach (A).
|
||||
87
docs/PLAN.md
Normal file
87
docs/PLAN.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# Chessistics — Plan de travail (restant)
|
||||
|
||||
Consolidation des sections non implementees des anciens `PLAN_missions.md` et
|
||||
`PLAN_leveldesign.md`. Le moteur (black-box sim, campagne, transformateurs,
|
||||
missions 1-7) est en place. Ce qui suit concerne la finition UX, les visuels
|
||||
et l'extension de la campagne.
|
||||
|
||||
---
|
||||
|
||||
## 1. UX / Presentation — gaps Godot
|
||||
|
||||
Le moteur expose deja les commandes et events requis ; cote Godot il manque
|
||||
les surfaces d'interaction et d'animation.
|
||||
|
||||
### 1.1 Undo (Ctrl+Z)
|
||||
Annule le dernier placement ou retrait. L'architecture event-sourcing rend
|
||||
l'implementation naturelle : conserver un historique de commandes cote
|
||||
presentation, rejouer l'etat sans la derniere. Essentiel pour l'iteration
|
||||
rapide sur un reseau en temps reel.
|
||||
|
||||
### 1.2 Drag & drop des pieces placees
|
||||
`MovePieceCommand` + `PieceMovedByPlayerEvent` existent cote engine.
|
||||
Cote Godot : permettre de glisser une piece placee pour deplacer son point de
|
||||
depart. Les arrivees legales se recalculent pendant le drag. Application
|
||||
atomique au relachement (entre deux tours).
|
||||
|
||||
### 1.3 Collision — camera pan/zoom + notification
|
||||
L'engine emet deja `PieceReturnedToStockEvent` + auto-pause. Il reste a :
|
||||
- Animer un pan + zoom de la camera vers la case de collision.
|
||||
- Afficher une notification dans un coin de l'ecran
|
||||
(ex : "Tour II detruite par Dame — retournee au stock").
|
||||
- Reprise a Espace apres lecture.
|
||||
|
||||
### 1.4 Touche Suppr pour retirer une piece
|
||||
Le bouton `[Retirer]` du `DetailPanel` existe. Ajouter en complement :
|
||||
selection + `Delete` → meme effet que le bouton.
|
||||
|
||||
### 1.5 Cinematique de transition de mission
|
||||
Sur `MissionStartedEvent` (hors mission 0) :
|
||||
- Titre "Nouvelle mission" plein ecran en fade-in.
|
||||
- Lock pan/zoom ; la camera se deplace vers la nouvelle zone.
|
||||
- Animation d'expansion pour les cases debloquees par le `TerrainPatch`.
|
||||
- Le titre glisse ensuite vers le panneau d'objectifs avant de disparaitre
|
||||
(guide l'oeil).
|
||||
- Unlock pan/zoom, la simulation reprend.
|
||||
|
||||
### 1.6 Visualisation des trajets
|
||||
`TrajectView` existe. Manque :
|
||||
- Fleches directionnelles sur le trait.
|
||||
- Pulsation legere du trait quand la piece est en mouvement.
|
||||
- Couleur unique par piece pour distinguer les chaines.
|
||||
|
||||
---
|
||||
|
||||
## 2. Extension de la campagne
|
||||
|
||||
`campaign_01.json` compte actuellement 7 missions (Pion → Tour → Cavalier →
|
||||
Fou → Dame + 2 transformateurs). La vision GDD/plan prevoit une campagne
|
||||
plus longue et un final orchestrant toutes les chaines.
|
||||
|
||||
### 2.1 Missions supplementaires
|
||||
- **"L'Expansion Finale"** (14x14) : multiples transformateurs, murs
|
||||
complexes. Defi : maintenir toutes les chaines en s'etendant ; gestion de
|
||||
la congestion / collisions.
|
||||
- **"Le Couronnement final"** (14x14) : Cathedrale qui demande or + armes +
|
||||
outils simultanement. Orchestrer l'ensemble des chaines logistiques.
|
||||
|
||||
Actuel mission 7 "Le Couronnement" = transformation outils→or seule.
|
||||
La renommer ou l'inserer comme mission intermediaire et ajouter les deux
|
||||
missions ci-dessus pour retrouver la progression 10 missions de la vision
|
||||
initiale.
|
||||
|
||||
### 2.2 Demandes recurrentes (post-campagne 1)
|
||||
Pour forcer la preservation des automatisations entre missions :
|
||||
- Une demande consomme N unites par tour.
|
||||
- Si plus approvisionnee → etat "en penurie".
|
||||
- Condition : aucune demande en penurie pendant X tours consecutifs.
|
||||
|
||||
Implique : nouveau champ sur `DemandState`, flag shortage, regle de victoire
|
||||
parametrable par mission. A valider via playtest avant d'ajouter au scope.
|
||||
|
||||
---
|
||||
|
||||
## 3. Polish visuel des transformateurs
|
||||
|
||||
Couleur de cellule dediee (orange cuivre), animation flash input → flash
|
||||
output sur `CargoConvertedEvent`, icones de cargo sur les pieces porteuses.
|
||||
Loading…
Add table
Reference in a new issue