extends SceneTree # Headless test: run with # godot --headless -s res://tests/test_dev_mode.gd # A "Dev Mode" start for play-testing: one of every planet type next to the # spawn, every resource banked, and research points to spend. Offered by the # main menu only when the game is run from Godot, alongside the map editor. # # The same change took the training wheels off a NORMAL game: no 500 free # ore, and no gas giant guaranteed in the origin chunk. Both of those were # play-testing conveniences that had become part of the real game, so this # test covers both sides - what dev mode hands you, and what a normal game # no longer does. # # Covers: # A. a dev game banks every resource type and research points # B. a dev game is a normal game PLUS that, not a separate definition # C. one of each planet type sits within a chunk or two of the spawn # D. none of that leaks into a normal game, and starting one clears it # E. a normal game starts with an empty bank # F. the menu gates dev mode and the map editor on the same rule var game_state: Node var menu: Node var stage: int = 0 var elapsed: float = 0.0 func _initialize() -> void: game_state = root.get_node("GameState") func _process(delta: float) -> bool: elapsed += delta match stage: 0: # --- E. a normal game hands over nothing ---------------------- game_state.reset_new_game() if game_state.rocks != 0: return _fail("a normal game started with %d ore banked" % game_state.rocks) for resource_type in game_state.resource_types(): if game_state.banked_resource(resource_type) != 0: return _fail("a normal game started with %s banked" % resource_type) if game_state.research_points != 0: return _fail("a normal game started with research points") if game_state.dev_mode: return _fail("a normal game started in dev mode") print("normal game starts with an empty bank and no research points") # --- D. no dev planets in a normal game ----------------------- # The dev arrangement must not survive into a normal world. Noise # could coincidentally place a planet in one of these chunks, so # what is asserted is that the ARRANGEMENT is not reproduced, not # that each chunk is empty. var reproduced: bool = true for chunk in WorldGen.DEV_PLANET_CHUNKS: var expected: String = String(WorldGen.DEV_PLANET_CHUNKS[chunk]) var data: Dictionary = WorldGen.planet_data_at_chunk(chunk) if not WorldGen.has_planet_at_chunk(chunk) or String(data.get("definition_id", "")) != expected: reproduced = false if Vector2(data.get("local_pos", Vector2.ZERO)) == WorldGen.DEV_PLANET_LOCAL_POS: return _fail("a normal game placed a planet at dev mode's fixed in-chunk position, in chunk %s" % chunk) if reproduced: return _fail("a normal game reproduced dev mode's whole planet arrangement") print("normal game does not carry dev mode's planets") # --- A + B. what a dev game hands over ------------------------ game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO) game_state.planet_capture["chunk:9:9"] = {"captured": [true], "conquered": true} game_state.start_dev_game() if not game_state.dev_mode: return _fail("start_dev_game() did not set dev_mode") for resource_type in game_state.resource_types(): var banked: int = game_state.banked_resource(resource_type) if banked != GameState.DEV_RESOURCE_AMOUNT: return _fail("dev game banked %d of %s, expected %d" \ % [banked, resource_type, GameState.DEV_RESOURCE_AMOUNT]) if game_state.research_points != GameState.DEV_RESEARCH_POINTS: return _fail("dev game granted %d research points, expected %d" \ % [game_state.research_points, GameState.DEV_RESEARCH_POINTS]) # B: it still went through reset_new_game, so a dirty run is gone. if not game_state.buildings.is_empty() or not game_state.planet_capture.is_empty(): return _fail("a dev game did not reset the previous run - it must be a fresh game plus a stock") print("dev game banks %d of each resource and %d research points, from a clean slate" \ % [GameState.DEV_RESOURCE_AMOUNT, GameState.DEV_RESEARCH_POINTS]) # --- C. one of each planet type, close by --------------------- var found: Dictionary = {} for chunk in WorldGen.DEV_PLANET_CHUNKS: if not WorldGen.has_planet_at_chunk(chunk): return _fail("dev mode chunk %s holds no planet" % chunk) var data: Dictionary = WorldGen.planet_data_at_chunk(chunk) var definition_id: String = String(data.get("definition_id", "")) if definition_id != String(WorldGen.DEV_PLANET_CHUNKS[chunk]): return _fail("dev mode chunk %s holds a %s, expected a %s" \ % [chunk, definition_id, WorldGen.DEV_PLANET_CHUNKS[chunk]]) found[definition_id] = true # "Near the start" is the whole point - a play-tester should # reach any of them in seconds, not go exploring. var world_pos: Vector2 = WorldGen.chunk_origin(chunk) + Vector2(data.local_pos) if world_pos.length() > WorldGen.CHUNK_SIZE * 2.5: return _fail("dev mode's %s spawned %.0f units away, too far to be 'near the start'" \ % [definition_id, world_pos.length()]) for definition_id in [PlanetDefinitions.ROCKY_ID, PlanetDefinitions.GAS_GIANT_ID, PlanetDefinitions.BLACK_HOLE_ID, PlanetDefinitions.RESEARCH_LAB_ID]: if not found.has(definition_id): return _fail("dev mode is missing a %s - it must plant one of EVERY planet type" % definition_id) print("dev mode plants all %d planet types within %.0f units of the spawn" \ % [found.size(), WorldGen.CHUNK_SIZE * 2.5]) # --- D. starting a normal game clears it ---------------------- game_state.reset_new_game() if game_state.dev_mode: return _fail("starting a normal game after a dev one left dev_mode set") if game_state.rocks != 0 or game_state.research_points != 0: return _fail("starting a normal game after a dev one kept the dev stock") print("starting a normal game afterwards clears dev mode entirely") menu = load("res://MainMenu.tscn").instantiate() root.add_child(menu) current_scene = menu elapsed = 0.0 stage = 1 return false 1: # --- F. the menu gates both on the same rule ------------------ if elapsed < 0.1: return false var developing: bool = menu.is_developer_build() if menu.dev_button.visible != developing: return _fail("the dev mode button's visibility did not follow is_developer_build()") if menu.editor_button.visible != developing: return _fail("the map editor button's visibility did not follow is_developer_build()") if menu.play_button.visible != true or menu.quit_button.visible != (not OS.has_feature("web")): return _fail("gating the development entries also hid an entry a player needs") # Hiding is presentation; pressing it anyway must still be refused. if not developing: menu._on_dev_mode_pressed() if game_state.dev_mode: return _fail("dev mode started outside a developer build") print("dev mode and the map editor are both gated on is_developer_build() (%s here)" % developing) print("TEST_DEV_MODE: PASS") quit(0) return true return false func _fail(message: String) -> bool: print("TEST_DEV_MODE: FAIL (%s)" % message) quit(1) return true