From 69a1c0de445ae8b45c76e61f32d7fee740e003c5 Mon Sep 17 00:00:00 2001 From: megaproxy Date: Fri, 15 May 2026 15:09:19 +0100 Subject: [PATCH] ConstructionProvider: skip unreachable build sites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Player reported pawns ignoring chop designations. Root cause was a lingering door designation at (36, 27) — painted on the test shed wall, which is pre-built and impassable. ConstructionProvider (priority 6) kept offering the doomed job; Decision picked it over chop (priority 5); JobRunner cancelled the empty walk each tick. Busy-spin starved all elective work. Mirrors the reachability pattern from HaulingProvider / DoctorProvider / EatProvider. For pathing-blocking sites (walls) we probe from an adjacent walkable cell; for other sites (doors / beds / crates / torches) we probe the site tile directly. Unreachable sites are skipped silently so the queue can sit dormant without starving lower-priority work. Verified via MCP: with a deliberately-unreachable door designation in the queue, all three pawns successfully picked up chop jobs. Co-Authored-By: Claude Opus 4.7 (1M context) --- scenes/ai/construction_provider.gd | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scenes/ai/construction_provider.gd b/scenes/ai/construction_provider.gd index 2143bf5..fd97669 100644 --- a/scenes/ai/construction_provider.gd +++ b/scenes/ai/construction_provider.gd @@ -48,6 +48,19 @@ func find_best_for(pawn) -> Job: continue if Job.is_target_taken_by_other(site, pawn): continue + # Reachability — same pattern as HaulingProvider. Without this gate, an + # unreachable build site (e.g. a door painted on a tile that already has + # a pre-built wall) is offered every tick, Decision picks it over lower- + # priority work like chop, and JobRunner cancels the doomed walk each + # tick — a busy-spin that starves all elective work. For pathing-blocking + # sites (walls) we check from an adjacent tile; for others, from the tile. + var probe_tile: Vector2i = site.tile + if site.has_method("blocks_pathing_when_complete") and site.blocks_pathing_when_complete(): + probe_tile = _find_adjacent_walkable(site.tile, pawn.tile) + if probe_tile == site.tile: + continue # no walkable neighbour at all → boxed-in site, skip silently + if pawn.tile != probe_tile and World.pathfinder.find_path(pawn.tile, probe_tile).is_empty(): + continue var d: int = abs(site.tile.x - pawn.tile.x) + abs(site.tile.y - pawn.tile.y) if d < best_dist: best_dist = d