Placed buildings block pathfinding #98
No reviewers
Labels
No labels
assets
bug
duplicate
enhancement
help wanted
invalid
question
testing
ui
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
liamjd/UntitledColonyBuilder!98
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "56-buildings-block-pathfinding"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Placed buildings now block pathfinding.
TerrainGridgains a blocked-cell set consulted byis_walkable(), plusblock_cells()/unblock_cells()that update the A* graph incrementally, andgame.gddrives them from theGameState.building_placed/building_removedsignals.Closes #56.
Decisions taken
Both were open in the issue body and are pinned by tests rather than left to a comment.
A reroll clears the blocked set.
generate()empties_blockedbefore_build_navigation(). A new map is a new world, and buildings whose anchors are now in the sea are meaningless; a blocked set that outlives its terrain corrupts_main_regionand therefore pawn spawning. Pinned bytest_regenerating_clears_the_blocked_set.The graph is driven by the signals, not by
Buildings.game.gdlistens and callsmap.block_cells(), so rubble, reservations and hazards can block cells later without going through the registry.Buildingsnever touches_astar.The cost of that second choice, because #83 lands on it directly: the listener runs during the emission, so the graph is stale for the duration of the signal, and listener order becomes load-bearing for any other listener that paths inside its own handler. #83's re-path handler must connect after the navigation listener, or defer a frame. The guarantee that holds unconditionally is the weaker one — the graph is current by the time
place()returns.Correction to the issue body
The issue says blocking is "one call per occupied cell and none of the cliff-edge or diagonal-corner logic needs re-running". That is true for cliff edges and false for diagonal corners, and the difference is a pawn walking through the corner of a building. Recorded in full at #56 (comment 582).
remove_point()drops the edges incident to the point, but_can_traverse()gives a cell authority over edges it is not an endpoint of. For a blocked cellC = (10, 10), the diagonal(10, 11) -> (11, 10)hasside_b == C, so blockingCmust close it — yetCis neither endpoint andremove_point(C)leaves it intact. Four such edges per cell, joining its orthogonal neighbours in pairs.So both paths update
_blocked, add or remove the points, and then call one shared_refresh_connections_around()that re-derives every edge in the 3x3 around each changed cell from_can_traverse(). Neither path carries bespoke edge logic, so neither can disagree with the other, and theHALF_NEIGHBOURSasymmetry the issue warns about never arises —HALF_NEIGHBOURSstays confined to the full sweep in_build_navigation()where it is correct.Testing
Unit — 32 new tests, written before the implementation. Full suite: 374 passing, 0 failing, 1585 asserts.
test/unit/test_terrain_navigation.gd(23) covers the issue's list plus four cases it did not name:unblock_cells()must re-ask the terrain, so a water cell never gains a point_main_regionas the larger half, not "landmass minus wall" — the case a_main_regiononly recomputed ingenerate()gets wrongtest/unit/test_buildings_block_navigation.gd(9) covers the registry seam: the L-shape's hole stays walkable (a bounding-box implementation passes every 1x1 test and fails this one), rotation is derived from the samecells()call, removal hands cells back, and removing one of two adjacent buildings leaves the survivor blocking.Headed —
tools/screenshot_building_blocks_path.gd, 12 checks, all passing.gl_compatibilitydraws nothing headless, so this runs headed against the realgame.tscnand saves four PNGs totmp/. It wires nothing by hand, which is what makes thegame.gdlistener genuinely covered — unhook it and CHECK 1 fails.From the run: main region 256 -> 247 on a 9-cell footprint (exact; no severed region), detour 18 waypoints against 16 direct, and back to 16 after removal — so
unblock_cells()leaves no invisible wall where a building stood.The screenshots show a pawn walking, the rocket dropped on its trail, the trail visibly doglegging around the rocket once the move is re-issued, and the ground clear again after removal.
Not covered
EXPECTEDso it does not read as a defect of this change.game.gdconnection is not covered by the unit suite.test_buildings_block_navigation.gdmirrors the listener rather than invoking it, becausegame.gdneeds the whole scene and cannot be built headless — delete the connection and those 9 tests still pass. The headed probe is what guards it.test_unblocking_restores_exactly_the_connections_it_had;AStar2Dbreaks ties between two equally short routes on internal ordering, so waypoint-for-waypoint equality would assert an implementation detail of the engine rather than of this issue.🤖 Generated with Claude Code
https://claude.ai/code/session_01KDzLtT2uajq2ozyFVydZeG