diff --git a/engine/docs/ENVIRONMENT_ARCHITECTURE.md b/engine/docs/ENVIRONMENT_ARCHITECTURE.md new file mode 100644 index 00000000..db9c5415 --- /dev/null +++ b/engine/docs/ENVIRONMENT_ARCHITECTURE.md @@ -0,0 +1,276 @@ +# 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 + +```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 + +```gdscript +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: + +```gdscript +# 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: + +```gdscript +# 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.