magicciv/engine/docs/ENVIRONMENT_ARCHITECTURE.md
Claude Code 9bc36d88ca docs(docs-specific): 📝 Update engine-specific documentation in the docs directory
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-03-28 21:31:38 -07:00

11 KiB

Environment Architecture: Worlds, Planes, and Environment Packages

Overview

The engine supports multiple distinct environments — surfaces of planets, underground caverns, ethereal planes, extra-solar worlds. Each environment has its own biome set, climate rules, terrain generation, and ecological systems. Environments are composable: they subscribe to shared biome collections rather than defining everything from scratch.

Hierarchy

Game Pack (e.g., "Age of Dwarves")
 ├── declares Worlds and Planes
 │    ├── World: "surface" → environment: earth-environment
 │    ├── World: "underground" → environment: cave-environment
 │    └── Plane: "ethereal" → environment: ether-plane
 │
 └── each environment subscribes to Biome Collections
      ├── earth-environment → [aquatic, temperate, tropical, arctic, elevated]
      ├── cave-environment → [subterranean, aquatic-underground]
      └── ether-plane → [ethereal]

Worlds vs Planes

Concept World Plane / Dimension
What A physical body (planet, moon, asteroid) An alternate reality overlaid on a world
Examples Earth surface, Mars, underground caverns Ethereal Plane, Shadow Dimension
Map Independent hex grid with own generation May share spatial coordinates with a world
Climate Has full climate physics (temp, moisture, wind) May have simplified or alien physics
Traversal Travel between worlds = long-range (portals, ships) Plane shift = local (enter/exit at same position)

Both worlds and planes are environments from the engine's perspective — they have biomes, tiles, and rules. The distinction matters for gameplay (how you get there, what rules apply) but not for the engine's data architecture.

Environment Packages

An environment package is a self-contained definition of "what exists in this world."

Package Contents

environment-package/
 ├── biomes.json          — biome definitions + tags (subscribed collections merged)
 ├── climate_params.json  — climate physics tuning (or null for no-climate worlds)
 ├── climate_spec.json    — biome classification thresholds
 ├── substrates.json      — geological substrates
 ├── generation/          — terrain generation rules
 │    ├── terrain_fractions.json
 │    └── placement_rules.json
 └── manifest.json        — package metadata, subscribed collections

manifest.json

{
  "id": "earth-environment",
  "name": "Earth-like Environment",
  "description": "Temperate planet with oceans, continents, and full climate simulation.",
  "has_climate": true,
  "has_weather": true,
  "has_ecology": true,
  "subscribes": [
    "biomes/aquatic",
    "biomes/temperate",
    "biomes/tropical",
    "biomes/arctic",
    "biomes/elevated"
  ]
}

Subscription Model

Environment packages don't duplicate biome definitions. They subscribe to biome collections — shared libraries of biome definitions that multiple environments can reuse.

Biome Collections (shared resources)
 ├── biomes/aquatic      — ocean, coast, deep_ocean, coral_reef, lake, pond, river, ...
 ├── biomes/temperate    — forest, grassland, plains, chaparral, bog, ...
 ├── biomes/tropical     — jungle, savanna, mangrove, tropical_rainforest, ...
 ├── biomes/arctic       — tundra, polar_desert, glacial, sea_ice, ...
 ├── biomes/elevated     — mountains, hills, alpine_meadow, alpine_tundra, ...
 ├── biomes/subterranean — cave, fungal_forest, underground_lake, crystal_cavern, ...
 ├── biomes/magical      — enchanted_forest, mana_node, corrupted_wasteland, ...
 └── biomes/ethereal     — spirit_mist, void_rift, planar_nexus, ...

A new planet only needs to declare which collections it subscribes to. Tags, climate ranges, flora climax values, and fauna capacity all come from the collection definitions.

An environment can also define local overrides — biomes unique to that world that don't belong in any shared collection. These are declared directly in the package's biomes.json alongside the subscribed collections.

Biome Tags

Every biome declares semantic tags. The engine queries tags — never biome ID strings.

Core Tags (engine-defined contract)

These tags have engine-level meaning. The engine's climate, generation, and ecology systems query them. Any environment package must use these tags correctly for engine systems to function.

Tag Meaning Engine Usage
is_water Liquid water body Evaporation, hydrology, navigation, marine ecology
is_elevated High terrain Wind blocking, rain shadow, generation, movement
is_frozen Ice/snow covered Freeze/thaw physics, temperature transitions
has_vegetation Supports plant life Evapotranspiration, ecology, fire spread
is_volcanic Active volcanism Aerosol injection, deep earth water, terrain skip
is_dry Arid terrain River generation skip, dust aerosol
is_wetland Saturated ground Substrate classification, drainage
is_coast Land-water boundary Marine resources, coastal weather, reef systems
is_grassland Open low vegetation Building placement, terrain fallback

Custom Tags (environment-defined)

Environment packages can define additional tags for game-pack-specific logic:

Tag Example Usage
is_magical Mana generation, spell interactions
is_corrupted Corruption spreading, purification targets
is_subterranean No sky, different light rules
is_ethereal Plane-specific movement, visibility

The engine ignores tags it doesn't recognize. Game-pack systems (spells, mana, etc.) can query custom tags through the same BiomeRegistry API.

