extends SceneTree # The dev button is now a doorway rather than a one-off wave shortcut. This # exercises the real Main scene so additions and removals prove they affect # the live fleet, not only GameState bookkeeping. const VESSEL_DEFINITIONS = preload("res://vessels/VesselDefinitions.gd") const BUILDING_DEFINITIONS = preload("res://buildings/BuildingDefinitions.gd") var state: Node var scene: Node var fleet: Node var dev_button: Control var menu: Control var stage: int = 0 var elapsed: float = 0.0 func _initialize() -> void: state = root.get_node("GameState") state.start_dev_game() scene = load("res://world/Main.tscn").instantiate() root.add_child(scene) current_scene = scene fleet = scene.get_node("Fleet") dev_button = scene.get_node("UI/DevMenuButton") menu = scene.get_node("UI/DevMenu") func _process(delta: float) -> bool: elapsed += delta match stage: 0: if elapsed < 0.2: return false if state.intro_active: return _fail("the dev world opened inside the intro") if fleet.ships.size() != GameState.STARTING_SHIP_COUNT: return _fail("post-intro dev mode did not start with the normal fleet") dev_button.button.pressed.emit() if not menu.visible: return _fail("the HUD dev button opened no menu") menu.unlock_button.pressed.emit() for skill in GameState.SKILL_TREE: if not state.is_unlocked(String(skill.id)): return _fail("unlock-all missed %s" % skill.id) if state.max_fleet_size() != GameState.DEFAULT_FORMATION.size(): return _fail("unlock-all did not expose the complete fleet capacity") for definition in VESSEL_DEFINITIONS.fleet(): menu.fleet_add_requested.emit(String(definition.definition_id)) for definition in VESSEL_DEFINITIONS.fleet(): if not fleet.has_vessel_type(String(definition.definition_id)): return _fail("the menu could not add %s" % definition.display_name) print("dev menu added every fleet-eligible vessel to the live fleet") var before: int = fleet.ships.size() menu.fleet_remove_requested.emit(GameState.VESSEL_GAS_BOMBER) if fleet.ships.size() != before - 1: return _fail("the dev menu did not remove a vessel") menu.unlimited_button.pressed.emit() if not state.dev_unlimited_resources: return _fail("the unlimited-resources toggle did not turn on") for resource_type in state.resource_types(): state.bank_inventory.withdraw(resource_type, GameState.DEV_UNLIMITED_AMOUNT) state.research_points = 0 state._process(0.0) for resource_type in state.resource_types(): if state.banked_resource(resource_type) != GameState.DEV_UNLIMITED_AMOUNT: return _fail("unlimited mode did not replenish %s" % resource_type) if state.research_points != GameState.DEV_UNLIMITED_AMOUNT: return _fail("unlimited mode did not replenish research points") print("skill unlock and unlimited resources operate on persistent GameState") # --- #202: every research and production price reads zero ------- # Unlimited resources goes off first: a bottomless bank and a free # price are indistinguishable while both are on, and it is the # price this checks. menu.unlimited_button.pressed.emit() if state.dev_unlimited_resources: return _fail("the unlimited-resources toggle did not turn back off") var charged: int = _priced_things() if charged == 0: return _fail("nothing in the game carries a price to make free") menu.free_costs_button.pressed.emit() if not state.dev_free_costs: return _fail("the free-costs toggle did not turn on") if _priced_things() != 0: return _fail("%d prices survived the free-costs toggle" % _priced_things()) # The accessors are what every purchase, queue and price label # reads, so an empty bank must now afford anything. for resource_type in state.resource_types(): state.bank_inventory.set_amount(resource_type, 0) state.fleet_inventory.set_amount(resource_type, 0) state.research_points = 0 for definition in BUILDING_DEFINITIONS.all(): if not state.can_afford_resources(state.building_costs(String(definition.definition_id))): return _fail("a broke fleet could not afford the free %s" % definition.definition_id) print("free costs zeroed every research and production price") menu.free_costs_button.pressed.emit() if state.dev_free_costs: return _fail("the free-costs toggle did not turn back off") if _priced_things() != charged: return _fail("turning free costs off did not restore every price") print("prices come back exactly as they were when the toggle goes off") var wave_before: int = state.wave_number menu.wave_button.pressed.emit() if menu.visible: return _fail("starting a wave left the dev menu over the action") if state.wave_number != wave_before + 1 or not state.wave_pending: return _fail("the dev menu did not retain the immediate wave tool") state.reset_new_game() if state.dev_mode or state.dev_unlimited_resources or state.dev_free_costs: return _fail("dev cheats leaked into a normal new game") print("TEST_DEV_MENU: PASS") quit(0) return true return false # How many prices in the whole game are currently non-zero: skills, vessel # unlocks and upgrades on the research side, buildings and vessel production # on the other. Counted rather than sampled so a price added later is covered # here without anyone remembering to add it. func _priced_things() -> int: var total: int = 0 for node in GameState.SKILL_TREE: var id: String = String(node.id) total += state.skill_costs(id).size() + state.skill_research_point_cost(id) for definition in BUILDING_DEFINITIONS.all(): total += state.building_costs(String(definition.definition_id)).size() for definition in VESSEL_DEFINITIONS.all(): total += state.product_costs(String(definition.definition_id)).size() var unlock_id: String = String(definition.unlock_id) if unlock_id != "": total += state.research_costs(unlock_id).size() \ + state.research_point_cost(unlock_id) for upgrade in definition.upgrades: var upgrade_id: String = String(upgrade.upgrade_id) total += state.upgrade_cost(upgrade_id) \ + state.research_point_cost(upgrade_id) return total func _fail(message: String) -> bool: print("TEST_DEV_MENU: FAIL (%s)" % message) quit(1) return true