128 lines
4.2 KiB
GDScript
128 lines
4.2 KiB
GDScript
class_name FaunaSimplified
|
|
extends RefCounted
|
|
## Tile-level fauna approximation for the guide (no individual creatures, no SQLite).
|
|
##
|
|
## Transpiler-friendly: static tick functions accept flat tile arrays + plain
|
|
## Dictionary params. No DataLoader, EventBus, preload, or HexUtils calls
|
|
## inside tick loops. Tiles accessed by col/row (not position Vector2i).
|
|
|
|
|
|
# =========================================================================
|
|
# Transpilable tick functions
|
|
# =========================================================================
|
|
|
|
|
|
static func tick_fish_stock(tiles: Array, marine_params: Dictionary) -> void:
|
|
## Logistic fish reproduction on water tiles.
|
|
## Seed empty water tiles at 10% capacity x tempMult.
|
|
var repro_rate: float = marine_params.get("reproduction_rate", 0.05)
|
|
var cap_base: float = marine_params.get("fish_capacity", 100.0)
|
|
var reef_bonus: float = marine_params.get("reef_bonus", 0.5)
|
|
var reef_penalty: float = marine_params.get("reef_penalty", -0.5)
|
|
var seed_fraction: float = marine_params.get("seed_fraction", 0.1)
|
|
|
|
for tile: Variant in tiles:
|
|
if not _is_water(tile):
|
|
continue
|
|
|
|
var temp_mult: float = _temp_mult(tile.temperature)
|
|
var cap: float = cap_base
|
|
if tile.reef_health > 0.5:
|
|
cap *= (1.0 + reef_bonus)
|
|
elif tile.reef_health < 0.1:
|
|
cap *= maxf(0.1, 1.0 + reef_penalty)
|
|
|
|
var stock: float = float(tile.fish_stock)
|
|
|
|
# Seed empty water tiles
|
|
if stock <= 0.0:
|
|
tile.fish_stock = int(cap * seed_fraction * temp_mult)
|
|
continue
|
|
|
|
var growth: float = repro_rate * temp_mult * stock * (1.0 - stock / cap)
|
|
tile.fish_stock = clampi(int(stock + growth), 0, int(cap))
|
|
|
|
|
|
static func tick_habitat_suitability(tiles: Array, w: int, h: int) -> void:
|
|
## Per land tile: average flora in radius-1 neighbors.
|
|
## undergrowth x 0.6 + canopy x 0.2 + fungi x 0.2.
|
|
for i: int in tiles.size():
|
|
var tile: Variant = tiles[i]
|
|
if _is_water(tile):
|
|
continue
|
|
|
|
var col: int = tile.col
|
|
var row: int = tile.row
|
|
var total_ug: float = tile.undergrowth
|
|
var total_ca: float = tile.canopy_cover
|
|
var total_fn: float = tile.fungi_network
|
|
var count: int = 1
|
|
|
|
# Radius-1 neighbors via even-q offset
|
|
var nb_offsets: Array = _get_neighbor_offsets(col)
|
|
for off: Variant in nb_offsets:
|
|
var nc: int = col + off[0]
|
|
var nr: int = row + off[1]
|
|
if nc < 0 or nc >= w or nr < 0 or nr >= h:
|
|
continue
|
|
var ni: int = nr * w + nc
|
|
if ni < 0 or ni >= tiles.size():
|
|
continue
|
|
var ntile: Variant = tiles[ni]
|
|
if _is_water(ntile):
|
|
continue
|
|
total_ug += ntile.undergrowth
|
|
total_ca += ntile.canopy_cover
|
|
total_fn += ntile.fungi_network
|
|
count += 1
|
|
|
|
if count > 0:
|
|
var avg_ug: float = total_ug / float(count)
|
|
var avg_ca: float = total_ca / float(count)
|
|
var avg_fn: float = total_fn / float(count)
|
|
tile.habitat_suitability = avg_ug * 0.6 + avg_ca * 0.2 + avg_fn * 0.2
|
|
else:
|
|
tile.habitat_suitability = 0.0
|
|
|
|
|
|
static func tick_reef_health(tiles: Array, marine_params: Dictionary) -> void:
|
|
## Reef growth in ideal temperature range.
|
|
var growth_rate: float = marine_params.get("reef_growth_rate", 0.02)
|
|
var ideal_min: float = marine_params.get("reef_ideal_min", 0.55)
|
|
var ideal_max: float = marine_params.get("reef_ideal_max", 0.75)
|
|
|
|
for tile: Variant in tiles:
|
|
if not _is_water(tile):
|
|
continue
|
|
if tile.temperature >= ideal_min and tile.temperature <= ideal_max:
|
|
tile.reef_health = minf(1.0, tile.reef_health + growth_rate)
|
|
|
|
|
|
# =========================================================================
|
|
# Pure static helpers
|
|
# =========================================================================
|
|
|
|
|
|
static func _temp_mult(temperature: float) -> float:
|
|
## Temperature multiplier for fish: tropical=1.0, temperate=0.8, polar=0.5.
|
|
if temperature > 0.55:
|
|
return 1.0
|
|
if temperature > 0.25:
|
|
return 0.8
|
|
return 0.5
|
|
|
|
|
|
static func _is_water(tile: Variant) -> bool:
|
|
if "substrate_id" in tile:
|
|
var sub: String = tile.substrate_id
|
|
return sub in ["deep_water", "shallow_water", "lake_bed"]
|
|
return tile.biome_id in ["ocean", "coast"]
|
|
|
|
|
|
static func _get_neighbor_offsets(col: int) -> Array:
|
|
## Even-q offset hex neighbor deltas as [dc, dr] arrays.
|
|
var parity: int = col & 1
|
|
if parity == 0:
|
|
return [[1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [0, 1]]
|
|
else:
|
|
return [[1, 1], [1, 0], [0, -1], [-1, 0], [-1, 1], [0, 1]]
|