Extend campaign_01 to 9 missions with a finale cathedral
Renames mission 7 from "Le Couronnement" to "Le Comptoir" (it only sets
up the tools→gold chain — the coronation is now the final mission) and
adds:
- Mission 8 "L'Expansion Finale" (12×10): a Forge Est (wood→tools) and
an Armurerie Est (stone→arms) on new rightmost columns, plus walls
that force pieces to route around them. An Entrepôt Est demand on
(11,9) gives the mission its own goal without depending on the old
demands.
- Mission 9 "Le Couronnement" (12×12): the Cathédrale occupies row 11
as three adjacent demands — outils, armes, and or — so the player
must keep all three transformation chains running simultaneously to
complete the campaign.
Existing file tests updated for the new count and rename; new
Campaign01Tests asserts structure and non-regressive terrain across all
nine missions.
This commit is contained in:
parent
480c783bd6
commit
e3eb10570b
4 changed files with 170 additions and 10 deletions
|
|
@ -204,8 +204,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"name": "Le Couronnement",
|
"name": "Le Comptoir",
|
||||||
"description": "Le Comptoir transforme les outils en or. Livrez l'or au Trésor Royal pour couronner le Roi.",
|
"description": "Le Comptoir transforme les outils en or. Livrez l'or au Trésor Royal — bientôt le Roi.",
|
||||||
"flavor": "« De l'or ! Le Roi sera content. Enfin… s'il reste du budget pour nous payer. » — Dame sceptique",
|
"flavor": "« De l'or ! Le Roi sera content. Enfin… s'il reste du budget pour nous payer. » — Dame sceptique",
|
||||||
"terrainPatch": {
|
"terrainPatch": {
|
||||||
"newWidth": 10,
|
"newWidth": 10,
|
||||||
|
|
@ -222,6 +222,91 @@
|
||||||
{ "kind": "rook", "count": 4 },
|
{ "kind": "rook", "count": 4 },
|
||||||
{ "kind": "bishop", "count": 2 }
|
{ "kind": "bishop", "count": 2 }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "L'Expansion Finale",
|
||||||
|
"description": "Deux nouveaux transformateurs s'ajoutent pour soutenir la production. Les routes existantes doivent tenir.",
|
||||||
|
"flavor": "« On double l'équipe, on double la paie ? Non. D'accord, double le boulot alors. » — Pion pragmatique",
|
||||||
|
"terrainPatch": {
|
||||||
|
"newWidth": 12,
|
||||||
|
"newHeight": 10,
|
||||||
|
"cells": [
|
||||||
|
{ "col": 10, "row": 0, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 1, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 2, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 3, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 4, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 5, "type": "transformer", "transformer": { "name": "Forge Est", "inputCargo": "wood", "inputRequired": 2, "outputCargo": "tools", "outputAmount": 1 } },
|
||||||
|
{ "col": 10, "row": 6, "type": "wall" },
|
||||||
|
{ "col": 10, "row": 7, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 8, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 9, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 0, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 1, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 2, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 3, "type": "transformer", "transformer": { "name": "Armurerie Est", "inputCargo": "stone", "inputRequired": 2, "outputCargo": "arms", "outputAmount": 1 } },
|
||||||
|
{ "col": 11, "row": 4, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 5, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 6, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 7, "type": "wall" },
|
||||||
|
{ "col": 11, "row": 8, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 9, "type": "demand", "demand": { "name": "Entrepôt Est", "cargo": "tools", "amount": 2 } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unlockedPieces": [],
|
||||||
|
"unlockedLevels": [],
|
||||||
|
"stock": [
|
||||||
|
{ "kind": "rook", "count": 4 },
|
||||||
|
{ "kind": "bishop", "count": 2 },
|
||||||
|
{ "kind": "knight", "count": 2 },
|
||||||
|
{ "kind": "pawn", "count": 4 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "Le Couronnement",
|
||||||
|
"description": "La Cathédrale est la dernière étape. Elle réclame outils, armes et or simultanément pour couronner le Roi.",
|
||||||
|
"flavor": "« Trois offrandes, un Roi. Et après, c'est lui qui nous couronne ? Ou qui nous exécute ? » — Cavalier inquiet",
|
||||||
|
"terrainPatch": {
|
||||||
|
"newWidth": 12,
|
||||||
|
"newHeight": 12,
|
||||||
|
"cells": [
|
||||||
|
{ "col": 0, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 0, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 1, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 1, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 2, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 2, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 3, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 3, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 4, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 4, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 5, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 5, "row": 11, "type": "demand", "demand": { "name": "Cathédrale (Outils)", "cargo": "tools", "amount": 2 } },
|
||||||
|
{ "col": 6, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 6, "row": 11, "type": "demand", "demand": { "name": "Cathédrale (Armes)", "cargo": "arms", "amount": 2 } },
|
||||||
|
{ "col": 7, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 7, "row": 11, "type": "demand", "demand": { "name": "Cathédrale (Or)", "cargo": "gold", "amount": 2 } },
|
||||||
|
{ "col": 8, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 8, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 9, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 9, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 10, "row": 11, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 10, "type": "empty" },
|
||||||
|
{ "col": 11, "row": 11, "type": "empty" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unlockedPieces": [],
|
||||||
|
"unlockedLevels": [],
|
||||||
|
"stock": [
|
||||||
|
{ "kind": "queen", "count": 2 },
|
||||||
|
{ "kind": "rook", "count": 4 },
|
||||||
|
{ "kind": "bishop", "count": 2 },
|
||||||
|
{ "kind": "knight", "count": 2 },
|
||||||
|
{ "kind": "pawn", "count": 4 }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
chessistics-tests/Loading/Campaign01Tests.cs
Normal file
81
chessistics-tests/Loading/Campaign01Tests.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
using System.IO;
|
||||||
|
using Chessistics.Engine.Loading;
|
||||||
|
using Chessistics.Engine.Model;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Chessistics.Tests.Loading;
|
||||||
|
|
||||||
|
public class Campaign01Tests
|
||||||
|
{
|
||||||
|
private static CampaignDef LoadRealCampaign()
|
||||||
|
{
|
||||||
|
var repoRoot = Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..");
|
||||||
|
var path = Path.GetFullPath(Path.Combine(repoRoot, "Data", "campaigns", "campaign_01.json"));
|
||||||
|
return CampaignLoader.Load(File.ReadAllText(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Campaign_HasExpectedStructure()
|
||||||
|
{
|
||||||
|
var c = LoadRealCampaign();
|
||||||
|
Assert.Equal(9, c.Missions.Count);
|
||||||
|
Assert.Equal(4, c.InitialWidth);
|
||||||
|
Assert.Equal(4, c.InitialHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Mission8_AddsTwoTransformersAndExpandsTo12x10()
|
||||||
|
{
|
||||||
|
var c = LoadRealCampaign();
|
||||||
|
var m8 = c.Missions[7];
|
||||||
|
Assert.Equal("L'Expansion Finale", m8.Name);
|
||||||
|
Assert.Equal(12, m8.TerrainPatch.NewWidth);
|
||||||
|
Assert.Equal(10, m8.TerrainPatch.NewHeight);
|
||||||
|
|
||||||
|
var transformers = m8.TerrainPatch.Cells.Where(p => p.Type == CellType.Transformer).ToList();
|
||||||
|
Assert.Equal(2, transformers.Count);
|
||||||
|
Assert.Contains(transformers, t => t.Transformer!.Name == "Forge Est");
|
||||||
|
Assert.Contains(transformers, t => t.Transformer!.Name == "Armurerie Est");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Mission9_CathedralDemandsAllThreeCargoTypes()
|
||||||
|
{
|
||||||
|
var c = LoadRealCampaign();
|
||||||
|
var m9 = c.Missions[8];
|
||||||
|
Assert.Equal("Le Couronnement", m9.Name);
|
||||||
|
Assert.Equal(12, m9.TerrainPatch.NewWidth);
|
||||||
|
Assert.Equal(12, m9.TerrainPatch.NewHeight);
|
||||||
|
|
||||||
|
var demands = m9.TerrainPatch.Cells.Where(p => p.Type == CellType.Demand).ToList();
|
||||||
|
Assert.Equal(3, demands.Count);
|
||||||
|
var types = demands.Select(d => d.Demand!.Cargo).ToHashSet();
|
||||||
|
Assert.Contains(CargoType.Tools, types);
|
||||||
|
Assert.Contains(CargoType.Arms, types);
|
||||||
|
Assert.Contains(CargoType.Gold, types);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Mission7_RenamedToComptoir()
|
||||||
|
{
|
||||||
|
var c = LoadRealCampaign();
|
||||||
|
var m7 = c.Missions[6];
|
||||||
|
Assert.Equal("Le Comptoir", m7.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AllTerrainPatchesAreNonRegressive()
|
||||||
|
{
|
||||||
|
// Subsequent missions must only grow the board, never shrink it.
|
||||||
|
var c = LoadRealCampaign();
|
||||||
|
int w = c.InitialWidth, h = c.InitialHeight;
|
||||||
|
for (int i = 0; i < c.Missions.Count; i++)
|
||||||
|
{
|
||||||
|
var tp = c.Missions[i].TerrainPatch;
|
||||||
|
Assert.True(tp.NewWidth >= w, $"Mission {i}: width shrunk from {w} to {tp.NewWidth}");
|
||||||
|
Assert.True(tp.NewHeight >= h, $"Mission {i}: height shrunk from {h} to {tp.NewHeight}");
|
||||||
|
w = tp.NewWidth;
|
||||||
|
h = tp.NewHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,7 @@ public class CampaignFileTests
|
||||||
var campaign = CampaignLoader.LoadFromFile("../../../../Data/campaigns/campaign_01.json");
|
var campaign = CampaignLoader.LoadFromFile("../../../../Data/campaigns/campaign_01.json");
|
||||||
|
|
||||||
Assert.Equal("La Quête du Roi", campaign.Name);
|
Assert.Equal("La Quête du Roi", campaign.Name);
|
||||||
Assert.Equal(7, campaign.Missions.Count);
|
Assert.Equal(9, campaign.Missions.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -52,7 +52,7 @@ public class CampaignFileTests
|
||||||
var campaign = CampaignLoader.LoadFromFile("../../../../Data/campaigns/campaign_01.json");
|
var campaign = CampaignLoader.LoadFromFile("../../../../Data/campaigns/campaign_01.json");
|
||||||
var m7 = campaign.Missions[6];
|
var m7 = campaign.Missions[6];
|
||||||
|
|
||||||
Assert.Equal("Le Couronnement", m7.Name);
|
Assert.Equal("Le Comptoir", m7.Name);
|
||||||
|
|
||||||
var transformerCell = m7.TerrainPatch.Cells
|
var transformerCell = m7.TerrainPatch.Cells
|
||||||
.FirstOrDefault(c => c.Type == CellType.Transformer);
|
.FirstOrDefault(c => c.Type == CellType.Transformer);
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,6 @@ et l'extension de la campagne.
|
||||||
Le moteur expose deja les commandes et events requis ; cote Godot il manque
|
Le moteur expose deja les commandes et events requis ; cote Godot il manque
|
||||||
les surfaces d'interaction et d'animation.
|
les surfaces d'interaction et d'animation.
|
||||||
|
|
||||||
### 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
|
## 2. Extension de la campagne
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue