206 lines
8.7 KiB
GDScript3
206 lines
8.7 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Headless test: run with
|
||
|
|
# godot --headless -s res://tests/test_outpost_garrison_menu.gd
|
||
|
|
# Forgejo #103 (#97e): "Outposts can be occupied by ships. Only ships that
|
||
|
|
# attack can be assigned. A dedicated menu on each outpost should let you
|
||
|
|
# compose how you want the outpost to be filled. There's a default of 2
|
||
|
|
# vessel per outpost, upgrades can increase this number."
|
||
|
|
#
|
||
|
|
# Two things live on an outpost's record and conflating them is the first
|
||
|
|
# thing to get wrong: the ORDER (what the player asked for) and the CREW
|
||
|
|
# (who is actually there). This menu edits the order only; #104 fills the
|
||
|
|
# crew. Keeping the order as a wish is what lets an outpost ask for a hull
|
||
|
|
# nobody has built yet and simply wait.
|
||
|
|
#
|
||
|
|
# Covers:
|
||
|
|
# A. only armed vessels can be assigned - a shield, a hauler or an engine
|
||
|
|
# block cannot hold a position however much the player wants it there
|
||
|
|
# B. two berths by default, and the order cannot exceed them
|
||
|
|
# C. researching the berth upgrade raises the ceiling at outposts ALREADY
|
||
|
|
# standing, not only at new ones
|
||
|
|
# D. the menu opens from the outpost, drives the real order through the
|
||
|
|
# real buttons, and reads back "here / ordered" rather than one number
|
||
|
|
# E. an outpost with an order nobody can fill reports it as waiting, and
|
||
|
|
# the order survives - it is not silently dropped
|
||
|
|
|
||
|
|
var game_state: Node
|
||
|
|
var scene: Node
|
||
|
|
var controller: Node
|
||
|
|
var menu: Control
|
||
|
|
var panel: Control
|
||
|
|
var outpost_id: int = 0
|
||
|
|
var stage: int = 0
|
||
|
|
var elapsed: float = 0.0
|
||
|
|
|
||
|
|
|
||
|
|
func _initialize() -> void:
|
||
|
|
# GameState is an autoload: a bare GameState.<instance member> does not
|
||
|
|
# compile in a SceneTree script, so the node is fetched by name instead.
|
||
|
|
game_state = root.get_node("GameState")
|
||
|
|
game_state.buildings.clear()
|
||
|
|
game_state.extractors.clear()
|
||
|
|
game_state.transporters.clear()
|
||
|
|
game_state.reset_progression()
|
||
|
|
game_state.reset_waves()
|
||
|
|
game_state.rocks = 5000
|
||
|
|
game_state.unlocked.append("forward_bases")
|
||
|
|
|
||
|
|
|
||
|
|
func _fail(message: String) -> bool:
|
||
|
|
print("TEST_OUTPOST_GARRISON_MENU: FAIL (%s)" % message)
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
|
||
|
|
|
||
|
|
func _state_cases() -> bool:
|
||
|
|
# --- A. only armed vessels ------------------------------------------
|
||
|
|
var eligible: PackedStringArray = game_state.garrison_vessel_types()
|
||
|
|
if eligible.is_empty():
|
||
|
|
return _fail("no vessel at all may be garrisoned")
|
||
|
|
if not (GameState.VESSEL_INTERCEPTOR in eligible):
|
||
|
|
return _fail("the interceptor, which carries the fleet's gun, cannot be garrisoned")
|
||
|
|
for unarmed in [GameState.VESSEL_SHIELD, GameState.VESSEL_TRANSPORTER,
|
||
|
|
GameState.VESSEL_CARRIER, GameState.VESSEL_DISTORDER]:
|
||
|
|
if game_state.vessel_can_garrison(unarmed):
|
||
|
|
return _fail("%s can be garrisoned - it does not attack" % unarmed)
|
||
|
|
print("garrisonable: %s" % str(eligible))
|
||
|
|
|
||
|
|
var id: int = game_state.add_building(GameState.BUILDING_OUTPOST, Vector2(500.0, 0.0))
|
||
|
|
outpost_id = id
|
||
|
|
if not game_state.is_outpost(game_state.find_building(id)):
|
||
|
|
return _fail("the outpost does not carry the garrison capability")
|
||
|
|
if game_state.set_garrison_desired(id, GameState.VESSEL_SHIELD, 1) != 0:
|
||
|
|
return _fail("a shield vessel was accepted into a garrison")
|
||
|
|
|
||
|
|
# --- B. two berths, and the ceiling bites ---------------------------
|
||
|
|
if GameState.GARRISON_BASE_SLOTS != 2:
|
||
|
|
return _fail("the default is %d berths, not the 2 the ticket asks for" \
|
||
|
|
% GameState.GARRISON_BASE_SLOTS)
|
||
|
|
if game_state.outpost_slot_capacity() != 2:
|
||
|
|
return _fail("an unresearched outpost offers %d berths" % game_state.outpost_slot_capacity())
|
||
|
|
if game_state.set_garrison_desired(id, GameState.VESSEL_INTERCEPTOR, 5) != 2:
|
||
|
|
return _fail("an order of 5 was not clamped to the 2 berths available")
|
||
|
|
if game_state.garrison_desired_total(id) != 2:
|
||
|
|
return _fail("the outpost holds an order of %d against 2 berths" \
|
||
|
|
% game_state.garrison_desired_total(id))
|
||
|
|
print("2 berths by default; an order of 5 clamps to 2")
|
||
|
|
|
||
|
|
# --- C. the upgrade reaches existing outposts -----------------------
|
||
|
|
var upgrade_id: String = ""
|
||
|
|
for node in GameState.SKILL_TREE:
|
||
|
|
if int(node.get("outpost_bonus", 0)) > 0:
|
||
|
|
upgrade_id = String(node.id)
|
||
|
|
if upgrade_id == "":
|
||
|
|
return _fail("no skill node raises the berth count")
|
||
|
|
game_state.unlocked.append(upgrade_id)
|
||
|
|
if game_state.outpost_slot_capacity() != 3:
|
||
|
|
return _fail("researching \"%s\" left the capacity at %d" \
|
||
|
|
% [upgrade_id, game_state.outpost_slot_capacity()])
|
||
|
|
# The outpost was placed BEFORE the research, which is the case that
|
||
|
|
# matters: a bonus that only reached new buildings would mean demolishing
|
||
|
|
# and rebuilding to collect it.
|
||
|
|
if game_state.set_garrison_desired(id, GameState.VESSEL_INTERCEPTOR, 3) != 3:
|
||
|
|
return _fail("an outpost built before the upgrade did not gain the berth")
|
||
|
|
print("\"%s\" raises an already-standing outpost to 3 berths" % upgrade_id)
|
||
|
|
game_state.set_garrison_desired(id, GameState.VESSEL_INTERCEPTOR, 0)
|
||
|
|
return false
|
||
|
|
|
||
|
|
|
||
|
|
func _process(delta: float) -> bool:
|
||
|
|
elapsed += delta
|
||
|
|
match stage:
|
||
|
|
0:
|
||
|
|
if _state_cases():
|
||
|
|
return true
|
||
|
|
game_state.buildings.clear()
|
||
|
|
scene = load("res://world/Main.tscn").instantiate()
|
||
|
|
root.add_child(scene)
|
||
|
|
controller = scene.get_node("BuildController")
|
||
|
|
menu = scene.get_node("UI/GarrisonMenu")
|
||
|
|
panel = scene.get_node("UI/GarrisonMenu/Panel")
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 1
|
||
|
|
return false
|
||
|
|
1:
|
||
|
|
if elapsed < 0.3:
|
||
|
|
return false
|
||
|
|
scene.get_node("AsteroidField").set_process(false)
|
||
|
|
scene.get_node("EnemySpawner").set_process(false)
|
||
|
|
scene.get_node("BuildingField").place(GameState.BUILDING_OUTPOST, Vector2(500.0, 0.0))
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 2
|
||
|
|
return false
|
||
|
|
2:
|
||
|
|
if elapsed < 0.2:
|
||
|
|
return false
|
||
|
|
# --- D. the real menu, through the real buttons --------------
|
||
|
|
var outpost: Node2D = null
|
||
|
|
for b in scene.get_node("BuildingField").spawned:
|
||
|
|
if is_instance_valid(b) and b.building_type == GameState.BUILDING_OUTPOST:
|
||
|
|
outpost = b
|
||
|
|
if outpost == null:
|
||
|
|
return _fail("no outpost node was placed")
|
||
|
|
outpost_id = int(outpost.building_id)
|
||
|
|
if outpost.primary_action() != "garrison":
|
||
|
|
return _fail("the outpost's primary action is \"%s\", not \"garrison\"" \
|
||
|
|
% outpost.primary_action())
|
||
|
|
controller.current_building = outpost
|
||
|
|
controller._on_action_pressed()
|
||
|
|
if not menu.visible:
|
||
|
|
return _fail("the outpost did not open the garrison menu")
|
||
|
|
if panel.building_id != outpost_id:
|
||
|
|
return _fail("the panel opened without knowing which outpost it belongs to")
|
||
|
|
|
||
|
|
var row: Dictionary = {}
|
||
|
|
for candidate in panel._rows:
|
||
|
|
if String(candidate.type) == GameState.VESSEL_INTERCEPTOR:
|
||
|
|
row = candidate
|
||
|
|
if row.is_empty():
|
||
|
|
return _fail("the panel has no row for the interceptor")
|
||
|
|
for unarmed in [GameState.VESSEL_SHIELD, GameState.VESSEL_TRANSPORTER]:
|
||
|
|
for candidate in panel._rows:
|
||
|
|
if String(candidate.type) == unarmed:
|
||
|
|
return _fail("the panel offers a row for the unarmed %s" % unarmed)
|
||
|
|
|
||
|
|
row.plus.pressed.emit()
|
||
|
|
row.plus.pressed.emit()
|
||
|
|
if game_state.garrison_desired_of(outpost_id, GameState.VESSEL_INTERCEPTOR) != 2:
|
||
|
|
return _fail("two presses of + ordered %d vessels" \
|
||
|
|
% game_state.garrison_desired_of(outpost_id, GameState.VESSEL_INTERCEPTOR))
|
||
|
|
if row.count.text != "0/2":
|
||
|
|
return _fail("the row reads \"%s\" - it should show crew over order" % row.count.text)
|
||
|
|
row.minus.pressed.emit()
|
||
|
|
if game_state.garrison_desired_of(outpost_id, GameState.VESSEL_INTERCEPTOR) != 1:
|
||
|
|
return _fail("- did not take the order back down")
|
||
|
|
print("the menu drives the real order: + + - leaves 1, shown as \"%s\"" % row.count.text)
|
||
|
|
|
||
|
|
# The ceiling is enforced through the buttons too, not only in
|
||
|
|
# GameState - a + that silently did nothing would be worse than a
|
||
|
|
# disabled one.
|
||
|
|
row.plus.pressed.emit()
|
||
|
|
row.plus.pressed.emit()
|
||
|
|
row.plus.pressed.emit()
|
||
|
|
var capacity: int = game_state.outpost_slot_capacity()
|
||
|
|
if game_state.garrison_desired_total(outpost_id) != capacity:
|
||
|
|
return _fail("pressing + past the ceiling ordered %d against %d berths" \
|
||
|
|
% [game_state.garrison_desired_total(outpost_id), capacity])
|
||
|
|
if row.plus.enabled:
|
||
|
|
return _fail("+ is still offered with every berth already spoken for")
|
||
|
|
print("+ stops at %d berths and greys itself out" % capacity)
|
||
|
|
|
||
|
|
# --- E. an unfillable order waits ---------------------------
|
||
|
|
if game_state.garrison_crew(outpost_id).size() != 0:
|
||
|
|
return _fail("the outpost is crewed already, so waiting proves nothing")
|
||
|
|
controller._refresh_garrison()
|
||
|
|
var status: String = scene.get_node("UI/GarrisonMenu/StatusLabel").text
|
||
|
|
if status.strip_edges() == "":
|
||
|
|
return _fail("an outpost waiting on vessels says nothing at all")
|
||
|
|
if game_state.garrison_desired_total(outpost_id) != capacity:
|
||
|
|
return _fail("the order was dropped because it could not be filled")
|
||
|
|
print("waiting, and saying so: \"%s\"" % status)
|
||
|
|
|
||
|
|
print("TEST_OUTPOST_GARRISON_MENU: PASS")
|
||
|
|
quit(0)
|
||
|
|
return true
|
||
|
|
return false
|