256 lines
9.2 KiB
GDScript
256 lines
9.2 KiB
GDScript
extends SceneTree
|
|
|
|
# Headless gameplay test: run with
|
|
# godot --headless -s res://tests/test_building_stock_queue.gd
|
|
# Forgejo #16 (part 3 of #12), updated for #23: a building produces out of
|
|
# its OWN stock, an order it cannot pay for yet is queued rather than
|
|
# refused, and building now TAKES TIME.
|
|
#
|
|
# What #23 changed here: orders no longer complete the instant the ore
|
|
# lands. The head of the queue consumes its ore, builds for build_time, and
|
|
# only then finishes - and it finishes into the mothership hangar rather
|
|
# than straight into the fleet. So a queue is something the player can watch
|
|
# and cancel, and a full fleet no longer stops a crafter working.
|
|
#
|
|
# Verifies:
|
|
# 1. ordering with an empty stock queues instead of failing, and produces
|
|
# nothing yet
|
|
# 2. it does NOT quietly take the cost out of the bank - the whole point is
|
|
# that the building has to be supplied
|
|
# 3. delivering enough stock starts the build, and it completes on its own
|
|
# 4. orders build ONE AT A TIME in order, not all at once the moment a big
|
|
# delivery covers them
|
|
# 5. a part-delivery is not enough: stock below the cost leaves the order
|
|
# queued and the ore untouched
|
|
# 6. stock and queue survive the space scene being torn down
|
|
# 7. a build completes with NO fleet in existence, into the hangar, and
|
|
# waits there - it is not lost, and not silently crewed either
|
|
|
|
var game_state: Node
|
|
var scene: Node
|
|
var fleet: Node
|
|
var field: Node
|
|
var crafter_id: int = 0
|
|
var stage: int = 0
|
|
var elapsed: float = 0.0
|
|
var ships_at_start: int = 0
|
|
var bank_at_start: int = 0
|
|
var stored_at_start: int = 0
|
|
var ticks: int = 0
|
|
|
|
const CRAFTER_AT: Vector2 = Vector2(900.0, 0.0)
|
|
|
|
|
|
func _initialize() -> void:
|
|
game_state = root.get_node("GameState")
|
|
game_state.buildings.clear()
|
|
game_state.extractors.clear()
|
|
game_state.stored_vessels.clear()
|
|
game_state.rocks = 500
|
|
game_state.fleet_cargo = 0
|
|
# Real build times are 10s+; the timed path is what matters here, not the
|
|
# wall clock. See GameState.build_time_scale.
|
|
game_state.build_time_scale = 0.02
|
|
game_state.unlocked = ["shipyards", "coordination", "wing", "squadron", "armada"]
|
|
scene = load("res://Main.tscn").instantiate()
|
|
root.add_child(scene)
|
|
fleet = scene.get_node("Fleet")
|
|
field = scene.get_node("BuildingField")
|
|
|
|
|
|
func _build_time() -> float:
|
|
return game_state.build_time(GameState.PRODUCT_SHIP)
|
|
|
|
|
|
func _process(delta: float) -> bool:
|
|
elapsed += delta
|
|
match stage:
|
|
0:
|
|
scene.get_node("AsteroidField").set_process(false)
|
|
scene.get_node("EnemySpawner").set_process(false)
|
|
_clear_hazards()
|
|
fleet.spawn_ships(2, Vector2.ZERO)
|
|
field.place(GameState.BUILDING_SHIP_CRAFTER, CRAFTER_AT)
|
|
crafter_id = int(game_state.buildings[-1].id)
|
|
ships_at_start = fleet.ships.size()
|
|
bank_at_start = game_state.rocks
|
|
elapsed = 0.0
|
|
stage = 1
|
|
return false
|
|
1:
|
|
if elapsed < 0.2:
|
|
return false
|
|
# --- 1 + 2. an unaffordable order queues, and costs nothing --
|
|
if not game_state.queue_order(crafter_id):
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (the crafter refused an order outright)")
|
|
quit(1)
|
|
return true
|
|
elapsed = 0.0
|
|
stage = 2
|
|
return false
|
|
2:
|
|
if elapsed < 0.3:
|
|
return false
|
|
if game_state.pending_orders(crafter_id) != 1:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (order did not stay queued: %d pending)" % game_state.pending_orders(crafter_id))
|
|
quit(1)
|
|
return true
|
|
if not game_state.stored_vessels.is_empty():
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (a vessel appeared with an empty stock)")
|
|
quit(1)
|
|
return true
|
|
if game_state.rocks != bank_at_start:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (queueing took %d out of the bank - the building is supposed to be supplied)" % (bank_at_start - game_state.rocks))
|
|
quit(1)
|
|
return true
|
|
print("empty stock -> queued=%d stored=%d bank=%d (untouched)" % [
|
|
game_state.pending_orders(crafter_id), game_state.stored_vessels.size(), game_state.rocks
|
|
])
|
|
|
|
# --- 5. a part-delivery is not enough -----------------------
|
|
game_state.deposit_to_building(crafter_id, GameState.SHIP_COST - 1)
|
|
elapsed = 0.0
|
|
stage = 3
|
|
return false
|
|
3:
|
|
if elapsed < 0.3:
|
|
return false
|
|
if game_state.pending_orders(crafter_id) != 1 or not game_state.stored_vessels.is_empty():
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (produced on a part-delivery of %d against a cost of %d)" % [
|
|
GameState.SHIP_COST - 1, GameState.SHIP_COST
|
|
])
|
|
quit(1)
|
|
return true
|
|
if game_state.building_stock(crafter_id) != GameState.SHIP_COST - 1:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (part-delivered ore was consumed anyway)")
|
|
quit(1)
|
|
return true
|
|
print("part delivery (%d of %d) -> still queued, ore intact" % [
|
|
game_state.building_stock(crafter_id), GameState.SHIP_COST
|
|
])
|
|
|
|
# --- 3. topping it up starts the build ----------------------
|
|
game_state.deposit_to_building(crafter_id, 1)
|
|
elapsed = 0.0
|
|
stage = 4
|
|
return false
|
|
4:
|
|
# One tick: the build must have STARTED (ore taken) but not yet
|
|
# finished, or "takes time" means nothing.
|
|
ticks += 1
|
|
if ticks < 2:
|
|
return false
|
|
var queue: Array = game_state.order_list(crafter_id)
|
|
if queue.is_empty() or not bool(queue[0].started):
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (a fully funded order never started building)")
|
|
quit(1)
|
|
return true
|
|
if game_state.building_stock(crafter_id) != 0:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (starting the build did not consume the ore)")
|
|
quit(1)
|
|
return true
|
|
if not game_state.stored_vessels.is_empty():
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (the vessel appeared instantly - builds are supposed to take time)")
|
|
quit(1)
|
|
return true
|
|
print("funded -> building started, ore consumed, nothing delivered yet")
|
|
elapsed = 0.0
|
|
stage = 5
|
|
return false
|
|
5:
|
|
if elapsed < _build_time() * 2.0:
|
|
return false
|
|
if game_state.pending_orders(crafter_id) != 0:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (build never finished)")
|
|
quit(1)
|
|
return true
|
|
if game_state.stored_vessels.size() != 1:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (finished build did not reach the hangar: %d stored)" % game_state.stored_vessels.size())
|
|
quit(1)
|
|
return true
|
|
if fleet.ships.size() != ships_at_start:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (the vessel crewed itself - the player is supposed to pick it up)")
|
|
quit(1)
|
|
return true
|
|
print("built -> 1 vessel waiting in the hangar, fleet untouched at %d" % fleet.ships.size())
|
|
|
|
# --- 4. one at a time, in order -----------------------------
|
|
stored_at_start = game_state.stored_vessels.size()
|
|
game_state.queue_order(crafter_id)
|
|
game_state.queue_order(crafter_id)
|
|
game_state.queue_order(crafter_id)
|
|
game_state.deposit_to_building(crafter_id, GameState.SHIP_COST * 3)
|
|
elapsed = 0.0
|
|
stage = 6
|
|
return false
|
|
6:
|
|
# Enough time for exactly one build, and no more. Funding all
|
|
# three at once must NOT finish all three at once - that is the
|
|
# behaviour #23 replaced.
|
|
if elapsed < _build_time() * 1.4:
|
|
return false
|
|
var done: int = game_state.stored_vessels.size() - stored_at_start
|
|
if done != 1:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (after one build time, %d of 3 orders finished - they are not building one at a time)" % done)
|
|
quit(1)
|
|
return true
|
|
if game_state.pending_orders(crafter_id) != 2:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (%d orders left, expected 2 still queued)" % game_state.pending_orders(crafter_id))
|
|
quit(1)
|
|
return true
|
|
print("one build time -> 1 of 3 done, 2 still queued (sequential)")
|
|
elapsed = 0.0
|
|
stage = 7
|
|
return false
|
|
7:
|
|
if elapsed < _build_time() * 4.0:
|
|
return false
|
|
if game_state.stored_vessels.size() - stored_at_start != 3:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (the rest of the queue never drained: %d of 3)" % (game_state.stored_vessels.size() - stored_at_start))
|
|
quit(1)
|
|
return true
|
|
print("queue drained in order -> 3 vessels waiting")
|
|
|
|
# --- 6 + 7. no scene, no fleet ------------------------------
|
|
stored_at_start = game_state.stored_vessels.size()
|
|
scene.queue_free()
|
|
elapsed = 0.0
|
|
stage = 8
|
|
return false
|
|
8:
|
|
if elapsed < 0.3:
|
|
return false
|
|
if is_instance_valid(fleet):
|
|
return false
|
|
game_state.queue_order(crafter_id)
|
|
game_state.deposit_to_building(crafter_id, GameState.SHIP_COST)
|
|
elapsed = 0.0
|
|
stage = 9
|
|
return false
|
|
9:
|
|
if elapsed < _build_time() * 3.0:
|
|
return false
|
|
# The queue, the stock and the hangar live in the autoload, so
|
|
# tearing the space scene down must not touch them.
|
|
if game_state.pending_orders(crafter_id) != 0:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (build stalled once the space scene was gone)")
|
|
quit(1)
|
|
return true
|
|
if game_state.stored_vessels.size() != stored_at_start + 1:
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (a vessel built with no fleet present was lost)")
|
|
quit(1)
|
|
return true
|
|
print("no scene at all -> build completed into the hangar (%d waiting)" % game_state.stored_vessels.size())
|
|
print("TEST_BUILDING_STOCK_QUEUE: PASS")
|
|
quit(0)
|
|
return true
|
|
return false
|
|
|
|
|
|
func _clear_hazards() -> void:
|
|
for a in get_nodes_in_group("asteroids"):
|
|
if is_instance_valid(a):
|
|
a.queue_free()
|
|
for e in get_nodes_in_group("enemies"):
|
|
if is_instance_valid(e):
|
|
e.queue_free()
|