extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_enemy_projectile_damage.gd # Verifies the ranged damage path #44 adds: an EnemyProjectile destroys an # unshielded ship on contact, and is absorbed by a shielded ship, removing # exactly one charge rather than destroying it. var game_state: Node var scene: Node var fleet: Node var interceptor: Node var shield_ship: Node var stage: int = 0 var elapsed: float = 0.0 func _initialize() -> void: game_state = root.get_node("GameState") game_state.buildings.clear() game_state.extractors.clear() game_state.transporters.clear() game_state.stored_vessels.clear() game_state.build_time_scale = 0.02 game_state.reset_progression() game_state.rocks = 2000 scene = load("res://world/Main.tscn").instantiate() root.add_child(scene) fleet = scene.get_node("Fleet") func _fire_at(ship: Node) -> void: var projectile: Area2D = load("res://enemies/EnemyProjectile.tscn").instantiate() projectile.global_position = ship.global_position projectile.launch(Vector2.ZERO) # already on top of the target scene.add_child(projectile) func _process(delta: float) -> bool: elapsed += delta match stage: 0: scene.get_node("AsteroidField").set_process(false) scene.get_node("EnemySpawner").set_process(false) # set_process(false) only stops NEW asteroids; the ones already # placed keep drifting under their own _physics_process, and they # are placed at random. One of them wandering into the shield # vessel parked out at (2000, 0) spends its single charge, and # the projectile fired at it a moment later then kills it - which # presented as "a shielded ship was destroyed by an enemy # projectile" on roughly one parallel suite run in thirty, and # never in isolation. Clearing them is what the other combat # tests already do (see test_sniper_enemy.gd). for asteroid in get_nodes_in_group("asteroids"): if is_instance_valid(asteroid): asteroid.queue_free() fleet.spawn_ships(1, Vector2.ZERO) interceptor = fleet.ships[0] fleet.add_ship(GameState.VESSEL_SHIELD) shield_ship = fleet.ships[1] shield_ship.position = Vector2(2000.0, 0.0) elapsed = 0.0 stage = 1 return false 1: if elapsed < 0.2: return false if not shield_ship.has_shield(): return _fail("a fresh shield vessel has no shield") _fire_at(interceptor) elapsed = 0.0 stage = 2 return false 2: if elapsed < 0.4: return false if is_instance_valid(interceptor): return _fail("an unshielded ship survived an enemy projectile") print("unshielded interceptor destroyed by an enemy projectile") _fire_at(shield_ship) elapsed = 0.0 stage = 3 return false 3: if elapsed < 0.4: return false if not is_instance_valid(shield_ship): return _fail("a shielded ship was destroyed by an enemy projectile") if shield_ship.shield_charges != 0: return _fail("shield charges did not drop to 0 after absorbing one hit (charges=%d)" % shield_ship.shield_charges) print("shielded ship absorbed the hit and lost exactly one charge") print("TEST_ENEMY_PROJECTILE_DAMAGE: PASS") quit(0) return true return false func _fail(message: String) -> bool: print("TEST_ENEMY_PROJECTILE_DAMAGE: FAIL (%s)" % message) quit(1) return true