Files

144 lines
3.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Domain types — the single source of truth shared by the frontend (src/), the
// backend (server/), and the LLM prompt contract. No runtime dependencies here.
/** A nation's 0–100 status indices. Every value is clamped to 0–100 by the engine. */
export interface NationStats {
military: number;
economy: number;
stability: number;
population: number;
}
export const STAT_KEYS = ['military', 'economy', 'stability', 'population'] as const;
export type StatKey = (typeof STAT_KEYS)[number];
export type NationType = 'major' | 'minor' | 'power' | 'alliance' | 'faction';
/** Direction of a bilateral diplomatic relationship. */
export type RelationStatus = 'ally' | 'neutral' | 'rival' | 'hostile' | 'war';
export const RELATION_STATUSES: RelationStatus[] = [
'ally',
'neutral',
'rival',
'hostile',
'war',
];
/**
* A single nation in the world. `territory` holds ISO_A3 codes; the map colors
* each of those regions with this nation's stable color (see protocol.nationColor).
*/
export interface Nation {
id: string;
name: string;
type: NationType;
stats: NationStats;
territory: string[];
relations: Record<string, RelationStatus>;
}
/** Categories a narrative event can take. */
export type EventType =
| 'military'
| 'diplomatic'
| 'economic'
| 'political'
| 'social'
| 'disaster'
| 'scientific'
| 'cultural';
export const EVENT_TYPES: EventType[] = [
'military',
'diplomatic',
'economic',
'political',
'social',
'disaster',
'scientific',
'cultural',
];
export type EventOutcome = 'success' | 'failure' | 'partial' | 'neutral';
/** A narrative event the simulation emitted on some turn. */
export interface EventEntry {
id: string;
turn: number;
date: string;
nation: string; // nation id the event concerns
type: EventType;
title: string;
text: string;
outcome: EventOutcome;
}
/**
* A single item queued in the current round, waiting to be committed by a
* "Jump forward". `kind: 'action'` is a free-text player move; `kind:
* 'diplomacy'` is a message to a specific nation (`to`).
*/
export type RoundItem = {
kind: 'action' | 'diplomacy';
text: string;
to?: string;
};
/**
* Monotonic revision counter for a game, bumped by the server on every
* mutation (queue add/remove, turn commit) and persisted with the state.
* Clients use it to tell a fresh state from a stale one: an SSE `state`/`turn`
* snapshot (e.g. the on-connect resync) that arrives *after* a fresher REST
* response carries a lower version and must be ignored, never applied
* (see the client's "never go backwards" rule in src/App.tsx). Legacy games
* saved before this field existed are treated as version 0 by consumers.
*/
export type GameVersion = number;
/** The full, persisted state of one game. Saved to SQLite by id. */
export interface GameState {
id: string;
presetId: string;
presetTitle: string;
playerNationId: string;
turn: number;
date: string;
nations: Record<string, Nation>;
events: EventEntry[];
/**
* Actions + diplomatic messages queued this round but not yet committed.
* Adding an item never advances the turn; a "Jump forward" processes the whole
* batch in a single turn and clears this back to `[]`.
*/
pending: RoundItem[];
/**
* Monotonic revision counter (see GameVersion). Bumped on every mutation so
* clients can reject stale SSE snapshots; see the "never go backwards" rule
* in src/App.tsx. Starts at 1 on game creation.
*/
version: GameVersion;
}
/**
* A scenario the player can start. One JSON file per preset (presets/<id>.json).
* `worldBackground` + `simulationRules` become the LLM system prompt.
*/
export interface Preset {
id: string;
title: string;
startDate: string;
summary: string;
worldBackground: string;
simulationRules: string[];
nations: Nation[];
}
/** The lightweight manifest entry used to render the preset picker cards. */
export interface PresetMeta {
id: string;
title: string;
startDate: string;
summary: string;
}