extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_shield_vessel.gd # Forgejo #5: the shield vessel's shield absorbs a hit that would destroy any # other ship, and is then down until it cycles back up. # # The shield is PERSONAL - it protects the vessel carrying it and nothing # else. Ships have no health in this game: contact with an enemy or an # asteroid destroys them outright, so "absorbs a hit" means surviving the # collision that would otherwise have killed it. # # Verifies: # 1. an ordinary interceptor still dies to a rock (so the shield result # below means something) # 2. a shield vessel survives the same rock, and the rock is destroyed # 3. its shield is then DOWN - a second rock while it is cycling kills it, # or the shield would be permanent invulnerability # 4. the shield comes back on its own after the recharge time # 5. researched capacity lets it take more than one hit # 6. the recharge upgrade actually shortens the wait 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.vessels_owed.clear() game_state.reset_progression() game_state.rocks = 2000 scene = load("res://Main.tscn").instantiate() root.add_child(scene) fleet = scene.get_node("Fleet") func _rock_onto(ship: Node) -> void: var rock: Area2D = load("res://Asteroid.tscn").instantiate() rock.velocity = Vector2.ZERO rock.global_position = ship.global_position scene.add_child(rock) func _process(delta: float) -> bool: elapsed += delta match stage: 0: scene.get_node("AsteroidField").set_process(false) scene.get_node("EnemySpawner").set_process(false) for a in get_nodes_in_group("asteroids"): if is_instance_valid(a): a.queue_free() fleet.spawn_ships(1, Vector2.ZERO) interceptor = fleet.ships[0] # Well clear of the interceptor, or one rock hits both. 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(): print("TEST_SHIELD_VESSEL: FAIL (a fresh shield vessel has no shield)") quit(1) return true # --- 1. the control: an interceptor dies -------------------- _rock_onto(interceptor) elapsed = 0.0 stage = 2 return false 2: if elapsed < 0.4: return false if is_instance_valid(interceptor): print("TEST_SHIELD_VESSEL: FAIL (an ordinary interceptor survived a rock - the test proves nothing)") quit(1) return true print("control: interceptor destroyed by a rock") # --- 2. the shield vessel survives it ----------------------- _rock_onto(shield_ship) elapsed = 0.0 stage = 3 return false 3: if elapsed < 0.4: return false if not is_instance_valid(shield_ship): print("TEST_SHIELD_VESSEL: FAIL (the shield did not absorb the hit)") quit(1) return true if shield_ship.has_shield(): print("TEST_SHIELD_VESSEL: FAIL (shield still up after taking a hit - it never goes down)") quit(1) return true print("shield vessel survived, shield now down") # --- 3. defenceless while it cycles ------------------------- _rock_onto(shield_ship) elapsed = 0.0 stage = 4 return false 4: if elapsed < 0.4: return false if is_instance_valid(shield_ship): print("TEST_SHIELD_VESSEL: FAIL (survived a second hit with the shield down - it is just invulnerable)") quit(1) return true print("second hit while cycling destroyed it, as it should") # --- 4 + 5. recharge, and researched capacity --------------- game_state.vessel_levels["shd_hits"] = 1 fleet.add_ship(GameState.VESSEL_SHIELD) shield_ship = fleet.ships[fleet.ships.size() - 1] shield_ship.position = Vector2(-2000.0, 0.0) elapsed = 0.0 stage = 5 return false 5: if elapsed < 0.2: return false if shield_ship.shield_charges != game_state.shield_max_charges(): print("TEST_SHIELD_VESSEL: FAIL (researched capacity %d, vessel has %d)" % [ game_state.shield_max_charges(), shield_ship.shield_charges ]) quit(1) return true print("with Layered Emitters researched: %d charges" % shield_ship.shield_charges) _rock_onto(shield_ship) elapsed = 0.0 stage = 6 return false 6: if elapsed < 0.4: return false if not is_instance_valid(shield_ship) or not shield_ship.has_shield(): print("TEST_SHIELD_VESSEL: FAIL (a two-charge shield did not survive one hit with a charge left)") quit(1) return true print("two-charge shield took a hit and is still up (%d left)" % shield_ship.shield_charges) # --- 6. the recharge upgrade shortens the wait -------------- var slow: float = game_state.shield_recharge_time() game_state.vessel_levels["shd_recharge"] = 2 var fast: float = game_state.shield_recharge_time() print("recharge %.1fs -> %.1fs with Fast Cycling" % [slow, fast]) if fast >= slow: print("TEST_SHIELD_VESSEL: FAIL (the recharge upgrade did not shorten the wait)") quit(1) return true # --- 4. it really does come back ---------------------------- shield_ship.shield_charges = 0 shield_ship.shield_recharge_left = 0.15 elapsed = 0.0 stage = 7 return false 7: if elapsed < 0.6: return false if not shield_ship.has_shield(): print("TEST_SHIELD_VESSEL: FAIL (the shield never cycled back up)") quit(1) return true print("shield cycled back up on its own") print("TEST_SHIELD_VESSEL: PASS") quit(0) return true return false