diff --git a/engine/src/models/world/biome.gd b/engine/src/models/world/biome.gd index 26e709af..506813a9 100644 --- a/engine/src/models/world/biome.gd +++ b/engine/src/models/world/biome.gd @@ -16,6 +16,7 @@ var flora_climax: Dictionary = { } # target values at peak quality var fauna_capacity: int = 10 # max creatures per tile var food_web_tier: String = "" +var tags: Array[String] = [] # semantic tags (is_water, is_elevated, etc.) static func from_dict(data: Dictionary) -> BiomeModel: @@ -43,6 +44,9 @@ static func from_dict(data: Dictionary) -> BiomeModel: b.flora_climax = data.get("flora_climax", {"canopy": 0.0, "undergrowth": 0.0, "fungi": 0.0}) b.fauna_capacity = data.get("fauna_capacity", 10) b.food_web_tier = data.get("food_web_tier", "") + b.tags = [] + for t: Variant in data.get("tags", []): + b.tags.append(String(t)) return b @@ -57,4 +61,5 @@ func to_dict() -> Dictionary: "flora_climax": flora_climax, "fauna_capacity": fauna_capacity, "food_web_tier": food_web_tier, + "tags": tags, } diff --git a/engine/src/models/world/biome_classifier.gd b/engine/src/models/world/biome_classifier.gd index bb3ec909..0fea69a1 100644 --- a/engine/src/models/world/biome_classifier.gd +++ b/engine/src/models/world/biome_classifier.gd @@ -29,6 +29,12 @@ static func classify(tile: Variant) -> String: ## Aquatic classification: water body type + temperature + depth. ## Falls back to biome_id when water_body_type is not set (TS guide tiles). static func _classify_aquatic(tile: Variant) -> String: + # Boil threshold: water-substrate tile too hot for liquid water → volcanic basin. + # Normalized temperature 0.82 ≈ 800°C surface temp — pre-ocean Hadean conditions. + const WATER_BOIL_TEMP: float = 0.82 + if _get_temperature(tile) >= WATER_BOIL_TEMP: + return "volcanic" + var wb_type: String = _get_water_body_type(tile) var biome: String = _get_biome_id(tile) @@ -70,7 +76,7 @@ static func _classify_land(tile: Variant) -> String: # Wetland override: saturated moisture on lowland # (mangrove already handled in classify() before water/land split) - if moisture > 0.7 and elevation < 0.4: + if moisture > 0.7 and elevation < 0.4 and canopy > 0: if temp > 0.4: return "swamp" return "bog" @@ -78,36 +84,41 @@ static func _classify_land(tile: Variant) -> String: # Elevation-driven if elevation > 0.85: if temp < 0.1: - return "permanent_ice" + return "glacial" return "alpine_tundra" if elevation > 0.70: - if moisture > 0.3: + if canopy > 0 and moisture > 0.3: return "alpine_meadow" return "alpine_tundra" if elevation > 0.55: if canopy > 0.4: return "montane_forest" - if moisture > 0.7 and temp > 0.3: + if canopy > 0 and moisture > 0.7 and temp > 0.3: return "cloud_forest" - return "alpine_meadow" + if canopy > 0 and moisture > 0.3: + return "alpine_meadow" + return "alpine_tundra" # Temperature-driven (lowland/midland) if temp > 0.55: if moisture > 0.7 and canopy > 0.6: return "tropical_rainforest" - if moisture > 0.4: - return "tropical_dry_forest" - if moisture > 0.2: - return "savanna" + # Without biology, warm moist land is bare rock — not savanna or dry forest + if canopy > 0: + if moisture > 0.4: + return "tropical_dry_forest" + if moisture > 0.2: + return "savanna" return "desert" # Temperate if temp > 0.25: if canopy > 0.5: return "temperate_forest" - if moisture > 0.3: + # Without biology, temperate moist land is bare rock — not grassland + if moisture > 0.3 and canopy > 0: return "temperate_grassland" return "chaparral" @@ -115,7 +126,9 @@ static func _classify_land(tile: Variant) -> String: if temp > 0.1: if canopy > 0.3: return "boreal_forest" - return "tundra" + if canopy > 0: + return "tundra" + return "polar_desert" return "polar_desert"