BiomeRegistry

The BiomeRegistry is an engine autoload that provides tag-based biome queries. It is scoped per environment — each world/plane has its own registry instance with its own biome set.

API

BiomeRegistry.has_tag(biome_id: String, tag: String) -> bool
BiomeRegistry.get_tags(biome_id: String) -> Array[String]
BiomeRegistry.get_biomes_with_tag(tag: String) -> Array[String]
BiomeRegistry.register_runtime_biome(biome_id: String, tags: Array[String])

Multi-Environment Scoping

For Age of Dwarves (single surface world), BiomeRegistry loads the earth-environment biomes at startup. Future multi-world games will need per-environment registries:

# Future API (not implemented yet):
var surface_registry = BiomeRegistry.for_environment("earth-environment")
var cave_registry = BiomeRegistry.for_environment("cave-environment")

# Current API (single environment, sufficient for AoD):
BiomeRegistry.has_tag("ocean", "is_water")  # queries the active environment

The current implementation uses a single global registry. The architecture supports future per-environment scoping by making the registry data-driven (loaded from the environment package's biomes.json, not hardcoded).

Runtime Biomes

Some biomes are created dynamically during simulation (ice from frozen water, snow from temperature). These aren't in biomes.json but need tags. The registry supports runtime registration:

# Called by climate system when water freezes:
BiomeRegistry.register_runtime_biome("ice", ["is_frozen", "is_water"])

Runtime biome tags are defined in the environment package's manifest, not in engine code.

Example: Age of Dwarves Environments

Surface World (earth-environment)

Subscribes to: aquatic, temperate, tropical, arctic, elevated. ~26 biomes. Full climate simulation. Weather events. Ecological events.

Underground (cave-environment)

Subscribes to: subterranean, aquatic-underground. ~8-12 biomes (cave, fungal_forest, underground_lake, crystal_cavern, lava_tube, etc.). No climate simulation (stable temperature). No weather. Simplified ecology. Different generation rules (cave system generation vs continental).

Example: Full Magic Game Environments

Surface World (magical-earth-environment)

Subscribes to: aquatic, temperate, tropical, arctic, elevated, magical. ~35 biomes (all earth biomes + enchanted_forest, mana_node, corrupted_wasteland, etc.). Full climate + mana weather. Magical ecological events.

Ethereal Plane (ether-plane)

Subscribes to: ethereal. ~6-10 biomes (spirit_mist, void_rift, planar_nexus, essence_pool, etc.). No climate. Unique physics (mana-based). Different map topology.

Migration Path

Phase 1 (Current: Age of Dwarves)

  • BiomeRegistry autoload with tag queries
  • earth-environment biomes defined in existing biomes.json with tags added
  • Engine refactored to use BiomeRegistry.has_tag() instead of string comparisons
  • Single-environment (no per-world scoping needed yet)

Phase 2 (Underground)

  • cave-environment package with its own biomes.json
  • BiomeRegistry gains environment-scoping
  • Map system supports multiple environments (already has "layers" concept)

Phase 3 (Full Magic Game)

  • magical-earth-environment subscribes to earth collections + magical collection
  • ether-plane environment for the Ethereal Plane transit layer
  • Biome collections extracted as shared resources

Phase 4 (Extra-Solar)

  • New planet types subscribe to different collection combinations
  • Alien biome collections (silicon-based life, gas giant atmospheres, etc.)
  • Same engine, same BiomeRegistry API, completely different content

File Organization

engine/
  src/
    autoloads/
      biome_registry.gd           — tag query autoload (environment-scoped)
      data_loader.gd              — load_world() reads manifest + collections
    models/world/
      biome.gd                    — BiomeModel with tags field
    worlds/
      collections/                — shared biome collections (reusable across worlds)
        aquatic/biomes.json       — deep_ocean, shallow_ocean, coral_reef, estuary, lake, pond, river, mangrove
        arctic/biomes.json        — sea_ice, glacial, polar_desert, tundra, alpine_tundra, boreal_forest
        temperate/biomes.json     — chaparral, temperate_grassland, temperate_forest
        tropical/biomes.json      — desert, savanna, tropical_dry_forest, tropical_rainforest
        elevated/biomes.json      — hills, mountains, alpine_meadow, montane_forest, cloud_forest
        wetland/biomes.json       — swamp, bog
        special/biomes.json       — volcanic, subterranean
      earth/                      — earth-like world definition
        manifest.json             — subscribes: [aquatic, arctic, temperate, tropical, elevated, wetland, special]
        runtime_biomes.json       — biomes created during simulation (ice, snow, etc.)
        substrates.json           — geological substrates
      cave/                       — future: underground world
        manifest.json
      ethereal/                   — future: ether plane
        manifest.json

Loading Flow

  1. DataLoader.load_theme("age-of-dwarves") loads game pack data as before
  2. DataLoader.load_world("earth") reads manifest, merges collection biomes, registers runtime biomes
  3. BiomeRegistry.rebuild_from_data() rebuilds tag caches from merged biome set
  4. Game-pack biomes take precedence over collection biomes (local overrides)

Climate params and spec remain in the game pack (games/age-of-dwarves/data/) since they are tuned per game. The world definition provides the biome composition; the game pack provides the physics tuning.