spaceship/tests/test_building_collateral.gd
2026-09-02 13:43:56 +02:00

137 lines
4.5 KiB
GDScript

extends SceneTree
# Headless gameplay test: run with
# godot --headless -s res://tests/test_building_collateral.gd
# Forgejo #176: the player's own ships and their weapons do not damage the
# player's buildings. Friendly fire on structures made defending them with a
# gas bomb or crossing them at speed punish ordinary play.
#
# Covers:
# A. a bomb landing on a structure leaves it unharmed
# B. a bomb landing clear of it does not - the radius is the radius, and
# this is a weapon that has to be aimed
# C. a vessel resting against a structure does NOT damage it. The fleet
# sits on its own mothership constantly to bank ore; if that hurt, the
# game's own economy loop would grind its buildings down
# D. a vessel arriving fast still does not damage it or itself
var game_state: Node
var main: Node
var fleet: Node
var building_id: int = 0
var stage: int = 0
var elapsed: float = 0.0
var hp_before: int = 0
var ships_before: int = 0
const BUILDING_AT := Vector2(1200.0, 0.0)
func _initialize() -> void:
game_state = root.get_node("GameState")
game_state.buildings.clear()
game_state.extractors.clear()
game_state.reset_progression()
main = load("res://world/Main.tscn").instantiate()
root.add_child(main)
current_scene = main
fleet = main.get_node("Fleet")
func _hp() -> int:
return int(game_state.find_building(building_id).get("hp", 0))
func _detonate_at(at: Vector2) -> void:
var bomb: Node2D = load("res://vfx/GasBombEffect.gd").new()
main.add_child(bomb)
bomb.target = at
bomb.damage = 6
bomb._explode()
bomb.queue_free()
func _process(delta: float) -> bool:
elapsed += delta
match stage:
0:
if elapsed < 0.4:
return false
main.get_node("EnemySpawner").set_process(false)
main.get_node("AsteroidField").set_process(false)
for hazard in get_nodes_in_group("asteroids") + get_nodes_in_group("enemies"):
if is_instance_valid(hazard):
hazard.queue_free()
main.get_node("BuildingField").place(GameState.BUILDING_MOTHERSHIP, BUILDING_AT)
building_id = int(game_state.buildings[0].id)
if _hp() <= 0:
return _fail("the structure has no hit points to lose")
stage = 1
elapsed = 0.0
return false
1:
if elapsed < 0.2:
return false
# --- B. a bomb well clear of it does nothing ------------------
hp_before = _hp()
_detonate_at(BUILDING_AT + Vector2(900.0, 0.0))
if _hp() != hp_before:
return _fail("a bomb 900 away still damaged the structure")
print("a bomb clear of the structure leaves it alone")
# --- A. a friendly bomb on top of it is safe ----------------------
_detonate_at(BUILDING_AT)
if _hp() != hp_before:
return _fail("a friendly bomb damaged the structure (%d -> %d)" % [hp_before, _hp()])
print("a friendly bomb on the structure leaves it at %d hp" % _hp())
# --- C. resting against it is free ------------------------------
hp_before = _hp()
fleet.spawn_ships(1, BUILDING_AT, PackedStringArray([GameState.VESSEL_INTERCEPTOR]))
for ship in fleet.ships:
ship.global_position = BUILDING_AT
ship.velocity = Vector2.ZERO
stage = 2
elapsed = 0.0
return false
2:
# Long enough for several strike ticks to have passed.
if elapsed < 2.0:
return false
for ship in fleet.ships:
ship.velocity = Vector2.ZERO
if _hp() != hp_before:
return _fail("a vessel resting on the structure damaged it (%d -> %d) - the fleet docks here to bank ore" \
% [hp_before, _hp()])
print("a vessel parked on the structure for %.1fs did nothing to it" % elapsed)
# --- D. arriving fast is still friendly -------------------------
hp_before = _hp()
ships_before = fleet.ships.size()
for ship in fleet.ships:
# Placed clear and sent IN, because the impact is detected on
# entry - a ship spawned already overlapping never enters.
ship.global_position = BUILDING_AT - Vector2(400.0, 0.0)
ship.velocity = Vector2(900.0, 0.0)
ship.set_move_target(BUILDING_AT + Vector2(600.0, 0.0))
stage = 3
elapsed = 0.0
return false
3:
if elapsed < 1.2:
return false
if _hp() != hp_before:
return _fail("a fast friendly vessel damaged the structure (%d -> %d)" % [hp_before, _hp()])
if fleet.ships.size() != ships_before:
return _fail("a friendly building destroyed a vessel crossing it")
print("a vessel crossing the structure at speed leaves both unharmed")
print("TEST_BUILDING_COLLATERAL: PASS")
quit(0)
return true
return false
func _fail(message: String) -> bool:
print("TEST_BUILDING_COLLATERAL: FAIL (%s)" % message)
quit(1)
return true