diff --git a/scenes/world/world.gd b/scenes/world/world.gd index 09a5f86..1542287 100644 --- a/scenes/world/world.gd +++ b/scenes/world/world.gd @@ -642,6 +642,17 @@ var _build_sites_by_tile: Dictionary = {} func _on_designation_added(cell: Vector2i, tool: StringName) -> void: + # Replacement rule: painting a door on a tile occupied by a Wall (ghost or + # completed) demolishes the wall in place so the door build can proceed. + # Mirrors the Going-Medieval / Rimworld "door replaces wall" convention. + # Source of truth is World.build_queue — pre-built walls (test shed, cabin + # seed) aren't in _build_sites_by_tile but ARE in build_queue via _ready. + if tool == &"build_door": + var wall_at_cell = _find_wall_at(cell) + if wall_at_cell != null: + _demolish_wall_in_place(wall_at_cell, cell) + _build_sites_by_tile.erase(cell) # may not be present; erase is no-op + # Fall through to spawn the door ghost below. if _build_sites_by_tile.has(cell): return # already a build site here # Phase 17 — read material override from the Designation controller (may be ""). @@ -747,6 +758,45 @@ func _on_designation_added(cell: Vector2i, tool: StringName) -> void: Audit.log("world", "queued %s at %s" % [tool, cell]) +## True if `entity` is a Wall (ghost or completed). Duck-typed by script path +## to avoid registration-order issues with class_name Wall. +func _entity_is_wall(entity) -> bool: + if entity == null: + return false + var s = entity.get_script() + return s != null and s.resource_path.ends_with("/wall.gd") + + +## Finds the Wall entity at `cell` (ghost or completed), or null. Searches +## World.build_queue first since every Wall self-registers there on _ready; +## that covers both designation-painted walls and pre-built _spawn_complete_wall +## seeds. +func _find_wall_at(cell: Vector2i): + for site in World.build_queue: + if not is_instance_valid(site): + continue + if site.get("tile") != cell: + continue + if _entity_is_wall(site): + return site + return null + + +## Atomically remove a Wall entity at `cell`. Reverses Wall._complete() effects +## if the wall was already built: unstamps the wall_layer TileMap, marks the +## pathfinder cell walkable, and triggers a room recompute. Then frees the +## entity. Used by the door-replaces-wall replacement rule. +func _demolish_wall_in_place(wall, cell: Vector2i) -> void: + var was_completed: bool = wall.has_method("is_completed") and wall.is_completed() + if was_completed: + wall_layer.erase_cell(cell) + pathfinder.set_cell_walkable(cell, true) + if room_detector != null: + room_detector.recompute_around(cell) + wall.queue_free() + Audit.log("world", "wall at %s demolished (replaced by door designation)" % cell) + + ## Instantiate a Workbench ghost (in build-queue state) at `tile` with the ## given label and accepted skill. Returns the entity (already add_child'd). func _spawn_workbench(tile: Vector2i, label: String, skill: StringName):