extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_planet_setups.gd # Verifies: # 1. setup_for_planet is deterministic per planet_id and only returns # setups matching the requested surface profile # 2. dropping a new .tres into res://PlanetSetups/ makes it appear via # PlanetSetups.rescan() with no other code change # 3. landing on a rocky/gas planet composes a PlanetSetupHost carrying the # resolved setup, with one marker per capture point const SCRATCH_SETUP_PATH: String = "res://PlanetSetups/_test_scratch_setup.tres" var game_state: Node var rocky_setup: Resource var stage: int = 0 var elapsed: float = 0.0 var pending_surface: Node func _initialize() -> void: # --- 1. determinism and profile filtering --- rocky_setup = PlanetSetups.setup_for_planet("chunk:3:7", "rocky") if rocky_setup == null: return _fail("no rocky setup resolved for a rocky planet id") if rocky_setup.surface_profile_id != "rocky": return _fail("resolved setup does not match the requested profile") var rocky_setup_again: Resource = PlanetSetups.setup_for_planet("chunk:3:7", "rocky") if rocky_setup != rocky_setup_again: return _fail("setup_for_planet was not deterministic for the same planet id") var gas_setup: Resource = PlanetSetups.setup_for_planet("chunk:3:7", "gas") if gas_setup == null: return _fail("no gas setup resolved for the same planet id under the gas profile") if gas_setup.surface_profile_id != "gas": return _fail("gas request returned a mismatched-profile setup") for setup in PlanetSetups.setups_for_profile("rocky"): if setup.surface_profile_id != "rocky": return _fail("setups_for_profile('rocky') leaked a non-rocky setup") # --- 2. dropping a new .tres is picked up by rescan() --- var before_count: int = PlanetSetups.setups_for_profile("rocky").size() var scratch: Resource = PlanetSetup.new() scratch.setup_id = "_test_scratch_setup" scratch.display_name = "Scratch" scratch.surface_profile_id = "rocky" scratch.capture_points = PackedVector2Array([Vector2(100, 100)]) var save_err: int = ResourceSaver.save(scratch, SCRATCH_SETUP_PATH) if save_err != OK: return _fail("could not save the scratch setup for the pickup check (err %d)" % save_err) PlanetSetups.rescan() var after_count: int = PlanetSetups.setups_for_profile("rocky").size() DirAccess.remove_absolute(ProjectSettings.globalize_path(SCRATCH_SETUP_PATH)) var import_file: String = SCRATCH_SETUP_PATH + ".import" if FileAccess.file_exists(import_file): DirAccess.remove_absolute(ProjectSettings.globalize_path(import_file)) PlanetSetups.rescan() if after_count != before_count + 1: return _fail("registry did not pick up a newly saved setup (before=%d after=%d)" % [ before_count, after_count ]) # --- 3. landing composes a PlanetSetupHost with the resolved setup --- # Deferred to _process(): a scene instantiated and added to the tree # inside _initialize() has not had its _ready() chain flushed yet (this # runs before the engine's first frame), so children added during # PlanetSurfaceController._ready() are not observable until a frame has # actually processed - see test_gas_system.gd for the same pattern. game_state = root.get_node("GameState") game_state.planet_name = "Setup Test" game_state.planet_id = "chunk:3:7" game_state.planet_definition_id = "rocky" pending_surface = load("res://PlanetSurface.tscn").instantiate() root.add_child(pending_surface) func _process(delta: float) -> bool: elapsed += delta match stage: 0: if elapsed < 0.1: return false var host: Node = pending_surface.get_node_or_null("SurfaceFeatures/PlanetSetupHost") if host == null: pending_surface.free() return _fail("landing did not compose a PlanetSetupHost") if host.setup != rocky_setup: pending_surface.free() return _fail("host did not receive the deterministically resolved setup") var marker_count: int = 0 for child in host.get_children(): if child is NeonPiece: marker_count += 1 if marker_count != rocky_setup.capture_points.size(): pending_surface.free() return _fail("host placed %d markers for %d capture points" % [ marker_count, rocky_setup.capture_points.size() ]) pending_surface.free() game_state.planet_definition_id = "gas_giant" pending_surface = load("res://PlanetSurface.tscn").instantiate() root.add_child(pending_surface) elapsed = 0.0 stage = 1 return false 1: if elapsed < 0.1: return false var gas_host: Node = pending_surface.get_node_or_null("SurfaceFeatures/PlanetSetupHost") if gas_host == null or gas_host.setup.surface_profile_id != "gas": pending_surface.free() return _fail("gas landing did not compose a matching-profile setup host") pending_surface.free() print("TEST_PLANET_SETUPS: PASS") quit(0) return true return false func _fail(message: String) -> bool: print("TEST_PLANET_SETUPS: FAIL (%s)" % message) if FileAccess.file_exists(SCRATCH_SETUP_PATH): DirAccess.remove_absolute(ProjectSettings.globalize_path(SCRATCH_SETUP_PATH)) quit(1) return true