179 lines
6 KiB
GDScript3
179 lines
6 KiB
GDScript3
|
|
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
|