2026-08-11 15:55:45 +02:00
|
|
|
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
|
2026-08-12 13:07:52 +02:00
|
|
|
# 2. unloading at the mothership banks the lot and empties the hold - but
|
|
|
|
|
# only when the player asks. Arriving used to do it by itself; #24
|
|
|
|
|
# removed that, so this now checks BOTH halves: parking changes
|
|
|
|
|
# nothing, and the explicit transfer moves everything
|
2026-08-11 15:55:45 +02:00
|
|
|
# 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
|
2026-08-12 13:07:52 +02:00
|
|
|
var mothership_id: int = 0
|
2026-08-11 15:55:45 +02:00
|
|
|
|
|
|
|
|
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)
|
2026-08-12 13:07:52 +02:00
|
|
|
mothership_id = int(game_state.buildings[-1].id)
|
2026-08-11 15:55:45 +02:00
|
|
|
_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
|
2026-08-12 13:07:52 +02:00
|
|
|
# Parking alone must do nothing now (#24).
|
|
|
|
|
if game_state.fleet_cargo != 3 or game_state.rocks != banked_before:
|
|
|
|
|
print("TEST_FLEET_CARGO: FAIL (arriving at the mothership banked the hold by itself)")
|
|
|
|
|
quit(1)
|
|
|
|
|
return true
|
|
|
|
|
print("parked on the mothership -> hold still %d (no auto-banking)" % game_state.fleet_cargo)
|
|
|
|
|
|
|
|
|
|
game_state.transfer_to_building(mothership_id, 999)
|
2026-08-11 15:55:45 +02:00
|
|
|
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.
|
|
|
|
|
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
|