Skip to content

Commit dfe1659

Browse files
authored
feat: add architecture, nations, save/load, and terrain documentation
- Introduced architecture overview detailing high-level data flow and directory structure. - Documented nation system including spawning pipeline, nation class, political map overlay, and configuration. - Added save/load system documentation outlining save format, API, and IndexedDB schema. - Detailed terrain generation pipeline stages including elevation, temperature, humidity, rivers, biomes, resources, and strategic points. - Implemented NationPanel component for displaying nation details and TileInspector for tile information.
1 parent 39e11fb commit dfe1659

12 files changed

Lines changed: 1539 additions & 141 deletions

File tree

‎README.md‎

Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,19 @@ Watch nations rise and fall in a living, breathing world of emergent geopolitics
2222

2323
### Procedural World Generation
2424

25-
- **Massive maps** — 2 000 × 1 200 tile worlds rendered with chunked Canvas 2D
26-
- **Three noise layers** — elevation, temperature and humidity via seeded simplex noise + domain warping
25+
- **Massive maps** — 2 000 × 2 000 tile worlds with per-tile data (elevation, temperature, humidity, biome, river, resource, ownership)
26+
- **Web Worker pipeline** — entire terrain generation runs off the main thread with zero-copy `ArrayBuffer` transfers
2727
- **26 biomes** — from deep ocean and glaciers to tropical rainforests and volcanic peaks, with organic jittered boundaries
2828
- **Rivers** — Minecraft-style noise zero-crossing algorithm producing primary and secondary waterways with shore tapering
29+
- **8 resource types** — Timber, Stone, Iron, Gold, Fertile Soil, Fish, Fur & Game — derived from biome, ore noise, elevation and river proximity
2930
- **Strategic locations** — automatic detection of river crossings, mountain passes, straits and peninsulas scored 0–10
31+
- **Nation spawning** — 12 nations placed on habitable land with procedural names, colours, flags, governments and personality traits
32+
33+
### Tile Data System
34+
35+
- **Struct-of-arrays** — 8 typed-array layers (Float32Array / Uint8Array) instead of 4M JS objects
36+
- **WorldMap.at(x, y)** — ergonomic accessor returns a `TileInfo` snapshot with biome, resource, owner, river, shore adjacency
37+
- **Per-tile ownership** — Uint8Array layer tracking which nation owns each tile (255 = unclaimed)
3038

3139
### Rendering & Performance
3240

@@ -36,15 +44,24 @@ Watch nations rise and fall in a living, breathing world of emergent geopolitics
3644
- **Spatial-hash tooltip grid** — O(1) strategic point lookups on hover
3745
- **`{ alpha: false }` canvas context** — lets the browser skip compositing
3846

39-
### Five Overlay Modes
47+
### Seven Overlay Modes
48+
49+
| Key | Mode | Visualises |
50+
| --- | ----------- | ----------------------------------------- |
51+
| `E` | Elevation | Height map (blue → green → brown → white) |
52+
| `T` | Temperature | Heat map (blue → red) |
53+
| `H` | Humidity | Moisture map (tan → teal) |
54+
| `B` | Biome | Full 26-biome colour palette (default) |
55+
| `G` | Strategic | Biome base + highlighted strategic points |
56+
| `R` | Resource | Biome base + tinted resource deposits |
57+
| `P` | Political | Territory colours + nation borders |
4058

41-
| Key | Mode | Visualises |
42-
|-----|------|-----------|
43-
| `E` | Elevation | Height map (blue → green → brown → white) |
44-
| `T` | Temperature | Heat map (blue → red) |
45-
| `H` | Humidity | Moisture map (tan → teal) |
46-
| `B` | Biome | Full 26-biome colour palette (default) |
47-
| `S` | Strategic | Biome base + highlighted strategic points |
59+
### State Management & Persistence
60+
61+
- **Zustand store** — single source of truth for phase routing, view mode, world data, nations
62+
- **Title screen** — seed input + "New World" button with phase-based routing
63+
- **Save / Load** — binary format in IndexedDB (~68 MB per 2000×2000 world)
64+
- **Tile inspector** — hover tooltip showing biome · river · resource · strategic · nation (toggle: `I`)
4865

4966
### Desktop & Web
5067

@@ -98,55 +115,77 @@ npm run build:linux
98115

99116
## Controls
100117

