extends SceneTree # Forgejo #224: a structure may not be built on top of one that is already # there. # # It matters beyond the look of it. BuildingField decides which structure the # fleet is standing on by testing the centroid against interact_radius and # returning the FIRST match, so two structures inside one another make that a # question of list order rather than of where the player flew - you would fly # to your workshop and open the crafter. # # Covers: # A. the spot a structure already occupies is refused # B. clear of it, the same build goes through # C. a unique structure can still be rebuilt where the old one stands, # because the old one is what is being replaced # D. and a REFUSED rebuild does not demolish the existing one first var game_state: Node var scene: Node var controller: Node var fleet: Node2D var elapsed: float = 0.0 var stage: int = 0 func _initialize() -> void: game_state = root.get_node("GameState") game_state.buildings.clear() game_state.extractors.clear() game_state.reset_progression() game_state.reset_new_game() game_state.intro_active = false game_state.reset_quests() game_state.rocks = 100000 game_state.bank_inventory.set_amount(GameState.RESOURCE_GAS, 100000) game_state.bank_inventory.set_amount(GameState.RESOURCE_ENERGY, 100000) scene = load("res://world/Main.tscn").instantiate() root.add_child(scene) current_scene = scene controller = scene.get_node("BuildController") fleet = scene.get_node("Fleet") func _process(delta: float) -> bool: elapsed += delta if elapsed < 0.4: return false var sep: float = GameState.MIN_BUILDING_SEPARATION # _confirm_build() returns SILENTLY on a locked structure, which would # make every refusal below look like the separation rule working when it # was only the unlock gate. Unlock what this test builds, and assert it. for type in [GameState.BUILDING_COMMAND_CENTER, GameState.BUILDING_MOTHERSHIP, GameState.BUILDING_SHIP_CRAFTER]: var node_id: String = game_state.research_node_for_building(type) if node_id != "" and not game_state.is_unlocked(node_id): game_state.unlocked.append(node_id) if not game_state.is_building_unlocked(type): return _fail("'%s' is still locked, so a refusal would prove nothing" % type) # --- A. the occupied spot is refused -------------------------------- game_state.buildings.clear() game_state.add_building(GameState.BUILDING_SHIP_CRAFTER, Vector2.ZERO) if game_state.building_blocking(Vector2.ZERO).is_empty(): return _fail("a structure's own position reads as a clear spot") # Just inside the separation, and just outside it. if game_state.building_blocking(Vector2(sep - 1.0, 0.0)).is_empty(): return _fail("a spot %.0f units away is not blocked, but the rule is %.0f" % [sep - 1.0, sep]) if not game_state.building_blocking(Vector2(sep + 1.0, 0.0)).is_empty(): return _fail("a spot %.0f units away is blocked, but the rule is %.0f" % [sep + 1.0, sep]) var blocker: Dictionary = game_state.building_blocking(Vector2.ZERO) if String(blocker.get("type", "")) != GameState.BUILDING_SHIP_CRAFTER: return _fail("the blocking structure is reported as '%s'" % blocker.get("type", "")) print("a spot inside %.0f units of a structure is refused, outside it is clear" % sep) # --- B. and the whole build path agrees ----------------------------- # On top of the crafter: refused, and nothing is built or spent. fleet.teleport_to(Vector2.ZERO) var before: int = game_state.buildings.size() controller.pending_build_type = GameState.BUILDING_COMMAND_CENTER controller._confirm_build() if game_state.buildings.size() != before: return _fail("a structure was built on top of the crafter") print("the build path refuses it too: %s" % controller.build_menu.status.text) # Clear of it: the same build goes through. fleet.teleport_to(Vector2(sep * 3.0, 0.0)) controller.pending_build_type = GameState.BUILDING_COMMAND_CENTER controller._confirm_build() if game_state.buildings.size() != before + 1: return _fail("the same build was refused on a clear spot") print("clear of it, the same build goes through") # --- C. a unique structure replaces itself in place ----------------- game_state.buildings.clear() game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO) fleet.teleport_to(Vector2.ZERO) if not game_state.building_blocking(Vector2.ZERO, GameState.BUILDING_MOTHERSHIP).is_empty(): return _fail("a unique structure blocks its own replacement") controller.pending_build_type = GameState.BUILDING_MOTHERSHIP controller._confirm_build() if game_state.buildings.size() != 1: return _fail("rebuilding the mothership in place left %d structures" % game_state.buildings.size()) if String(game_state.buildings[0].type) != GameState.BUILDING_MOTHERSHIP: return _fail("the rebuilt structure is not a mothership") print("a unique structure can still be rebuilt where the old one stood") # --- D. a refused rebuild demolishes nothing ------------------------ # The mothership is unique and is removed before the new one is placed. # Something ELSE in the way has to be caught before that demolition, or # the player is left with neither. game_state.buildings.clear() var keep: int = game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO) game_state.add_building(GameState.BUILDING_SHIP_CRAFTER, Vector2(sep * 4.0, 0.0)) fleet.teleport_to(Vector2(sep * 4.0, 0.0)) controller.pending_build_type = GameState.BUILDING_MOTHERSHIP controller._confirm_build() if game_state.find_building(keep).is_empty(): return _fail("a refused rebuild demolished the existing mothership - the player is left with neither") if game_state.buildings.size() != 2: return _fail("a refused rebuild changed the structure count to %d" % game_state.buildings.size()) print("a refused rebuild leaves the existing structure standing") print("TEST_BUILDING_SEPARATION: PASS") quit(0) return true func _fail(message: String) -> bool: print("TEST_BUILDING_SEPARATION: FAIL (%s)" % message) quit(1) return true