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