258 lines
10 KiB
GDScript
258 lines
10 KiB
GDScript
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 lab_id: int = 0
|
|
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://world/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"]
|
|
game_state.research_points = 10
|
|
field.place(GameState.BUILDING_SHIP_LAB, Vector2(-700.0, 0.0))
|
|
lab_id = int(game_state.buildings[-1].id)
|
|
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
|
|
# The lab is a direct vessel -> upgrade flow: tapping a vessel
|
|
# shows its own upgrade cards, and tapping a card changes the
|
|
# purchase detail. A locked vessel must show its unlock alone.
|
|
var lab_menu: Control = controller.get_node("../UI/LabMenu")
|
|
var lab_card_0: Control = controller.get_node("../UI/LabMenu/Upgrade0")
|
|
var lab_card_1: Control = controller.get_node("../UI/LabMenu/Upgrade1")
|
|
var lab_card_2: Control = controller.get_node("../UI/LabMenu/Upgrade2")
|
|
var lab_card_3: Control = controller.get_node("../UI/LabMenu/Upgrade3")
|
|
var lab_detail: Label = controller.get_node("../UI/LabMenu/DetailLabel")
|
|
lab_menu.visible = true
|
|
controller.call("_on_lab_vessel", 1)
|
|
if not lab_card_0.visible or lab_card_1.visible:
|
|
print("TEST_SHIP_LAB: FAIL (locked shield page did not show only its unlock)")
|
|
quit(1)
|
|
return true
|
|
controller.call("_on_lab_upgrade", 0)
|
|
# lab_can_buy, not a button's `enabled`: #164 removed the separate
|
|
# buy button, so the option itself is the control and a second tap
|
|
# on it is the purchase. What is being checked is unchanged -
|
|
# selecting the unlock card offers something actually buyable.
|
|
if not "Unlock Shield Vessel" in lab_detail.text or not controller.lab_can_buy:
|
|
print("TEST_SHIP_LAB: FAIL (unlock card did not select a purchasable detail)")
|
|
quit(1)
|
|
return true
|
|
controller.call("_on_lab_vessel", 0)
|
|
# #73: the interceptor's Thrusters upgrade was removed (global
|
|
# fleet speed now belongs to the distorder fader, #59/#62)
|
|
# leaving 3 upgrade cards, not 4 - card 3 is correctly hidden now.
|
|
if not lab_card_0.visible or not lab_card_2.visible or lab_card_3.visible:
|
|
print("TEST_SHIP_LAB: FAIL (interceptor upgrade cards were not shown)")
|
|
quit(1)
|
|
return true
|
|
controller.call("_on_lab_upgrade", 2)
|
|
if not "Targeting Array" in lab_detail.text or not lab_card_2.highlighted or lab_card_0.highlighted:
|
|
print("TEST_SHIP_LAB: FAIL (tapping an upgrade did not update its detail)")
|
|
quit(1)
|
|
return true
|
|
print("lab cards are vessel-specific and selectable")
|
|
|
|
# #184: a queued research uses the bar as its changing value. The
|
|
# status names the state but never repeats it as a percentage.
|
|
var lab_progress: ProgressBar = controller.get_node(
|
|
"../UI/LabMenu/ResearchProgress")
|
|
var lab_status: Label = controller.get_node("../UI/LabMenu/StatusLabel")
|
|
controller.lab_building_id = lab_id
|
|
if not game_state.queue_lab_research(lab_id, "int_rate"):
|
|
return _fail("could not queue research for the progress-bar check")
|
|
controller.call("_refresh_lab")
|
|
if not lab_progress.visible or lab_progress.show_percentage:
|
|
return _fail("pending research did not show a percentage-free progress bar")
|
|
if "%" in lab_status.text:
|
|
return _fail("pending research still prints a percentage: '%s'" % lab_status.text)
|
|
game_state.deposit_resource_to_building(lab_id, GameState.RESOURCE_ORE,
|
|
game_state.upgrade_cost("int_rate"))
|
|
game_state.call("_advance_lab_research", game_state.lab_research_time() * 0.25)
|
|
controller.call("_refresh_lab")
|
|
if lab_progress.value < 0.2 or lab_progress.value > 0.3 \
|
|
or lab_status.text != "Researching":
|
|
return _fail("research bar did not track active work (value %.2f, status '%s')" \
|
|
% [lab_progress.value, lab_status.text])
|
|
print("pending lab research is shown by a live progress bar, without percentage text")
|
|
|
|
# --- 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_range")
|
|
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
|
|
|
|
|
|
func _fail(message: String) -> bool:
|
|
print("TEST_SHIP_LAB: FAIL (%s)" % message)
|
|
quit(1)
|
|
return true
|