101-
| Input | Action |
102-
|-------|--------|
103-
| Click + drag | Pan the camera |
104-
| Scroll wheel | Zoom in / out |
105-
| `E` | Elevation overlay |
106-
| `T` | Temperature overlay |
107-
| `H` | Humidity overlay |
108-
| `B` | Biome overlay |
109-
| `S` | Strategic overlay |
118+
| Input | Action |
119+
| ------------ | --------------------- |
120+
| Click + drag | Pan the camera |
121+
| Scroll wheel | Zoom in / out |
122+
| `E` | Elevation overlay |
123+
| `T` | Temperature overlay |
124+
| `H` | Humidity overlay |
125+
| `B` | Biome overlay |
126+
| `G` | Strategic overlay |
127+
| `R` | Resource overlay |
128+
| `P` | Political overlay |
129+
| `I` | Toggle tile inspector |
130+
| `S` | Toggle toolbar |
110131

111132
---
112133

113134
## Architecture
114135

115136
```
116137
sovereign/
117-
├── electron/ # Electron main + preload
138+
├── electron/ # Electron main + preload
118139
│ ├── main/
119140
│ └── preload/
120141
├── src/
121142
│ ├── config/
122-
│ │ └── Config.ts # Every tunable parameter in one place
143+
│ │ └── Config.ts # Every tunable parameter in one place
123144
│ ├── core/
124-
│ │ ├── ai/ # (Phase 2+) AI decision systems
145+
│ │ ├── ai/ # (Phase 4+) AI decision systems
125146
│ │ ├── camera/
126-
│ │ │ └── Camera.ts # Pan / zoom state
127-
│ │ ├── entities/ # (Phase 2+) Nations, units
147+
│ │ │ └── Camera.ts # Pan / zoom state
148+
│ │ ├── entities/
149+
│ │ │ ├── Nation.ts # Nation class (provinces, stats, personality)
150+
│ │ │ ├── NameGenerator.ts # Procedural nation names
151+
│ │ │ ├── ColorGenerator.ts # Golden-angle colour spacing
152+
│ │ │ └── FlagGenerator.ts # Procedural flag patterns
128153
│ │ ├── rendering/
129-
│ │ │ └── Renderer.ts# Chunked Canvas 2D draw loop
154+
│ │ │ └── Renderer.ts # Chunked Canvas 2D draw loop
130155
│ │ ├── simulation/
131156
│ │ │ └── Simulation.ts
157+
│ │ ├── state/
158+
│ │ │ ├── GameStore.ts # Zustand global store
159+
│ │ │ └── persistence.ts # IndexedDB save / load
160+
│ │ ├── systems/
161+
│ │ │ └── NationSpawner.ts # Greedy spawn + territory claiming
132162
│ │ ├── terrain/
133163
│ │ │ ├── biomes.ts # 26-biome classifier
134164
│ │ │ ├── rivers.ts # Noise zero-crossing rivers
165+
│ │ │ ├── resources.ts # 8 resource types generator
135166
│ │ │ ├── strategic.ts # Strategic point detection
167+
│ │ │ ├── terrain.worker.ts # Web Worker pipeline
136168
│ │ │ └── TerrainGenerator.ts # Orchestrates all layers
137-
│ │ ├── systems/ # (Phase 2+) ECS-style systems
138169
│ │ └── world/
139-
│ │ └── WorldMap.ts
140-
│ ├── styles/ # SCSS w/ tokens, mixins, components
141-
│ ├── types/ # Shared TypeScript interfaces
170+
│ │ └── WorldMap.ts # Struct-of-arrays tile storage
171+
│ ├── styles/ # SCSS w/ tokens, mixins, components
172+
│ ├── types/
173+
│ │ ├── resources.ts # ResourceType enum + metadata
174+
│ │ └── tile.ts # TileInfo / TileData interfaces
142175
│ └── ui/
143-
│ ├── App.tsx # Main React component + toolbar
144-
│ └── main.tsx # Entry point
145-
├── public/ # Static assets
146-
├── docs/ # Documentation
147-
├── electron-builder.yml # Desktop packaging config
176+
│ ├── App.tsx # Phase router
177+
│ ├── MapView.tsx # Canvas + toolbar + tooltip + worker
178+
│ ├── TitleScreen.tsx # Seed input + New World
179+
│ └── main.tsx # Entry point
180+
├── public/ # Static assets
181+
├── docs/ # Documentation
182+
│ ├── architecture.md
183+
│ ├── terrain.md
184+
│ ├── nations.md
185+
│ └── save-load.md
186+
├── electron-builder.yml # Desktop packaging config
148187
├── electron.vite.config.ts
149-
└── vite.config.ts # Web-only Vite config
188+
└── vite.config.ts # Web-only Vite config
150189
```
151190

