11 KiB
Weather History & Lens System
Observation-based weather history tied to fog of war, with tech/magic-gated visualization lenses.
Core Concept
The climate simulation runs globally every turn, but players only record weather data for tiles they are actively observing (visibility state 2). This recorded history powers a replayable "Chronicle" map.
- More scouts early = richer historical dataset. A player with 3 scouts from turn 1 has 30+ turns of temperature/moisture data across wide territory by midgame.
- Fog-of-war tiles stop recording. When a scout moves away (tile drops to visibility 1), no new data is captured for that tile until another unit sees it again.
Two Separate Gates
Each lens has two unlock conditions that can differ:
| Gate | Controls | When unlocked |
|---|---|---|
recording_gate |
Whether the field is captured in observations | When your civilization has the conceptual framework + instruments to perceive this data type |
unlock / unlock_alt |
Whether you can visualize the lens | When you have the interpretive science or magic to read the data |
Why they differ: Some knowledge is innate (warm/cold, wet/dry) — your scouts recorded it from turn 1. Researching the display tech retroactively reveals that full history. But other fields require instruments or conceptual frameworks that didn't exist yet — you simply weren't measuring them, so there is no history to reveal.
Retroactive reveals vs. fresh starts
| Field | Recording starts | Display unlocked |
|---|---|---|
| temperature, moisture | Turn 1 (innate) | irrigation |
| wind direction/speed | Turn 1 (innate) | meteorology |
| tile quality | surveying (Era 1) |
horticulture (Era 3) |
| habitat suitability | animal_husbandry (Era 2) |
animal_husbandry (Era 2) |
| canopy/undergrowth | forestry (Era 3) |
horticulture or Nature T1 |
| fish stock/reef health | sailing (Era 3) |
sailing (Era 3) |
| pressure/humidity/CAPE | meteorology (Era 4) |
meteorology (Era 4) |
| sulfate aerosol | geology (Era 4) |
alchemy or Chaos T2 |
| atmospheric chemistry | natural_philosophy or Aether T2 |
same |
Derived lenses have no recording gate — they are computed from base fields already in storage:
- Change Over Time (
delta): derived from temperature + moisture. Researchscholarshipto retroactively see change trends from turn 1. - Weather Prediction: derived from pressure + wind history. Accuracy scales with how long you've had meteorology.
Strategic implications of the split
- Innate-recorded + late display tech: Researching
irrigationon turn 40 retroactively reveals 40 turns of temperature/moisture history for every tile your scouts ever visited. The scout player's advantage compounds. - Concept-gated recording: Researching
meteorologystarts recording pressure today. You cannot retroactively see pressure from before you had barometers. The earlier you research meteorology, the more pressure history you'll have when astronomy gives you the forecast lens. - The strategic bet: Do you rush
meteorologyearly to start accumulating pressure history, or save the research for higher-tier techs and accept lower forecast accuracy later?
Observation Records
Each observation is a compact 16-byte quantized snapshot of one tile's climate state:
| Field | Source | Quantization |
|---|---|---|
| temperature | [0,1] |
u8 (0-255) |
| moisture | [0,1] |
u8 |
| pressure | hPa | (hPa-970)/80 → u8 |
| wind_speed | [0,1] |
u8 |
| wind_direction | 0-5 | u8 (raw) |
| humidity | [0,1] |
u8 |
| cape | [0,1] |
u8 |
| canopy_cover | [0,1] |
u8 |
| undergrowth | [0,1] |
u8 |
| fungi_network | [0,1] |
u8 |
| reef_health | [0,1] |
u8 |
| habitat_suitability | [0,1] |
u8 |
| quality | 0-5 | u8 (raw) |
| fish_stock | [0,1] |
u8 |
| sulfate_aerosol | [0,1] |
u8 |
| succession_progress | 0-255 | u8 (raw) |
Fields are always stored at full resolution. Recording gates determine which fields get populated each turn — unreachable fields are zeroed and omitted from frame buffer output until unlocked.
Storage is sparse: only observed tiles are recorded per turn. Early game (~80 observed tiles) costs ~1.3 KB/turn. Endgame huge map with 50% visibility: ~80 KB/turn, ~12 MB total per player over 150 turns.
Lens Data Model
Lenses are defined in public/resources/lenses/ as a split directory (one JSON file per category). Each lens is a self-contained entity:
public/resources/lenses/
geography.json — terrain, rivers (innate)
climate.json — temperature, moisture, wind/pressure, delta
ecology.json — canopy, quality, wildlife, fungi, marine
atmosphere.json — atmospheric chemistry, aerosol, weather prediction
magic.json — ley lines, death sight, life sight
Lenses live in public/resources/ because they describe how to visualize Rust simulator output fields that exist on TileState regardless of which game pack is active. Game packs subscribe to lens categories via game.json "resources" — sandbox loads only geography, climate, ecology; age-of-dwarves loads all.
Each lens defines:
- rendering — layer bits, blend mode/strength, color ramp name, data sources (texture channel + field), overlay types (arrows, lines, particles)
- observation —
recording_gate(when recording starts),fields_recorded(fields captured),derived_from(retroactive computed lenses), quantization scheme - gameplay — tooltip format, alert conditions, strategic value description, forecast parameters
- unlock —
"innate"or{"tech": "tech_id"}or{"magic": {"school": "...", "tier": N}};unlock_altfor dual science/magic paths
TypeScript types: Lens, LensRendering, LensObservation, LensFieldRecordingGate in src/packages/guide/src/types/game-data.ts
Science Tech Tree
All natural philosophy techs that gate lens recording and display. Part of the natural_philosophy pillar.
Prerequisite graph
scholarship (Era 1)
├── herbalism (Era 2)
│ ├── forestry (Era 3) ─────────────────────┐
│ ├── horticulture (Era 3) ←─── surveying │
│ └── alchemy (Era 4) ←─── geology ─────────┤
└── mathematics (Era 2) │
├── sailing (Era 3) ─────────────────────── meteorology (Era 4)
└── advanced_scholarship (Era 3)
├── meteorology (Era 4) ←─── sailing, forestry
├── geology (Era 4) ←─── forestry
└── astronomy (Era 5) ←─── meteorology, master_lore
└── natural_philosophy (Era 5) ←─── alchemy, meteorology, master_lore
husbandry (Era 1)
└── animal_husbandry (Era 2) — wildlife recording + display
surveying (Era 1, no prereqs)
├── quality recording starts
├── horticulture prereq
└── sailing prereq
Lens progression by era
| Era | Lens | Layer bit | Recording gate | Display unlock |
|---|---|---|---|---|
| 1 | Terrain | 5 | innate (static) | innate |
| 1 | Rivers | 4 | innate (static) | innate |
| 1 | Temperature | 1 | innate | irrigation |
| 1 | Moisture | 2 | innate | irrigation |
| 1 | Tile Quality (recording) | 12 | surveying | — |
| 2 | Wildlife & Habitat | 14 | animal_husbandry | animal_husbandry |
| 3 | Change Over Time | 8 | innate (derived) | scholarship |
| 3 | Canopy & Undergrowth | 9+10 | forestry | horticulture or Nature T1 |
| 3 | Tile Quality (display) | 12 | — | horticulture |
| 3 | Marine Health | 13 | sailing | sailing |
| 4 | Wind (display) | 3+6 | innate | meteorology |
| 4 | Pressure (recording + display) | 3+6 | meteorology | meteorology |
| 4 | Sulfate Aerosol (recording) | 17 | geology | alchemy or Chaos T2 |
| 5 | Weather Prediction | 18 | n/a (derived) | astronomy |
| 5 | Atmospheric Chemistry | 15 | natural_philosophy or Aether T2 | same |
| 6 | Aquifer Mapping | 21 | hydrology | hydrology |
| 7 | Tectonic Activity | 22 | geophysics | geophysics |
| 8 | Ocean Currents | 23 | oceanography | oceanography |
| 9 | Climate Cycles | 24 | innate (derived) | climatology |
Magic-only lenses (magic tech tree todo):
| Era | Lens | Layer bit | Recording gate | Display unlock |
|---|---|---|---|---|
| 2 | Ley Lines | 16 | arcane_study | arcane_study |
| 4 | Fungi Network | 11 | Nature T2 | Nature T2 |
| 5 | Death Sight | 19 | Death T2 | Death T2 |
| 5 | Life Sight | 20 | Life T2 | Life T2 |
| 10 | Biosphere Health | 25 | n/a (derived) | world_theory |
Data Persistence
Observation stores are per-player and saved as part of the game state. They live in GameState.observation_stores: Dictionary (player_index → GdObservationStore), serialized as JSON strings in the save file.
GameState.get_observation_store(player_index, width, height) lazily creates and returns the store. world_map.gd calls this after _recalculate_vision() at game start and turn start.
Chronicle UI
The Chronicle panel is accessible from the world map HUD:
- Map shifts to a desaturated "memory" tint
- Timeline scrubber at bottom (turn 1 → current turn)
- Sidebar shows lens toggles — unlocked lenses can be toggled on/off, locked lenses are grayed with a tooltip showing the unlock requirement
- Scrubbing renders historical observation data on the map
- Tiles unobserved at the scrubbed turn appear black (unexplored) or dimmed (fog of war)
- Two viewing modes:
- Observation View — shows exact data from the selected turn only
- Latest Known — composite of the most recent observation per tile up to the selected turn
Implementation
- Rust crate:
src/simulator/crates/mc-observation/— ObservationRecord, TurnObservation, ObservationStore (12 unit tests) - API surfaces: GdObservationStore in api-gdext, WasmObservationStore in api-wasm — thin shims exposing record_turn, frame buffer queries, lens unlock, serialization
- Data:
public/resources/lenses/— 5 category files, 16 lens definitions (shared resources, not game-pack) - TypeScript types:
Lens+ supporting interfaces insrc/packages/guide/src/types/game-data.ts, exported from package index - Data loading:
games/age-of-dwarves/guide/src/data/game.ts—allLenses,lensById,innateLensesviaimport.meta.glob - Recording hook:
world_map.gdcallsGameState.get_observation_store()+store.record_turn()after_recalculate_vision()at both game start and turn start - Persistence:
GameState.observation_stores(player_index → GdObservationStore), serialized in save file viato_json()/from_json() - Signals:
EventBus.lens_unlocked,EventBus.chronicle_opened/closed - Guide web app: Extends existing LayerPanel with lens-gating UI for layer bits 15-20