feat(climate): Add climate evaluation logic and update climate specifications in climate_spec_eval.gd and climate_spec.json

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-29 05:03:43 -07:00
parent 5a559140a1
commit 8d024c7b87
4 changed files with 16 additions and 9 deletions

View file

@ -14,6 +14,7 @@ static func ideal_terrain(tile: Variant, spec: Dictionary) -> String:
var tid: String = tile.biome_id
var temp: float = tile.temperature
var moist: float = tile.moisture
var canopy: float = tile.get("canopy_cover", 0.0)
var transitions: Dictionary = spec.get("terrain_transitions", {})
var rules: Array = transitions.get(tid, [])
@ -23,7 +24,7 @@ static func ideal_terrain(tile: Variant, spec: Dictionary) -> String:
for rule: Variant in rules:
if not rule is Dictionary:
continue
if eval_condition(rule.get("condition", ""), temp, moist, tile.elevation):
if eval_condition(rule.get("condition", ""), temp, moist, tile.elevation, canopy):
var becomes: String = rule.get("becomes", "")
if becomes == "classify":
return BiomeClassifierScript.classify(tile)
@ -32,22 +33,22 @@ static func ideal_terrain(tile: Variant, spec: Dictionary) -> String:
return tid
static func eval_condition(cond: String, temp: float, moist: float, elev: float) -> bool:
static func eval_condition(cond: String, temp: float, moist: float, elev: float, canopy: float = 0.0) -> bool:
## Evaluate a condition string from climate_spec.json.
## Supports: "field op value", "cond1 AND cond2", "cond1 OR cond2".
## Fields: temperature, moisture, elevation.
## Fields: temperature, moisture, elevation, canopy.
## Operators: <, <=, >, >=.
if " OR " in cond:
var parts: PackedStringArray = cond.split(" OR ")
for part: String in parts:
if eval_condition(part.strip_edges(), temp, moist, elev):
if eval_condition(part.strip_edges(), temp, moist, elev, canopy):
return true
return false
if " AND " in cond:
var parts: PackedStringArray = cond.split(" AND ")
for part: String in parts:
if not eval_condition(part.strip_edges(), temp, moist, elev):
if not eval_condition(part.strip_edges(), temp, moist, elev, canopy):
return false
return true
@ -61,7 +62,7 @@ static func eval_condition(cond: String, temp: float, moist: float, elev: float)
var op: String = tokens[1]
var value: float = float(tokens[2])
var actual: float = _field_value(field, temp, moist, elev)
var actual: float = _field_value(field, temp, moist, elev, canopy)
if actual == -INF:
return false
@ -83,7 +84,7 @@ static func ley_channeling_mult(tile: Variant, spec: Dictionary) -> float:
return ley_spec.get("on_ley_generic", 2.0)
static func _field_value(field: String, temp: float, moist: float, elev: float) -> float:
static func _field_value(field: String, temp: float, moist: float, elev: float, canopy: float = 0.0) -> float:
match field:
"temperature":
return temp
@ -91,6 +92,8 @@ static func _field_value(field: String, temp: float, moist: float, elev: float)
return moist
"elevation":
return elev
"canopy":
return canopy
push_warning("ClimateSpecEval: unknown field '%s'" % field)
return -INF

View file

@ -406,7 +406,7 @@
"becomes": "savanna"
},
{
"condition": "temperature <= 0.25",
"condition": "temperature <= 0.25 AND canopy > 0",
"becomes": "tundra"
}
],
@ -443,6 +443,10 @@
}
],
"tundra": [
{
"condition": "canopy <= 0",
"becomes": "polar_desert"
},
{
"condition": "canopy > 0.30 AND temperature > 0.10",
"becomes": "boreal_forest"
@ -458,7 +462,7 @@
],
"polar_desert": [
{
"condition": "temperature > 0.10",
"condition": "temperature > 0.10 AND canopy > 0",
"becomes": "tundra"
}
],