magicciv/public/games/age-of-dwarves/docs/WEATHER_HISTORY.md
2026-04-07 17:52:04 -07:00

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. Research scholarship to 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 irrigation on 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 meteorology starts 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 meteorology early 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)
  • observationrecording_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_alt for 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:

  1. Map shifts to a desaturated "memory" tint
  2. Timeline scrubber at bottom (turn 1 → current turn)
  3. Sidebar shows lens toggles — unlocked lenses can be toggled on/off, locked lenses are grayed with a tooltip showing the unlock requirement
  4. Scrubbing renders historical observation data on the map
  5. Tiles unobserved at the scrubbed turn appear black (unexplored) or dimmed (fog of war)
  6. 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 in src/packages/guide/src/types/game-data.ts, exported from package index
  • Data loading: games/age-of-dwarves/guide/src/data/game.tsallLenses, lensById, innateLenses via import.meta.glob
  • Recording hook: world_map.gd calls GameState.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 via to_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