152191
---
@@ -161,15 +200,21 @@ sovereign/
161200
- [x] Camera pan & zoom
162201
- [x] River generation (primary + secondary waterways)
163202
- [x] Strategic point detection (river crossings, mountain passes, straits, peninsulas)
164-
- [x] Five overlay modes with bottom toolbar
203+
- [x] Web Worker terrain pipeline with zero-copy transfers
165204
- [x] JSDoc documentation across all modules
166205

167-
### Phase 2 — Nation Spawning (next)
206+
### Phase 2 — Foundation & Nation Spawning ✅
168207

169-
- [ ] Nation entity system with unique traits
170-
- [ ] 5 × 5 starting territories
171-
- [ ] Political map overlay
172-
- [ ] Province selection & tooltips
208+
- [x] Struct-of-arrays tile data (WorldMap)
209+
- [x] 8 resource types derived from biome, ore noise, elevation & rivers
210+
- [x] Title screen with seed input
211+
- [x] Zustand state management + phase routing
212+
- [x] Save / Load via IndexedDB (binary format)
213+
- [x] Seven overlay modes (Elevation, Temperature, Humidity, Biome, Strategic, Resource, Political)
214+
- [x] Tile inspector tooltip
215+
- [x] Nation entity system with procedural names, colours, flags & personality
216+
- [x] Greedy spawn placement + 5 × 5 starting territories
217+
- [x] Political map overlay with territory colouring
173218

174219
### Phase 3 — Expansion & Borders
175220

@@ -188,7 +233,7 @@ sovereign/
188233
- [ ] God Mode (intervention tools)
189234
- [ ] Historical timeline & statistics
190235

191-
> See individual phase docs in `docs/` for details.
236+
> See individual phase docs in [`docs/`](docs/) for details.
192237
193238
---
194239

@@ -223,4 +268,4 @@ Contributions are welcome! The project is in early development.
223268

