feat(transpile-engine): Add ecology and map generation assembly transformation logic for transpile engine

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-26 00:29:35 -07:00
parent 1ddb6574b0
commit 06641ebd32
3 changed files with 80 additions and 4 deletions

View file

@ -135,9 +135,9 @@ def _classifier() -> str:
function classifyBiome(tile: TileState): string {
const sub = tile.substrate_id
// Aquatic tiles keep their terrain_id
// Aquatic tiles keep their biome_id
if (sub === 'deep_water' || sub === 'shallow_water' || sub === 'lake_bed') {
return tile.terrain_id
return tile.biome_id
}
const temp = tile.temperature
@ -182,7 +182,7 @@ function isWater(tile: TileState): boolean {
if (sub) {
return sub === 'deep_water' || sub === 'shallow_water' || sub === 'lake_bed'
}
return tile.terrain_id === 'ocean' || tile.terrain_id === 'coast'
return tile.biome_id === 'ocean' || tile.biome_id === 'coast'
}
function climateMatch(tile: TileState, biome: BiomeDef): number {

View file

@ -233,6 +233,7 @@ class GenMap {
readonly width: number
readonly height: number
readonly tiles: Map<string, GenTile> = new Map()
sea_level = 0.0
constructor(width: number, height: number) {
this.width = width
@ -300,6 +301,8 @@ class GenMap {
height: this.height,
global_avg_temp: 0.5,
ocean_dead_fraction: 0.0,
ecosystem_health: 1.0,
sea_level: this.sea_level,
}
}
}
@ -630,6 +633,7 @@ function assignSeaLevel(
Math.max(Math.round(oceanTarget * allElevs.length), 0), allElevs.length - 1,
)
const seaLevel = allElevs[seaIdx]
gm.sea_level = seaLevel
for (const tile of gm.tiles.values()) {
const elev = elevation.get(axialKey(tile.axial)) ?? 0.0

View file

@ -356,6 +356,7 @@ export class ClimatePhysics {
this.stepDeepEarthWater(grid)
this.stepPrecipitation(grid)
this.stepTerrainEvolution(grid)
this.stepSeaLevel(grid)
const events = this.stepEcologicalEvents(grid, turn, seed)
this.stepAnchorDecay(grid)
this.stepGlobalStats(grid)
@ -771,7 +772,31 @@ def _emit_terrain_evolution() -> str:
const tid = tile.biome_id
if (tile.is_natural_wonder) continue
if (tid === 'ocean' || tid === 'coast' || tid === 'lake' || tid === 'volcano') continue
// Water freezing/thawing ice forms below freeze threshold, thaws above
const freezeTemp = this.p('water_freeze_threshold', 0.12)
const thawTemp = freezeTemp + 0.03 // hysteresis prevents oscillation
const isWater = tid === 'ocean' || tid === 'coast' || tid === 'lake' || tid === 'inland_sea'
if (isWater) {
if (tile.temperature < freezeTemp) {
tile.original_biome_id = tid
tile.biome_id = 'ice'
tile.quality = 1
tile.quality_progress = 0
}
continue
}
if (tid === 'ice') {
if (tile.temperature > thawTemp && tile.original_biome_id) {
tile.biome_id = tile.original_biome_id
tile.original_biome_id = ''
tile.quality = 1
tile.quality_progress = 0
}
continue
}
if (tid === 'volcano') continue
const ideal = idealTerrain(tile, this.spec)
@ -800,6 +825,52 @@ def _emit_terrain_evolution() -> str:
"""
def _emit_sea_level() -> str:
return """\
private stepSeaLevel(grid: GridState): void {
// Sea level responds to global temperature: warmer thermal expansion + ice melt higher.
// Each turn, sea_level drifts toward an equilibrium determined by global_avg_temp.
// Tiles below sea_level flood to coast/ocean; tiles above expose to land.
const sensitivity = this.p('sea_level_temp_sensitivity', 0.0008)
const eqTemp = this.p('sea_level_equilibrium_temp', 0.45)
const { tiles, width: w, height: h } = grid
// Adjust sea level: positive when warmer than equilibrium, negative when cooler
const tempAnomaly = grid.global_avg_temp - eqTemp
grid.sea_level += tempAnomaly * sensitivity
// Flood low land / expose high water
let changed = false
for (let i = 0; i < tiles.length; i++) {
const tile = tiles[i]
const isWater = tile.biome_id === 'ocean' || tile.biome_id === 'coast' ||
tile.biome_id === 'lake' || tile.biome_id === 'inland_sea'
if (!isWater && tile.elevation < grid.sea_level) {
// Land tile floods becomes coast if adjacent to land, ocean otherwise
if (tile.is_natural_wonder) continue
tile.original_biome_id = tile.biome_id
tile.biome_id = 'coast'
tile.quality = 1
tile.quality_progress = 0
changed = true
} else if (tile.biome_id === 'coast' && tile.elevation >= grid.sea_level + 0.02) {
// Coast tile exposed reclassify as land based on climate
// Small hysteresis buffer (0.02) prevents oscillation at the boundary
tile.biome_id = classifyTerrain(tile.temperature, tile.moisture, tile.elevation)
tile.quality = 1
tile.quality_progress = 0
changed = true
}
}
// Invalidate ocean distance cache if coastline changed
if (changed) this.oceanDistGridId = -1
}
"""
def _emit_anchor_decay() -> str:
return """\
private stepAnchorDecay(grid: GridState): void {
@ -848,6 +919,7 @@ def assemble(fns: dict[str, dict[str, str]]) -> str:
parts.append(f" // NOTE: {gd_name} not found in climate.gd\n")
parts.append(f" {visibility} {ts_name}(grid: GridState): void {{ }}\n\n")
parts.append(_emit_sea_level())
parts.append(_emit_ecological_events())
parts.append("\n")
parts.append(_emit_anchor_decay())