extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_ship_lab.gd # Forgejo #5: the ship lab researches upgrades per vessel type and unlocks # new vessel types. The shield vessel is the first of those. # # Verifies: # 1. the lab is gated behind its own research node, like every structure # 2. upgrades cannot be bought without a lab built - it is a place you go, # not a menu that exists once researched # 3. buying raises the level, charges the escalating price, and stops at # max # 4. an upgrade improves the ships ALREADY FLYING, not just the next one # built # 5. the shield vessel is unlocked at the lab, and only then can the # crafter build one # 6. a shield vessel is what comes back from production - a typed order # must not decay into an interceptor # 7. upgrades are per vessel type: an interceptor upgrade does not tune # the transporter var game_state: Node var scene: Node var fleet: Node var field: Node var controller: Node var crafter_id: int = 0 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 = 1200 game_state.fleet_cargo = 0 scene = load("res://Main.tscn").instantiate() root.add_child(scene) fleet = scene.get_node("Fleet") field = scene.get_node("BuildingField") controller = scene.get_node("BuildController") 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() # --- 1. gated behind research ------------------------------- if game_state.is_building_unlocked(GameState.BUILDING_SHIP_LAB): print("TEST_SHIP_LAB: FAIL (lab buildable with no research)") quit(1) return true var gate: String = game_state.research_for_building(GameState.BUILDING_SHIP_LAB) if gate == "": print("TEST_SHIP_LAB: FAIL (no research node gates the lab)") quit(1) return true print("lab gated behind '%s'" % gate) # --- 2. no lab, no upgrades --------------------------------- if game_state.can_buy_upgrade("int_damage"): print("TEST_SHIP_LAB: FAIL (upgrades purchasable with no lab built)") quit(1) return true if game_state.buy_upgrade("int_damage"): print("TEST_SHIP_LAB: FAIL (bought an upgrade with no lab built)") quit(1) return true print("no lab -> no upgrades") game_state.unlocked = ["shipyards", "vessel_science", "coordination", "wing", "squadron"] field.place(GameState.BUILDING_SHIP_LAB, Vector2(-700.0, 0.0)) field.place(GameState.BUILDING_SHIP_CRAFTER, Vector2(700.0, 0.0)) crafter_id = int(game_state.buildings[-1].id) elapsed = 0.0 stage = 1 return false 1: if elapsed < 0.3: return false # --- 3. buying raises the level and charges ------------------ var before_rocks: int = game_state.rocks var first_cost: int = game_state.upgrade_cost("int_damage") if not game_state.buy_upgrade("int_damage"): print("TEST_SHIP_LAB: FAIL (could not buy an upgrade with a lab built)") quit(1) return true if game_state.upgrade_level("int_damage") != 1: print("TEST_SHIP_LAB: FAIL (level did not rise)") quit(1) return true if game_state.rocks != before_rocks - first_cost: print("TEST_SHIP_LAB: FAIL (wrong price charged)") quit(1) return true var second_cost: int = game_state.upgrade_cost("int_damage") if second_cost <= first_cost: print("TEST_SHIP_LAB: FAIL (level 2 costs %d, no more than level 1 at %d)" % [second_cost, first_cost]) quit(1) return true print("bought level 1 for %d, level 2 now costs %d" % [first_cost, second_cost]) # --- 4. it reaches ships already flying ---------------------- var flying: Node = fleet.ships[0] if flying.projectile_damage != GameState.SHIP_BASE_DAMAGE + 1: print("TEST_SHIP_LAB: FAIL (a ship already flying still does %d damage)" % flying.projectile_damage) quit(1) return true print("ship already flying now does %d damage" % flying.projectile_damage) # --- 7. per vessel type -------------------------------------- var trn_speed_before: float = game_state.transporter_speed() game_state.buy_upgrade("int_speed") if game_state.transporter_speed() != trn_speed_before: print("TEST_SHIP_LAB: FAIL (an interceptor upgrade changed the transporter)") quit(1) return true print("interceptor upgrades leave the transporter alone") # --- 5. the shield vessel is unlocked at the lab ------------- if game_state.shield_vessel_unlocked(): print("TEST_SHIP_LAB: FAIL (shield vessel available before it was unlocked)") quit(1) return true if game_state.queue_order(crafter_id, GameState.VESSEL_SHIELD): print("TEST_SHIP_LAB: FAIL (crafter took a shield order before the type was unlocked)") quit(1) return true if not game_state.unlock_shield_vessel(): print("TEST_SHIP_LAB: FAIL (could not unlock the shield vessel at the lab)") quit(1) return true print("shield vessel unlocked at the lab") # --- 6. a typed order comes back as that type ---------------- fleet.spawn_ships(1, Vector2.ZERO) if not game_state.queue_order(crafter_id, GameState.VESSEL_SHIELD): print("TEST_SHIP_LAB: FAIL (crafter refused a shield order after unlocking)") quit(1) return true game_state.deposit_to_building(crafter_id, GameState.SHIELD_COST) elapsed = 0.0 stage = 2 return false 2: if elapsed < game_state.build_time(GameState.VESSEL_SHIELD) * 3.0: return false # Finished vessels wait at the mothership (#23); crew it here so # the rest of the checks can look at a real ship. if game_state.stored_vessels.size() != 1: print("TEST_SHIP_LAB: FAIL (the shield order produced nothing: %s)" % [game_state.stored_vessels]) quit(1) return true if String(game_state.stored_vessels[0]) != GameState.VESSEL_SHIELD: print("TEST_SHIP_LAB: FAIL (a shield order came back as %s)" % game_state.stored_vessels[0]) quit(1) return true fleet.add_ship(game_state.take_stored_vessel(0)) var shield_ship: Node = null for ship in fleet.ships: if is_instance_valid(ship) and ship.vessel_type == GameState.VESSEL_SHIELD: shield_ship = ship if shield_ship == null: print("TEST_SHIP_LAB: FAIL (crewing the stored shield vessel produced none: %d ships)" % fleet.ships.size()) quit(1) return true if not shield_ship.has_shield(): print("TEST_SHIP_LAB: FAIL (the shield vessel arrived with no shield up)") quit(1) return true print("shield vessel built, shield up with %d charge(s)" % shield_ship.shield_charges) print("TEST_SHIP_LAB: PASS") quit(0) return true return false