spaceship/tests/test_fleet_cargo.gd

179 lines
6 KiB
GDScript3
Raw Normal View History

extends SceneTree
# Headless gameplay test: run with
# godot --headless -s res://tests/test_fleet_cargo.gd
# Forgejo #14 (part 1 of #12): mined ore is CARGO the fleet is carrying, not
# instant global income. It only becomes spendable once the fleet flies it
# back to the mothership.
#
# Before this, Rock.gd did `GameState.rocks += 1` on pickup, so ore was
# banked instantly from anywhere on the map and a mining run had nothing to
# lose.
#
# Verifies:
# 1. mining fills the hold and leaves the bank alone
# 2. reaching the mothership banks the lot, and empties the hold
# 3. with no mothership there is nowhere to deliver, so cargo just
# accumulates - it is never quietly banked anyway
# 4. sitting on a DIFFERENT building is not a delivery
# 5. ore is conserved: nothing is created or destroyed in the transfer
# 6. cargo survives the trip to a planet's surface and back, which is
# exactly the trip a mining run is
var game_state: Node
var scene: Node
var fleet: Node
var field: Node
var stage: int = 0
var elapsed: float = 0.0
var banked_before: int = 0
const MOTHERSHIP_AT: Vector2 = Vector2(1200.0, 0.0)
const CRAFTER_AT: Vector2 = Vector2(-4000.0, 0.0)
func _initialize() -> void:
game_state = root.get_node("GameState")
game_state.buildings.clear()
game_state.extractors.clear()
game_state.rocks = 100
game_state.fleet_cargo = 0
scene = load("res://Main.tscn").instantiate()
root.add_child(scene)
fleet = scene.get_node("Fleet")
field = scene.get_node("BuildingField")
func _park_fleet_on(at: Vector2) -> void:
for ship in fleet.ships:
ship.position = at
fleet.move_to(at)
func _process(delta: float) -> bool:
elapsed += delta
match stage:
0:
scene.get_node("AsteroidField").set_process(false)
scene.get_node("EnemySpawner").set_process(false)
# --- 1. mining fills the hold, not the bank -----------------
# Through a REAL Rock, flown over by a real ship, rather than by
# calling collect_cargo() directly: the whole change is which
# pool Rock.gd credits on pickup, and a test that calls the new
# API by hand passes just as happily with Rock.gd left untouched.
banked_before = game_state.rocks
var rock: Area2D = load("res://Rock.tscn").instantiate()
rock.build_shape(1)
rock.global_position = fleet.ships[0].global_position
scene.add_child(rock)
elapsed = 0.0
stage = 5
return false
5:
# A tick for the Area2D overlap to register.
if elapsed < 0.25:
return false
if game_state.fleet_cargo != 1:
print("TEST_FLEET_CARGO: FAIL (flying over a rock put %d in the hold)" % game_state.fleet_cargo)
quit(1)
return true
if game_state.rocks != banked_before:
print("TEST_FLEET_CARGO: FAIL (a mined rock was banked instantly: %d -> %d)" % [
banked_before, game_state.rocks
])
quit(1)
return true
print("flew over a real rock -> hold=%d bank=%d (unchanged)" % [
game_state.fleet_cargo, game_state.rocks
])
game_state.collect_cargo(1)
game_state.collect_cargo(1)
if game_state.fleet_cargo != 3:
print("TEST_FLEET_CARGO: FAIL (mining did not fill the hold: %d)" % game_state.fleet_cargo)
quit(1)
return true
if game_state.rocks != banked_before:
print("TEST_FLEET_CARGO: FAIL (mining banked %d straight away)" % (game_state.rocks - banked_before))
quit(1)
return true
print("mined 3 -> hold=%d bank=%d (unchanged)" % [game_state.fleet_cargo, game_state.rocks])
# --- 3. no mothership means nowhere to deliver --------------
_park_fleet_on(Vector2.ZERO)
elapsed = 0.0
stage = 1
return false
1:
if elapsed < 0.4:
return false
if game_state.fleet_cargo != 3 or game_state.rocks != banked_before:
print("TEST_FLEET_CARGO: FAIL (delivered with no mothership built)")
quit(1)
return true
print("no mothership -> hold still %d, bank still %d" % [game_state.fleet_cargo, game_state.rocks])
# --- 4. a different building is not a delivery point --------
field.place(GameState.BUILDING_SHIP_CRAFTER, CRAFTER_AT)
field.place(GameState.BUILDING_MOTHERSHIP, MOTHERSHIP_AT)
_park_fleet_on(CRAFTER_AT)
elapsed = 0.0
stage = 2
return false
2:
if elapsed < 0.4:
return false
if game_state.fleet_cargo != 3:
print("TEST_FLEET_CARGO: FAIL (the ship crafter accepted a delivery)")
quit(1)
return true
print("parked on the crafter -> hold still %d" % game_state.fleet_cargo)
# --- 2. the mothership banks it -----------------------------
_park_fleet_on(MOTHERSHIP_AT)
elapsed = 0.0
stage = 3
return false
3:
if elapsed < 0.4:
return false
var total_before: int = banked_before + 3
print("delivered: hold=%d bank=%d (expected hold=0 bank=%d)" % [
game_state.fleet_cargo, game_state.rocks, total_before
])
if game_state.fleet_cargo != 0:
print("TEST_FLEET_CARGO: FAIL (hold not emptied on delivery)")
quit(1)
return true
# --- 5. conservation ----------------------------------------
if game_state.rocks != total_before:
print("TEST_FLEET_CARGO: FAIL (ore was created or destroyed in the transfer)")
quit(1)
return true
# --- 6. cargo survives the scene swap -----------------------
# Not simulated by loading PlanetSurface.tscn here: the point is
# that cargo lives in the autoload rather than in Main.tscn, so
# tearing Main.tscn down must not touch it.
# Fly off the mothership first, or this new cargo is (correctly)
# banked the moment BuildingField next runs, and the teardown
# check below would be measuring a delivery instead.
_park_fleet_on(CRAFTER_AT)
game_state.collect_cargo(5)
scene.queue_free()
elapsed = 0.0
stage = 4
return false
4:
if elapsed < 0.3:
return false
if game_state.fleet_cargo != 5:
print("TEST_FLEET_CARGO: FAIL (cargo lost when the space scene was torn down: %d)" % game_state.fleet_cargo)
quit(1)
return true
print("cargo survived the scene teardown: hold=%d" % game_state.fleet_cargo)
print("TEST_FLEET_CARGO: PASS")
quit(0)
return true
return false