extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_planet_garrison.gd # Forgejo #211: taking a planet is killing what defends it. The capture # points this replaced - stand in a ring, wait out a timer, once per ring - # are gone, so what has to be proven is that the FIGHT reaches the conquest # state, through the enemies' own deaths rather than through a bookkeeping # call a test makes on their behalf. # # test_conquered_planet_clear.gd covers the counting rules around landing and # re-landing. This one covers the wiring: a real Enemy, destroyed the way # anything destroys one, moves the planet toward being held. # # Covers: # A. an authored garrison registers itself when the surface is composed # B. killing one defender is progress, not conquest # C. killing the last one conquers the planet, exactly once, and the # research award rides along with the signal # D. an enemy that dies after the planet has already fallen changes nothing # Resolved via load() inside _initialize(), NOT a top-level `const ... # preload(...)`. A top-level preload in a script-mode ENTRY point (-s # this_file.gd) evaluates during this file's own early compilation, which can # happen before autoloads are registered - and these scripts reference # GameState in function bodies, which GDScript resolves at compile time. var PLANET_SETUP_SCRIPT: Script var SPAWN_SCRIPT: Script var game_state: Node var host: Node var planet_id: String = "test_garrison_planet" var conquered_count: int = 0 var conquered_points: int = -1 var stage: int = 0 var elapsed: float = 0.0 func _initialize() -> void: PLANET_SETUP_SCRIPT = load("res://surface/PlanetSetup.gd") SPAWN_SCRIPT = load("res://surface/PlanetSetupSpawn.gd") game_state = root.get_node("GameState") game_state.planet_capture.clear() game_state.planet_id = planet_id game_state.planet_definition_id = "rocky" game_state.planet_conquered.connect(func(_id, points): conquered_count += 1 conquered_points = points) var setup: Resource = PLANET_SETUP_SCRIPT.new() setup.setup_id = "test_garrison" setup.surface_profile_id = "rocky" # Worth something, so the award travelling with the signal is checked on a # planet that actually pays. setup.research_point_reward = 3 for i in range(3): var spawn: Resource = SPAWN_SCRIPT.new() spawn.enemy_id = "chaser" spawn.position = Vector2(0.0, 300.0 * float(i + 1)) setup.enemy_spawns.append(spawn) host = load("res://surface/PlanetSetupHost.tscn").instantiate() root.add_child(host) host.apply_setup(setup) func _defenders() -> Array: var out: Array = [] for child in host.get_children(): if is_instance_valid(child) and child.is_in_group("enemies") \ and not child.is_queued_for_deletion(): out.append(child) return out func _process(delta: float) -> bool: elapsed += delta if elapsed < 0.2: return false match stage: 0: # --- A. the garrison registered itself ----------------------- var defenders: Array = _defenders() if defenders.size() != 3: return _fail("the surface placed %d defenders for 3 authored" % defenders.size()) if game_state.garrison_total(planet_id) != 3 \ or game_state.garrison_remaining(planet_id) != 3: return _fail("a 3-strong garrison registered as %d of %d" % [ game_state.garrison_remaining(planet_id), game_state.garrison_total(planet_id)]) print("3 defenders placed, and the planet knows it") # --- B. one down is not conquest ----------------------------- var points_before: int = game_state.research_points defenders[0].take_damage(defenders[0].hp) if game_state.garrison_remaining(planet_id) != 2: return _fail("one kill left %d of the garrison standing, expected 2" \ % game_state.garrison_remaining(planet_id)) if game_state.is_planet_conquered(planet_id): return _fail("the planet fell to the first kill") if game_state.research_points != points_before: return _fail("an unfinished conquest paid out") print("one defender down: 2 left, planet still theirs") # --- C. the last one takes it -------------------------------- defenders[1].take_damage(defenders[1].hp) defenders[2].take_damage(defenders[2].hp) if not game_state.is_planet_conquered(planet_id): return _fail("the whole garrison is dead and the planet is not ours") if conquered_count != 1: return _fail("planet_conquered fired %d times, expected once" % conquered_count) if conquered_points != 3: return _fail("the conquest reported %d research points, expected the authored 3" \ % conquered_points) if game_state.research_points != points_before + 3: return _fail("the reported award was not the one banked") print("last defender down: planet taken, 3 research points paid once") elapsed = 0.0 stage = 1 return false 1: # --- D. a straggler after the fact changes nothing ----------- # A shot already in flight, or a bomb still blooming, can kill # something a frame after the planet has fallen. var points_after: int = game_state.research_points game_state.mark_garrison_enemy_destroyed(planet_id) if conquered_count != 1: return _fail("a kill after conquest fired planet_conquered again") if game_state.research_points != points_after: return _fail("a kill after conquest paid the reward a second time") print("a late kill is absorbed: no second conquest, no second payment") print("TEST_PLANET_GARRISON: PASS") quit(0) return true return false func _fail(message: String) -> bool: print("TEST_PLANET_GARRISON: FAIL (%s)" % message) quit(1) return true