diff --git a/engine/src/modules/ecology/flora.gd b/engine/src/modules/ecology/flora.gd index 11ad50ff..6232b57a 100644 --- a/engine/src/modules/ecology/flora.gd +++ b/engine/src/modules/ecology/flora.gd @@ -63,7 +63,7 @@ func process_turn(game_map: RefCounted) -> void: var tiles: Array = game_map.tiles.values() var o2: float = game_map.o2_fraction if "o2_fraction" in game_map else 0.21 - tick_pioneer(tiles, _biome_flora, _veg) + tick_pioneer(tiles, _biome_flora, _veg, o2) tick_canopy(tiles, _biome_flora, _veg, o2) tick_undergrowth(tiles, _biome_flora, _veg, o2) tick_fungi(tiles, _biome_flora, _veg) @@ -81,12 +81,16 @@ func process_turn(game_map: RefCounted) -> void: # ========================================================================= -static func tick_pioneer(tiles: Array, biome_flora: Dictionary, veg: Dictionary) -> void: +static func tick_pioneer(tiles: Array, biome_flora: Dictionary, veg: Dictionary, o2_fraction: float = 0.21) -> void: ## Pioneer colonization: seeds bare ground with initial flora using raw climate values. ## Uses temperature/moisture directly (not biome climate range) because abiotic-classified ## tiles may have narrow climate ranges that don't match their actual climate conditions ## (e.g., a temperate tile classified as polar_desert before biology establishes). ## Does NOT run for abiotic worlds (ecology is disabled when abioticWorld = true). + ## Respects the O2 minimum threshold — no pioneer seeding in anoxic atmospheres. + var o2_mult: float = _o2_growth_mult(o2_fraction) + if o2_mult <= 0.0: + return var pioneer_rate: float = veg.get("pioneer_rate", 0.002) for tile: Variant in tiles: diff --git a/packages/engine-ts/src/EcologyPhysics.generated.ts b/packages/engine-ts/src/EcologyPhysics.generated.ts index 17973a5d..47664018 100644 --- a/packages/engine-ts/src/EcologyPhysics.generated.ts +++ b/packages/engine-ts/src/EcologyPhysics.generated.ts @@ -391,12 +391,17 @@ function _getStage(stage_index: number, stages: Record[]): Recor // Flora tick functions (auto-transpiled from flora.gd) // --------------------------------------------------------------------------- -function tickPioneer(tiles: TileState[], biomeFlora: Record>, veg: Record): void { +function tickPioneer(tiles: TileState[], biomeFlora: Record>, veg: Record, o2_fraction: number = 0.21): void { // Pioneer colonization: seeds bare ground with initial flora using raw climate values. // Uses temperature/moisture directly (not biome climate range) because abiotic-classified // tiles may have narrow climate ranges that don't match their actual climate conditions // (e.g., a temperate tile classified as polar_desert before biology establishes). // Does NOT run for abiotic worlds (ecology is disabled when abioticWorld = true). + // Respects the O2 minimum threshold — no pioneer seeding in anoxic atmospheres. + let o2_mult = _o2GrowthMult(o2_fraction) + if (o2_mult <= 0.0) { + return + } let pioneer_rate = (veg as any)["pioneer_rate"] ?? 0.002 for (const tile of tiles) { @@ -1160,7 +1165,7 @@ export class EcologyPhysics { // Flora dynamics (order matches flora.gd process_turn) const o2 = grid.o2_fraction ?? 0.21 - tickPioneer(tiles, bf, veg) + tickPioneer(tiles, bf, veg, o2) // Reclassify pioneer-seeded tiles immediately so subsequent ticks use the correct biome. // A tile seeded from canopy=0 to canopy>0 may be in an abiotic biome (polar_desert, // chaparral) that won't match its actual climate, causing tick_canopy/tick_undergrowth diff --git a/tools/sprite-generation/spritegen.db-shm b/tools/sprite-generation/spritegen.db-shm index 35ce2169..e7186dc0 100644 Binary files a/tools/sprite-generation/spritegen.db-shm and b/tools/sprite-generation/spritegen.db-shm differ diff --git a/tools/sprite-generation/spritegen.db-wal b/tools/sprite-generation/spritegen.db-wal index 4470d33c..f5638cd1 100644 Binary files a/tools/sprite-generation/spritegen.db-wal and b/tools/sprite-generation/spritegen.db-wal differ diff --git a/tools/transpile-engine/ecology_assembly.py b/tools/transpile-engine/ecology_assembly.py index c46126ad..85c339bc 100644 --- a/tools/transpile-engine/ecology_assembly.py +++ b/tools/transpile-engine/ecology_assembly.py @@ -220,7 +220,7 @@ def _read_functions(path: Path) -> dict[str, str]: # --------------------------------------------------------------------------- FLORA_TICK_SIGS: dict[str, str] = { - "tick_pioneer": "tickPioneer(tiles: TileState[], biomeFlora: Record>, veg: Record): void", + "tick_pioneer": "tickPioneer(tiles: TileState[], biomeFlora: Record>, veg: Record, o2_fraction: number = 0.21): void", "tick_canopy": "tickCanopy(tiles: TileState[], biomeFlora: Record>, veg: Record, o2_fraction: number = 0.21): void", "tick_undergrowth": "tickUndergrowth(tiles: TileState[], biomeFlora: Record>, veg: Record, o2_fraction: number = 0.21): void", "tick_fungi": "tickFungi(tiles: TileState[], biomeFlora: Record>, veg: Record): void", @@ -657,7 +657,7 @@ export class EcologyPhysics {{ // Flora dynamics (order matches flora.gd process_turn) const o2 = grid.o2_fraction ?? 0.21 - tickPioneer(tiles, bf, veg) + tickPioneer(tiles, bf, veg, o2) // Reclassify pioneer-seeded tiles immediately so subsequent ticks use the correct biome. // A tile seeded from canopy=0 to canopy>0 may be in an abiotic biome (polar_desert, // chaparral) that won't match its actual climate, causing tick_canopy/tick_undergrowth