233 lines
8.4 KiB
GDScript3
233 lines
8.4 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Headless gameplay test: run with
|
||
|
|
# godot --headless -s res://tests/test_building_stock_queue.gd
|
||
|
|
# Forgejo #16 (part 3 of #12): a building produces out of its OWN stock, and
|
||
|
|
# an order it cannot pay for yet is queued rather than refused.
|
||
|
|
#
|
||
|
|
# Before this, BuildController deducted from the bank at the moment of
|
||
|
|
# purchase and refused outright with "Not enough rocks", so nothing ever had
|
||
|
|
# to be delivered anywhere.
|
||
|
|
#
|
||
|
|
# 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 completes the order on its own
|
||
|
|
# 4. one delivery covering several queued orders clears them all, rather
|
||
|
|
# than dribbling out one per frame
|
||
|
|
# 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 (the trip to
|
||
|
|
# a planet's surface)
|
||
|
|
# 7. production completes with NO fleet in existence, and the fleet picks
|
||
|
|
# the vessel up when it comes back - orders must not depend on which
|
||
|
|
# scene the player is standing in
|
||
|
|
|
||
|
|
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 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.rocks = 500
|
||
|
|
game_state.fleet_cargo = 0
|
||
|
|
game_state.ships_owed = 0
|
||
|
|
# Room to grow: the default cap is BASE_FLEET_SIZE (5) and the fleet
|
||
|
|
# starts at exactly 5, so without this every completed order would have
|
||
|
|
# nowhere to put its vessel and the test would be measuring the cap
|
||
|
|
# rather than the queue.
|
||
|
|
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 _process(delta: float) -> bool:
|
||
|
|
elapsed += delta
|
||
|
|
match stage:
|
||
|
|
0:
|
||
|
|
scene.get_node("AsteroidField").set_process(false)
|
||
|
|
scene.get_node("EnemySpawner").set_process(false)
|
||
|
|
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 fleet.ships.size() != ships_at_start:
|
||
|
|
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 ships=%d bank=%d (untouched)" % [
|
||
|
|
game_state.pending_orders(crafter_id), fleet.ships.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 fleet.ships.size() != ships_at_start:
|
||
|
|
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 completes the order -------------------
|
||
|
|
game_state.deposit_to_building(crafter_id, 1)
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 4
|
||
|
|
return false
|
||
|
|
4:
|
||
|
|
if elapsed < 0.4:
|
||
|
|
return false
|
||
|
|
if game_state.pending_orders(crafter_id) != 0:
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (order not completed once the stock arrived)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
if fleet.ships.size() != ships_at_start + 1:
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (no vessel delivered: %d -> %d)" % [
|
||
|
|
ships_at_start, fleet.ships.size()
|
||
|
|
])
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
if game_state.building_stock(crafter_id) != 0:
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (stock not consumed: %d left)" % game_state.building_stock(crafter_id))
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
print("supplied -> ships %d->%d, stock spent" % [ships_at_start, fleet.ships.size()])
|
||
|
|
|
||
|
|
# --- 4. one delivery clears ALL the orders it covers ---------
|
||
|
|
# Measured in TICKS, not seconds, and with more orders than the
|
||
|
|
# ticks allowed: production that handled one order per frame
|
||
|
|
# would still clear them all inside any generous wall-clock wait,
|
||
|
|
# so a timed check cannot tell "cleared at once" from "dribbled
|
||
|
|
# out". Three orders, two ticks - only clearing them together
|
||
|
|
# passes.
|
||
|
|
ships_at_start = fleet.ships.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)
|
||
|
|
ticks = 0
|
||
|
|
stage = 5
|
||
|
|
return false
|
||
|
|
5:
|
||
|
|
ticks += 1
|
||
|
|
if ticks < 2:
|
||
|
|
return false
|
||
|
|
if game_state.pending_orders(crafter_id) != 0 or fleet.ships.size() != ships_at_start + 3:
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (a delivery covering 3 orders left %d queued after %d ticks, ships %d->%d)" % [
|
||
|
|
game_state.pending_orders(crafter_id), ticks, ships_at_start, fleet.ships.size()
|
||
|
|
])
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
print("one delivery cleared 3 orders in %d ticks -> ships %d->%d" % [
|
||
|
|
ticks, ships_at_start, fleet.ships.size()
|
||
|
|
])
|
||
|
|
|
||
|
|
# --- 6 + 7. no scene, no fleet: production still completes --
|
||
|
|
# The scene has to be GONE before the order is placed. With a
|
||
|
|
# fleet still alive and under its cap it claims the vessel the
|
||
|
|
# same frame production finishes, which would leave ships_owed
|
||
|
|
# back at 0 and prove nothing about orders surviving without it.
|
||
|
|
ships_at_start = fleet.ships.size()
|
||
|
|
scene.queue_free()
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 6
|
||
|
|
return false
|
||
|
|
6:
|
||
|
|
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 = 61
|
||
|
|
return false
|
||
|
|
61:
|
||
|
|
if elapsed < 0.4:
|
||
|
|
return false
|
||
|
|
# The queue and stock live in the autoload, so tearing the space
|
||
|
|
# scene down must not touch them - and the order must complete
|
||
|
|
# even with no Fleet node anywhere to hand the vessel to.
|
||
|
|
if game_state.pending_orders(crafter_id) != 0:
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (order stalled once the space scene was gone)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
if game_state.ships_owed < 1:
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (vessel completed with no fleet present was lost, not owed)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
print("no scene at all -> order completed, %d vessel owed" % game_state.ships_owed)
|
||
|
|
|
||
|
|
# Come back to space: the fleet should claim what it is owed.
|
||
|
|
scene = load("res://Main.tscn").instantiate()
|
||
|
|
root.add_child(scene)
|
||
|
|
fleet = scene.get_node("Fleet")
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 8
|
||
|
|
return false
|
||
|
|
8:
|
||
|
|
if elapsed < 0.5:
|
||
|
|
return false
|
||
|
|
if game_state.ships_owed != 0:
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: FAIL (returning fleet never claimed its %d owed vessel(s))" % game_state.ships_owed)
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
print("fleet returned and claimed the owed vessel (ships=%d)" % fleet.ships.size())
|
||
|
|
print("TEST_BUILDING_STOCK_QUEUE: PASS")
|
||
|
|
quit(0)
|
||
|
|
return true
|
||
|
|
return false
|