224269
Built with [Electron](https://www.electronjs.org/) · [React](https://react.dev/) · [TypeScript](https://www.typescriptlang.org/) · [simplex-noise](https://github.com/jwagner/simplex-noise.js)
225270

226-
</div>
271+
</div>

‎docs/architecture.md‎

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Architecture Overview
2+
3+
> Last updated: February 2026 · v0.2.0
4+
5+
Sovereign is a procedural geopolitical simulation built with **React 18 + TypeScript + Canvas 2D**, shipped as both an **Electron** desktop app and a **static web build** via Vite.
6+
7+
---
8+
9+
## High-Level Data Flow
10+
11+
```
12+
┌──────────────────────────────────────────────────────────┐
13+
│ TitleScreen │
14+
│ User picks seed → GameStore.setPhase('generating') │
15+
└──────────────┬───────────────────────────────────────────┘
16+
▼
17+
┌──────────────────────────────────────────────────────────┐
18+
│ MapView (mounts) │
19+
│ Spawns terrain Web Worker │
20+
│ ┌──────────────────────────────────────────────────────┐ │
21+
│ │ terrain.worker.ts (off main thread) │ │
22+
│ │ elevation → waterDist → temp → humidity → rivers │ │
23+
│ │ → biomes → resources → strategic → ImageData maps │ │
24+
│ │ Posts back transferable ArrayBuffers (zero-copy) │ │
25+
│ └──────────────────────────────────────────────────────┘ │
26+
│ Main thread receives → builds WorldMap + chunks │
27+
│ → spawnNations() → builds political overlay │
28+
│ GameStore.setPhase('playing') │
29+
│ │
30+
│ Render loop: Camera → frustum cull → draw chunks │
31+
│ Tooltip: WorldMap.at(x,y) → TileInfo │
32+
└──────────────────────────────────────────────────────────┘
33+
```
34+
35+
---
36+
37+
## Directory Structure
38+
39+
```
40+
src/
41+
├── config/
42+
│ └── Config.ts # Central config (every tunable param)
43+
├── core/
44+
│ ├── ai/ # (Phase 4+) AI decision systems
45+
│ ├── camera/
46+
│ │ └── Camera.ts # Pan / zoom / screen↔world transforms
47+
│ ├── entities/
48+
│ │ ├── Nation.ts # Nation class: provinces, stats, personality
49+
│ │ ├── NameGenerator.ts # Procedural fantasy nation names
50+
│ │ ├── ColorGenerator.ts # Golden-angle hue spacing for distinct colours
51+
│ │ └── FlagGenerator.ts # 6-pattern procedural flag data
52+
│ ├── rendering/
53+
│ │ └── Renderer.ts # Chunked ImageBitmap compositing + culling
54+
│ ├── simulation/
55+
│ │ └── Simulation.ts # requestAnimationFrame throttle + FPS counter
56+
│ ├── state/
57+
│ │ ├── GameStore.ts # Zustand store (single source of truth)
58+
│ │ └── persistence.ts # Save/load via IndexedDB (binary format)
59+
│ ├── systems/
60+
│ │ └── NationSpawner.ts # Spawn N nations on habitable land
61+
│ ├── terrain/
62+
│ │ ├── TerrainGenerator.ts# Elevation, temp, humidity field gen
63+
│ │ ├── biomes.ts # 26-biome classifier + colour palette
64+
│ │ ├── rivers.ts # Noise zero-crossing river system
65+
│ │ ├── resources.ts # Resource layer (Timber→Fur) from biome/noise
66+
│ │ ├── strategic.ts # Strategic point detection
67+
│ │ └── terrain.worker.ts # Web Worker orchestrating the full pipeline
68+
│ └── world/
69+
│ └── WorldMap.ts # Struct-of-arrays tile data store
70+
├── styles/ # SCSS (tokens, mixins, components)
71+
├── types/
72+
│ ├── tile.ts # TileInfo interface
73+
│ └── resources.ts # ResourceType enum + RESOURCE_META
74+
└── ui/
75+
├── App.tsx # Phase router (title → generating → playing)
76+
├── TitleScreen.tsx # Seed input + New World button
77+
├── MapView.tsx # Canvas + toolbar + tooltip inspector
78+
└── main.tsx # React entry point
79+
```
80+
81+
---
82+
83+
## Key Design Decisions
84+
85+
### Struct-of-Arrays for Tile Data
86+
87+
With 4 million tiles (2000 × 2000), storing each tile as a JS object would create massive GC pressure. Instead, each property is a flat typed array:
88+
89+
```ts
90+
elevation: Float32Array // 4M × 4 bytes = 16 MB
91+
temperature: Float32Array // 16 MB
92+
humidity: Float32Array // 16 MB
93+
biomeIds: Uint8Array // 4 MB
94+
riverMask: Uint8Array // 4 MB
95+
resourceType: Uint8Array // 4 MB
96+
resourceDensity: Uint8Array // 4 MB
97+
ownership: Uint8Array // 4 MB
98+
// Total: ~68 MB
99+
```
100+
101+
The `WorldMap.at(x, y)` method composes a `TileInfo` object on demand, so the ergonomic API exists without the memory overhead.
102+
103+
### Web Worker for Generation
104+
105+
All terrain generation (elevation, temperature, humidity, rivers, biomes, resources, strategic detection, and ImageData rendering) runs in a dedicated Web Worker. Results are posted back as **transferable** `ArrayBuffer`s — true zero-copy transfer, not structured clone.
106+
107+
### Chunked Rendering
108+
109+
The map is split into 64 × 64 tile chunks, each rasterised into an `ImageBitmap`. The renderer only draws chunks overlapping the camera viewport (frustum culling). View switching swaps cached bitmap sets into the same chunk layout without re-rasterising.
110+
111+
### Zustand State Store
112+
113+
A single zustand store (`GameStore`) holds all application state:
114+
115+
- `phase`: drives routing (`'title'` → `'generating'` → `'playing'`)
116+
- `view`: which map overlay is active
117+
- `worldMap`: the WorldMap instance (tile data)
118+
- `nations`: spawned Nation instances
119+
- `seed`, `fps`, `inspectorEnabled`, etc.
120+
121+
### Save/Load Format
122+
123+
Binary blob stored in IndexedDB:
124+
125+
```
126+
[4-byte header length (LE)] [JSON header] [binary payload]
127+
```
128+
129+
The JSON header holds metadata (seed, dimensions, strategic points). The binary payload is the concatenated typed array buffers. This avoids Base64 overhead and handles 60+ MB save files efficiently.
130+
131+
---
132+
133+
## Module Dependency Graph
134+
135+
```
136+
Config.ts ◄──────────── everything reads config
137+
│
138+
▼
139+
TerrainGenerator.ts
140+
│
141+
├──► biomes.ts
142+
├──► rivers.ts
143+
├──► resources.ts
144+
├──► strategic.ts
145+
│
146+
▼
147+
terrain.worker.ts ──(postMessage)──► MapView.tsx
148+
│
149+
├──► WorldMap.ts ◄── TileInfo / ResourceType
150+
├──► NationSpawner.ts ◄── Nation / NameGen / ColorGen / FlagGen
151+
├──► Renderer.ts ◄── Camera.ts
152+
├──► Simulation.ts
153+
└──► GameStore.ts ◄── persistence.ts
154+
```

0 commit comments

Comments
 (0)