diff --git a/src/core/game/NationCreation.ts b/src/core/game/NationCreation.ts index 527b981615..62068257ea 100644 --- a/src/core/game/NationCreation.ts +++ b/src/core/game/NationCreation.ts @@ -34,7 +34,9 @@ export function createNationsForGame( ): Nation[] { const toNation = (n: ManifestNation): Nation => new Nation( - new Cell(n.coordinates[0], n.coordinates[1]), + n.coordinates !== undefined + ? new Cell(n.coordinates[0], n.coordinates[1]) + : undefined, new PlayerInfo(n.name, PlayerType.Nation, null, random.nextID()), ); diff --git a/src/core/game/TerrainMapLoader.ts b/src/core/game/TerrainMapLoader.ts index 5b9423a55a..69e8b98882 100644 --- a/src/core/game/TerrainMapLoader.ts +++ b/src/core/game/TerrainMapLoader.ts @@ -32,7 +32,7 @@ export interface MapManifest { } export interface Nation { - coordinates: [number, number]; + coordinates?: [number, number]; flag?: string; name: string; } @@ -69,10 +69,12 @@ export async function loadTerrainMap( if (mapSize === GameMapSize.Compact) { manifest.nations.forEach((nation) => { - nation.coordinates = [ - Math.floor(nation.coordinates[0] / 2), - Math.floor(nation.coordinates[1] / 2), - ]; + if (nation.coordinates !== undefined) { + nation.coordinates = [ + Math.floor(nation.coordinates[0] / 2), + Math.floor(nation.coordinates[1] / 2), + ]; + } }); manifest.additionalNations?.forEach((nation) => { if (nation.coordinates !== undefined) { diff --git a/tests/MapConsistency.test.ts b/tests/MapConsistency.test.ts index ade0b7ee6c..ff1e71f960 100644 --- a/tests/MapConsistency.test.ts +++ b/tests/MapConsistency.test.ts @@ -294,39 +294,68 @@ describe("Map consistency", () => { const info = JSON.parse(fs.readFileSync(infoPath, "utf8")); const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); - type NationEntry = { name: string; coordinates: [number, number] }; - const infoNations: NationEntry[] = (info.nations ?? []).map( - (n: NationEntry) => ({ name: n.name, coordinates: n.coordinates }), - ); - const manifestNations: NationEntry[] = (manifest.nations ?? []).map( - (n: NationEntry) => ({ name: n.name, coordinates: n.coordinates }), - ); - - if (infoNations.length !== manifestNations.length) { - errors.push( - `${key}: nation count mismatch — info.json has ${infoNations.length}, manifest.json has ${manifestNations.length}`, - ); - continue; - } - - // Compare nations by index (order must match; names can be duplicated). - for (let i = 0; i < infoNations.length; i++) { - const inf = infoNations[i]; - const man = manifestNations[i]; - if (inf.name !== man.name) { + // ── Compare nations ────────────────────────────────────────────── + type NationEntry = { + name: string; + coordinates?: [number, number]; + }; + + function compareNationArrays( + label: string, + infoArr: NationEntry[], + manifestArr: NationEntry[], + ): void { + if (infoArr.length !== manifestArr.length) { errors.push( - `${key}: nations[${i}] name mismatch — info.json "${inf.name}" vs manifest.json "${man.name}"`, + `${key}: ${label} count mismatch — info.json has ${infoArr.length}, manifest.json has ${manifestArr.length}`, ); - continue; + return; } - const [ix, iy] = inf.coordinates; - const [mx, my] = man.coordinates; - if (ix !== mx || iy !== my) { - errors.push( - `${key}: nation "${inf.name}" (index ${i}) coordinates differ — info.json [${ix}, ${iy}] vs manifest.json [${mx}, ${my}]`, - ); + for (let i = 0; i < infoArr.length; i++) { + const inf = infoArr[i]; + const man = manifestArr[i]; + if (inf.name !== man.name) { + errors.push( + `${key}: ${label}[${i}] name mismatch — info.json "${inf.name}" vs manifest.json "${man.name}"`, + ); + continue; + } + const infHasCoords = inf.coordinates !== undefined; + const manHasCoords = man.coordinates !== undefined; + if (infHasCoords !== manHasCoords) { + errors.push( + `${key}: ${label} "${inf.name}" (index ${i}) coordinate presence differs — info.json ${infHasCoords ? "has" : "missing"} coordinates, manifest.json ${manHasCoords ? "has" : "missing"} coordinates`, + ); + continue; + } + if (inf.coordinates && man.coordinates) { + const [ix, iy] = inf.coordinates; + const [mx, my] = man.coordinates; + if (ix !== mx || iy !== my) { + errors.push( + `${key}: ${label} "${inf.name}" (index ${i}) coordinates differ — info.json [${ix}, ${iy}] vs manifest.json [${mx}, ${my}]`, + ); + } + } } } + + const toEntry = (n: NationEntry) => ({ + name: n.name, + coordinates: n.coordinates, + }); + + compareNationArrays( + "nation", + (info.nations ?? []).map(toEntry), + (manifest.nations ?? []).map(toEntry), + ); + + compareNationArrays( + "additionalNation", + (info.additionalNations ?? []).map(toEntry), + (manifest.additionalNations ?? []).map(toEntry), + ); } catch (err) { errors.push(`${key}: failed to parse JSON — ${(err as Error).message}`); } diff --git a/tests/NationCreation.test.ts b/tests/NationCreation.test.ts index ebac8eb2df..128a6543a0 100644 --- a/tests/NationCreation.test.ts +++ b/tests/NationCreation.test.ts @@ -243,6 +243,34 @@ describe("createNationsForGame: additionalNations pool", () => { expect(withoutCoords!.spawnCell).toBeUndefined(); }); + test("uses coordinates from manifest nations when provided, undefined when omitted", () => { + const manifest: ManifestNation[] = [ + { name: "WithCoords", coordinates: [10, 20] }, + { name: "WithoutCoords" }, + ]; + const random = new PseudoRandom(5); + + const nations = createNationsForGame( + makeGameStart(2), + manifest, + [], + 0, + random, + ); + + expect(nations).toHaveLength(2); + const withCoords = nations.find((n) => n.playerInfo.name === "WithCoords"); + const withoutCoords = nations.find( + (n) => n.playerInfo.name === "WithoutCoords", + ); + + expect(withCoords).toBeDefined(); + expect(withoutCoords).toBeDefined(); + expect(withCoords!.spawnCell?.x).toBe(10); + expect(withCoords!.spawnCell?.y).toBe(20); + expect(withoutCoords!.spawnCell).toBeUndefined(); + }); + test("produces unique nation names overall", () => { const manifest = makeManifestNations(3); const extras = makeAdditionalNations(["Ex1", "Ex2", "Ex3"]);