188 lines
7.2 KiB
GDScript
188 lines
7.2 KiB
GDScript
extends SceneTree
|
|
|
|
# Headless gameplay test: run with
|
|
# godot --headless -s res://tests/test_skill_tree.gd
|
|
# The mothership's research tree (Forgejo issue #4). It gates which
|
|
# structures may be built at all, and carries permanent upgrades that have
|
|
# no building of their own.
|
|
#
|
|
# Verifies:
|
|
# 1. from a fresh start only the mothership is buildable - the others are
|
|
# not in the menu at all (#21 changed this from greyed-but-listed), and
|
|
# the gate still holds if the build is forced past the menu
|
|
# 2. a locked option cannot be built even if the confirm is forced
|
|
# 3. research costs rocks, needs its prerequisite, and unlocks its building
|
|
# 4. the mothership's action button opens the tree (it used to have no
|
|
# action at all)
|
|
# 5. fleet capacity upgrades raise the cap, and the extra vessel can
|
|
# actually be crafted afterwards
|
|
|
|
var scene: Node
|
|
var game_state: Node
|
|
var controller: Node
|
|
var fleet: Node
|
|
var field: Node
|
|
var popup: Control
|
|
var stage: int = 0
|
|
var elapsed: float = 0.0
|
|
|
|
|
|
func _initialize() -> void:
|
|
game_state = root.get_node("GameState")
|
|
game_state.rocks = 500
|
|
game_state.buildings.clear()
|
|
game_state.extractors.clear()
|
|
game_state.reset_progression()
|
|
game_state.reset_formation()
|
|
|
|
scene = load("res://Main.tscn").instantiate()
|
|
root.add_child(scene)
|
|
controller = scene.get_node("BuildController")
|
|
fleet = scene.get_node("Fleet")
|
|
field = scene.get_node("BuildingField")
|
|
popup = scene.get_node("UI/BuildMenu/InfoPopup")
|
|
|
|
|
|
func _process(delta: float) -> bool:
|
|
elapsed += delta
|
|
match stage:
|
|
0:
|
|
scene.get_node("AsteroidField").set_process(false)
|
|
scene.get_node("EnemySpawner").set_process(false)
|
|
field.place(GameState.BUILDING_MOTHERSHIP, Vector2(400.0, 0.0))
|
|
elapsed = 0.0
|
|
stage = 1
|
|
return false
|
|
1:
|
|
if elapsed < 0.2:
|
|
return false
|
|
|
|
# --- everything but the mothership starts locked --------------
|
|
var mothership_free: bool = game_state.is_building_unlocked(GameState.BUILDING_MOTHERSHIP)
|
|
var crafter_locked: bool = not game_state.is_building_unlocked(GameState.BUILDING_SHIP_CRAFTER)
|
|
print("fresh start: mothership buildable=%s crafter locked=%s" % [
|
|
mothership_free, crafter_locked
|
|
])
|
|
if not (mothership_free and crafter_locked):
|
|
print("TEST_SKILL_TREE: FAIL (wrong things buildable at the start)")
|
|
quit(1)
|
|
return true
|
|
|
|
controller._on_build_pressed()
|
|
# Since #21 an unresearched structure is not listed at all, rather
|
|
# than listed and greyed. It is therefore unreachable from the
|
|
# menu - but the popup must still refuse if something calls
|
|
# the generic definition entry point by another route.
|
|
var hidden: bool = not scene.get_node("UI/BuildMenu/CrafterButton").visible
|
|
controller._show_building_info(GameState.BUILDING_SHIP_CRAFTER)
|
|
var offered: bool = popup.get_node("Panel/BuildButton").visible
|
|
print("locked crafter: hidden_from_menu=%s confirm_offered=%s" % [hidden, offered])
|
|
if not hidden or offered:
|
|
print("TEST_SKILL_TREE: FAIL (unresearched option still on offer)")
|
|
quit(1)
|
|
return true
|
|
|
|
# Forcing the confirm must still build nothing.
|
|
var before: int = game_state.buildings.size()
|
|
controller.pending_build_type = GameState.BUILDING_SHIP_CRAFTER
|
|
controller._confirm_build()
|
|
if game_state.buildings.size() != before:
|
|
print("TEST_SKILL_TREE: FAIL (built a locked structure)")
|
|
quit(1)
|
|
return true
|
|
controller._close_info()
|
|
|
|
# --- the mothership opens the tree ----------------------------
|
|
var mothership: Node2D = null
|
|
for b in field.spawned:
|
|
if is_instance_valid(b) and b.building_type == GameState.BUILDING_MOTHERSHIP:
|
|
mothership = b
|
|
controller.current_building = mothership
|
|
controller._on_action_pressed()
|
|
var tree_open: bool = scene.get_node("UI/SkillMenu").visible
|
|
print("mothership action opens research: %s" % tree_open)
|
|
if not tree_open:
|
|
print("TEST_SKILL_TREE: FAIL (mothership has no research action)")
|
|
quit(1)
|
|
return true
|
|
# The tree is a tall, vertically scrolling canvas. A branch keeps a
|
|
# stable lane as it rises, and dragging exposes higher depths.
|
|
var freight: Vector2 = controller.skill_tree.node_screen_pos(game_state.skill_node("freight"))
|
|
var yards: Vector2 = controller.skill_tree.node_screen_pos(game_state.skill_node("freight_yards"))
|
|
if absf(freight.x - yards.x) > 1.0 or yards.y >= freight.y:
|
|
print("TEST_SKILL_TREE: FAIL (freight branch does not rise vertically)")
|
|
quit(1)
|
|
return true
|
|
var before_scroll: float = controller.skill_tree.scroll_y
|
|
var drag: InputEventScreenDrag = InputEventScreenDrag.new()
|
|
# Start at the root at the bottom; pull the canvas down to reveal
|
|
# the branches rising above it.
|
|
drag.relative = Vector2(0.0, 180.0)
|
|
controller.skill_tree.handle_drag(drag)
|
|
if controller.skill_tree.scroll_y >= before_scroll:
|
|
print("TEST_SKILL_TREE: FAIL (tree did not scroll upward)")
|
|
quit(1)
|
|
return true
|
|
|
|
# --- prerequisites --------------------------------------------
|
|
if game_state.can_unlock("wing"):
|
|
print("TEST_SKILL_TREE: FAIL (deep node unlockable with no prerequisite)")
|
|
quit(1)
|
|
return true
|
|
|
|
# --- research costs rocks and unlocks its building ------------
|
|
var rocks_before: int = game_state.rocks
|
|
var cost: int = int(game_state.skill_node("shipyards").cost)
|
|
var ok: bool = game_state.unlock("shipyards")
|
|
var charged: bool = game_state.rocks == rocks_before - cost
|
|
var now_buildable: bool = game_state.is_building_unlocked(GameState.BUILDING_SHIP_CRAFTER)
|
|
print("research shipyards: ok=%s rocks %d->%d charged=%s crafter unlocked=%s" % [
|
|
ok, rocks_before, game_state.rocks, charged, now_buildable
|
|
])
|
|
if not (ok and charged and now_buildable):
|
|
print("TEST_SKILL_TREE: FAIL (research did not unlock its building)")
|
|
quit(1)
|
|
return true
|
|
|
|
# The menu must show it as buildable now, without reopening.
|
|
controller._on_build_pressed()
|
|
if not scene.get_node("UI/BuildMenu/CrafterButton").enabled:
|
|
print("TEST_SKILL_TREE: FAIL (build menu still greys an unlocked option)")
|
|
quit(1)
|
|
return true
|
|
controller._close_info()
|
|
scene.get_node("UI/BuildMenu").visible = false
|
|
|
|
# --- fleet capacity -------------------------------------------
|
|
var base_cap: int = game_state.max_fleet_size()
|
|
game_state.research_points = 2
|
|
game_state.unlock("coordination")
|
|
var wing_ok: bool = game_state.unlock("wing")
|
|
var raised_cap: int = game_state.max_fleet_size()
|
|
print("fleet cap %d -> %d after Wing Doctrine (unlocked=%s)" % [
|
|
base_cap, raised_cap, wing_ok
|
|
])
|
|
if not (wing_ok and raised_cap == base_cap + 1):
|
|
print("TEST_SKILL_TREE: FAIL (capacity upgrade did not raise the cap)")
|
|
quit(1)
|
|
return true
|
|
|
|
# And the extra vessel must actually be craftable.
|
|
fleet.spawn_ships(base_cap, Vector2.ZERO)
|
|
var added: bool = fleet.add_ship()
|
|
print("fleet %d ships, add one more -> %s (now %d)" % [
|
|
base_cap, added, fleet.ships.size()
|
|
])
|
|
if not (added and fleet.ships.size() == base_cap + 1):
|
|
print("TEST_SKILL_TREE: FAIL (could not crew the extra slot)")
|
|
quit(1)
|
|
return true
|
|
if fleet.add_ship():
|
|
print("TEST_SKILL_TREE: FAIL (cap not enforced beyond the upgrade)")
|
|
quit(1)
|
|
return true
|
|
|
|
print("TEST_SKILL_TREE: PASS")
|
|
quit(0)
|
|
return true
|
|
return false
|