War as a first-class state: named War objects (sides, aims, front) + mutual-defense alliances + armistice/capitulation #56

Closed
opened 2026-10-15 17:05:42 +02:00 by kamilkrzywda · 0 comments
Owner

What

Today "at war" is a bare boolean on a Relation. Make war a first-class, named object in GameState so it can carry sides, aims, a front, and a lifecycle — and so alliances can drag nations into wars.

Mechanism

  • New entity War on GameState: { id: string; sides: string[] /* nation ids */; declaredTurn: number; aims?: string[]; status: 'active'|'armistice'|'capitulation'|'ended'; front?: string[] /* region keys */ }. GameState.wars: War[] (required, [] at creation; a legacy row loads as []).
  • TurnResult gains wars: { declare: { sides: string[]; aims?: string[] }[]; end: { id: string; outcome: 'armistice'|'capitulation'; terms?: string }[] } — both required, default [] (the required-with-default pattern used by every other field).
  • The war relation becomes derived: two nations are at war iff they sit on opposite sides of at least one active War. applyTurnResult creates/ends War objects and syncs the derived war relations. A bare war relation with no matching War degrades to an implicit single-war (back-compat).

Integration

  • A mutual-defense alliance (a relation of type alliance carrying a mutualDefense: true flag) means an attack on one member drags the other into the war (add to the attacker's War.sides).
  • Capturing a region while a war is active can be narrated as "taking territory in the war"; the front is the set of contested region keys.
  • Builds on: relations, territory sole-ownership, the canConquer military gate (unchanged).

Acceptance

  • e2e safety (critical): the stub returns wars: { declare: [], end: [] } and stays deterministic; the change is additive (legacy games load with wars: []); it must not break the UK-in-wwii-1939 e2e (empty frontier → no conquest, military 80→85, 7-item map legend).
  • Unit tests in server/engine.test.ts (declare/end, derived relation sync, mutual-defense drag-in, legacy bare-war back-compat).
  • System prompt documents wars; user prompt carries current active wars. Version bump at PR time (next = v0.33.0).
## What Today "at war" is a bare boolean on a `Relation`. Make war a **first-class, named object** in `GameState` so it can carry sides, aims, a front, and a lifecycle — and so alliances can drag nations into wars. ## Mechanism - New entity `War` on `GameState`: `{ id: string; sides: string[] /* nation ids */; declaredTurn: number; aims?: string[]; status: 'active'|'armistice'|'capitulation'|'ended'; front?: string[] /* region keys */ }`. `GameState.wars: War[]` (required, `[]` at creation; a legacy row loads as `[]`). - `TurnResult` gains `wars: { declare: { sides: string[]; aims?: string[] }[]; end: { id: string; outcome: 'armistice'|'capitulation'; terms?: string }[] }` — **both required, default `[]`** (the required-with-default pattern used by every other field). - The `war` relation becomes **derived**: two nations are at war iff they sit on opposite sides of at least one active `War`. `applyTurnResult` creates/ends `War` objects and syncs the derived `war` relations. A bare `war` relation with no matching `War` degrades to an implicit single-war (back-compat). ## Integration - A mutual-defense alliance (a `relation` of type `alliance` carrying a `mutualDefense: true` flag) means an attack on one member drags the other into the war (add to the attacker's `War.sides`). - Capturing a region while a war is active can be narrated as "taking territory in the war"; the `front` is the set of contested region keys. - Builds on: relations, territory sole-ownership, the `canConquer` military gate (unchanged). ## Acceptance - **e2e safety (critical):** the stub returns `wars: { declare: [], end: [] }` and stays deterministic; the change is **additive** (legacy games load with `wars: []`); it must **not** break the UK-in-`wwii-1939` e2e (empty frontier → no conquest, military 80→85, 7-item map legend). - Unit tests in `server/engine.test.ts` (declare/end, derived relation sync, mutual-defense drag-in, legacy bare-war back-compat). - System prompt documents `wars`; user prompt carries current active wars. Version bump at PR time (next = v0.33.0).
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OpenCode/History#56