feat: #66 contested regions / frontlines — 12th ability (v0.44.0)
CI / build + unit + e2e (pull_request) Successful in 2m10s

A third region state (contested frontline): a region in no nation's territory and not in unowned, opened/resolved by the LLM, tied to an active war. Sole-ownership invariant preserved. Durable GameState.contested; contested-inert stub; map legend stays at 7.
This commit is contained in:
2026-10-16 15:21:45 +00:00
parent d946fdf0d7
commit 5477336537
27 changed files with 1743 additions and 80 deletions
+13 -1
View File
@@ -3,7 +3,7 @@
// (The single `import type` below erases at compile time, so no runtime
// dependency on protocol.ts is introduced — see the GameState.pendingNationOps
// doc comment.)
import type { NationOp } from './protocol';
import type { NationOp, ContestedRegion } from './protocol';
/** A nation's 0–100 status indices. Every value is clamped to 0–100 by the engine. */
export interface NationStats {
@@ -474,6 +474,18 @@ export interface GameState {
* server/db.ts) — additive, no data wipe.
*/
seaZones: SeaZoneClaim[];
/**
* (v0.44.0, #66) Contested regions / frontlines (see {@link ContestedRegion}):
* one entry per region key that is a war frontline — a key in **no** nation's
* `territory` and not in `unowned`, contested by two or more nations at war.
* A region becomes contested when a turn's `contested.open` succeeds (it
* leaves its owner's `territory` and `unowned` — sole ownership is preserved);
* it is resolved (released to no man's land, or re-claimed by a party) by a
* later `contested.resolve`. At most ONE entry per region key (unique by
* `regionKey`). Always `[]` on a fresh game; rows saved before v0.44 load
* with this defaulted to `[]` (see server/db.ts) — additive, no data wipe.
*/
contested: ContestedRegion[];
}
/**