Server-authoritative Godot 4.6 strategy game. Includes the headless WebSocket game server, the browser client, and Nix packaging (server and web-export derivations plus a NixOS module) for deployment.
90 lines
3.1 KiB
GDScript
90 lines
3.1 KiB
GDScript
class_name TerrainStats
|
|
|
|
extends RefCounted
|
|
|
|
## Reads the per-terrain-type figures authored on the terrain TileSet's custom
|
|
## data layers. The tile types themselves (and their Land/Sea classification)
|
|
## still live in the TileSet; this class only looks the numbers up.
|
|
##
|
|
## Three layers are recognised:
|
|
## population_multiplier (float) scales the tile's base population
|
|
## gdp_multiplier (float) scales the tile's base GDP per capita
|
|
## movement_cost_multiplier (float) multiplier applied to a unit's travel time
|
|
##
|
|
## A value of 0 (the default for an unfilled layer) means "not authored". The
|
|
## callers pick the fallback in that case, so the game keeps running while the
|
|
## layers are being filled in.
|
|
|
|
const POPULATION_MULTIPLIER_LAYER := "population_multiplier"
|
|
const GDP_MULTIPLIER_LAYER := "gdp_multiplier"
|
|
const MOVEMENT_COST_MULTIPLIER_LAYER := "movement_cost_multiplier"
|
|
|
|
## Travel-time multiplier used for tiles with no authored movement cost.
|
|
const DEFAULT_MOVEMENT_COST_MULTIPLIER := 1.0
|
|
|
|
var _layer: TileMapLayer
|
|
var _tiles: Dictionary = {}
|
|
var _use_tiles: bool = false
|
|
|
|
|
|
func _init(layer: TileMapLayer) -> void:
|
|
_layer = layer
|
|
|
|
|
|
## Builds a stats reader from generated tile dictionaries (coords -> tile data)
|
|
## instead of a live TileMapLayer. This keeps the simulation independent of the
|
|
## scene tree.
|
|
static func from_tiles(tiles: Dictionary) -> TerrainStats:
|
|
var stats := TerrainStats.new(null)
|
|
stats._tiles = tiles
|
|
stats._use_tiles = true
|
|
return stats
|
|
|
|
|
|
## Authored population multiplier, or 0.0 when the layer is empty.
|
|
func population_multiplier(coords: Vector2i) -> float:
|
|
return float(_value(coords, POPULATION_MULTIPLIER_LAYER))
|
|
|
|
|
|
## Authored GDP multiplier, or 0.0 when the layer is empty.
|
|
func gdp_multiplier(coords: Vector2i) -> float:
|
|
return float(_value(coords, GDP_MULTIPLIER_LAYER))
|
|
|
|
|
|
## Travel-time multiplier for the tile. Unset or non-positive values fall back
|
|
## to a normal cost so a unit is never frozen.
|
|
func movement_cost_multiplier(coords: Vector2i) -> float:
|
|
var value := float(_value(coords, MOVEMENT_COST_MULTIPLIER_LAYER))
|
|
return value if value > 0.0 else DEFAULT_MOVEMENT_COST_MULTIPLIER
|
|
|
|
|
|
## Cheapest authored movement cost across every tile in use. The A* heuristic
|
|
## uses it so it never overestimates the cost of a path that runs over a
|
|
## terrain that is faster than normal.
|
|
func min_movement_cost_multiplier() -> float:
|
|
var cells: Array = _tiles.keys() if _use_tiles else (
|
|
_layer.get_used_cells() if _layer != null else []
|
|
)
|
|
if cells.is_empty():
|
|
return DEFAULT_MOVEMENT_COST_MULTIPLIER
|
|
var cheapest := INF
|
|
for coords in cells:
|
|
cheapest = minf(cheapest, movement_cost_multiplier(coords))
|
|
return cheapest if cheapest < INF else DEFAULT_MOVEMENT_COST_MULTIPLIER
|
|
|
|
|
|
func _value(coords: Vector2i, layer_name: String) -> Variant:
|
|
if _use_tiles:
|
|
var tile: Dictionary = _tiles.get(coords, {})
|
|
return tile.get(layer_name, 0)
|
|
var data := _tile_data(coords)
|
|
if data == null:
|
|
return 0
|
|
return data.get_custom_data(layer_name)
|
|
|
|
|
|
func _tile_data(coords: Vector2i) -> TileData:
|
|
if _layer == null:
|
|
return null
|
|
return _layer.get_cell_tile_data(coords)
|