Per-kind movement cost through a composable movement_cost() function #79

Open
opened 2026-09-07 20:56:30 +01:00 by liamjd · 0 comments
Owner

Part of milestone #4. No dependencies.

Each terrain kind (sand, grass, snow) should cost a different amount to walk across, and A* should balance that cost against distance. AStar2D.add_point() takes a weight_scale that multiplies the cost of entering the point, and the graph is one point per tile, so per-tile cost is a one-line change at graph build time plus a test.

The costs will need to flex later: presets may apply multipliers, and roads built on the terrain will lower the cost of specific cells. So the A* weight is never read from a table directly.

Design:

  • KIND_MOVE_COST: PackedFloat32Array const on TerrainGrid next to KIND_COLORS, in enum order, base cost per kind. Water stays impassable via is_walkable(), not via a high cost.
  • movement_cost(cell) -> float: the single function that composes the base kind cost with any modifiers. Today it returns the base cost. A preset multiplier or a road overlay changes this function and nothing else.
  • _build_navigation() passes movement_cost(cell) as weight_scale in add_point().
  • recost_cells(cells: Array[Vector2i]): calls AStar2D.set_point_weight_scale() for each cell, so a future road (or any modifier change) re-costs a handful of points without rebuilding the graph. Same argument #56 makes for remove_point() over a full rebuild. No caller yet; it exists so roads never reach into _astar from outside.
  • path_cost(from, to) -> float or equivalent, so tests can assert on cost and not only on the route.

AC:

  • Tests in test/unit/test_terrain_navigation.gd (shared with #56) using a real TerrainGrid with flat amplitude and hand-set levels: a path detours around a band of costly ground when the detour is short, and goes straight through it when the detour is long.
  • recost_cells() changes the chosen route on the next find_path() without generate() being called.
  • movement_cost() is the only reader of KIND_MOVE_COST.
Part of milestone #4. No dependencies. Each terrain kind (sand, grass, snow) should cost a different amount to walk across, and A* should balance that cost against distance. `AStar2D.add_point()` takes a `weight_scale` that multiplies the cost of entering the point, and the graph is one point per tile, so per-tile cost is a one-line change at graph build time plus a test. The costs will need to flex later: presets may apply multipliers, and roads built on the terrain will lower the cost of specific cells. So the A* weight is never read from a table directly. Design: - `KIND_MOVE_COST: PackedFloat32Array` const on `TerrainGrid` next to `KIND_COLORS`, in enum order, base cost per kind. Water stays impassable via `is_walkable()`, not via a high cost. - `movement_cost(cell) -> float`: the single function that composes the base kind cost with any modifiers. Today it returns the base cost. A preset multiplier or a road overlay changes this function and nothing else. - `_build_navigation()` passes `movement_cost(cell)` as `weight_scale` in `add_point()`. - `recost_cells(cells: Array[Vector2i])`: calls `AStar2D.set_point_weight_scale()` for each cell, so a future road (or any modifier change) re-costs a handful of points without rebuilding the graph. Same argument #56 makes for `remove_point()` over a full rebuild. No caller yet; it exists so roads never reach into `_astar` from outside. - `path_cost(from, to) -> float` or equivalent, so tests can assert on cost and not only on the route. AC: - Tests in `test/unit/test_terrain_navigation.gd` (shared with #56) using a real `TerrainGrid` with flat amplitude and hand-set levels: a path detours around a band of costly ground when the detour is short, and goes straight through it when the detour is long. - `recost_cells()` changes the chosen route on the next `find_path()` without `generate()` being called. - `movement_cost()` is the only reader of `KIND_MOVE_COST`.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
liamjd/UntitledColonyBuilder#79
No description provided.