spaceship/tests/test_outpost_crewing.gd

160 lines
7.1 KiB
GDScript3
Raw Normal View History

extends SceneTree
# Headless test: run with
# godot --headless -s res://tests/test_outpost_crewing.gd
# Forgejo #104 (#97f): "When an outpost is configured to have a given amount
# of ship, the game should transfer, whenever available, ships from the
# mothership to the outpost to always fullfill the outpost conditions if
# possible."
#
# Vessels come from the HANGAR and never from the fleet: the fleet is what
# the player is flying, and taking a vessel out of it to crew a building
# would be the game undoing the player's own arrangement.
#
# Covers:
# A. an order with nothing in the hangar waits. It does not conjure a
# vessel, and it does not take one out of the fleet
# B. a matching vessel in the hangar is dispatched, travels, and joins the
# crew on arrival
# C. it dispatches exactly the shortfall - what is already in the air
# counts, or the whole hangar leaves to fill one berth
# D. lowering the order releases the surplus back to the hangar
# E. an outpost levelled while a vessel is inbound does not destroy it:
# the vessel lands in the hangar instead
var game_state: Node
var outpost_id: int = 0
var second_id: int = 0
var stage: int = 0
var elapsed: float = 0.0
var fleet_before: int = 0
func _initialize() -> void:
# GameState is an autoload: a bare GameState.<instance member> does not
# compile in a SceneTree script, so the node is fetched by name instead.
game_state = root.get_node("GameState")
game_state.buildings.clear()
game_state.extractors.clear()
game_state.transporters.clear()
game_state.stored_vessels.clear()
game_state.vessel_deliveries.clear()
game_state.reset_progression()
game_state.reset_waves()
game_state.unlocked.append("forward_bases")
game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO)
outpost_id = game_state.add_building(GameState.BUILDING_OUTPOST, Vector2(1200.0, 0.0))
func _fail(message: String) -> bool:
print("TEST_OUTPOST_CREWING: FAIL (%s)" % message)
quit(1)
return true
# Runs the autoload's own clock without waiting out the real seconds: this
# test is about the dispatcher, not about how long a vessel takes to fly.
func _simulate(seconds: float) -> void:
var step: float = 1.0 / 60.0
var ticks: int = int(seconds / step)
for i in range(ticks):
game_state._advance_vessel_deliveries(step)
game_state._advance_garrison_dispatch()
func _process(_delta: float) -> bool:
# --- A. an order nobody can fill waits ------------------------------
game_state.set_garrison_desired(outpost_id, GameState.VESSEL_INTERCEPTOR, 2)
if game_state.garrison_desired_total(outpost_id) != 2:
return _fail("the order did not take")
_simulate(20.0)
if not game_state.garrison_crew(outpost_id).is_empty():
return _fail("an outpost crewed itself out of an empty hangar")
if not game_state.vessel_deliveries.is_empty():
return _fail("something was dispatched with nothing in the hangar")
if game_state.garrison_desired_total(outpost_id) != 2:
return _fail("the unfillable order was dropped instead of waiting")
print("an order of 2 with an empty hangar waits, and stays an order")
# --- B. a hull in the hangar is sent ---------------------------------
game_state.stored_vessels.append(GameState.VESSEL_INTERCEPTOR)
game_state._advance_garrison_dispatch()
if game_state.vessel_deliveries.size() != 1:
return _fail("a vessel in the hangar produced %d deliveries" \
% game_state.vessel_deliveries.size())
if not game_state.stored_vessels.is_empty():
return _fail("the vessel was dispatched and is still in the hangar too")
var delivery: Dictionary = game_state.vessel_deliveries[0]
if int(delivery.get("to_outpost", 0)) != outpost_id:
return _fail("the delivery is not bound for the outpost")
if float(delivery.progress) >= 1.0:
return _fail("the vessel teleported - it should have to fly there")
_simulate(30.0)
if game_state.garrison_crew_of(outpost_id, GameState.VESSEL_INTERCEPTOR) != 1:
return _fail("the vessel never arrived (crew=%s)" \
% str(game_state.garrison_crew(outpost_id)))
print("one hull in the hangar flies out and joins the crew")
# --- C. exactly the shortfall ---------------------------------------
for i in range(6):
game_state.stored_vessels.append(GameState.VESSEL_INTERCEPTOR)
game_state._advance_garrison_dispatch()
if game_state.vessel_deliveries.size() != 1:
return _fail("one empty berth dispatched %d vessels" \
% game_state.vessel_deliveries.size())
# A second call before the first lands must not send another.
game_state._advance_garrison_dispatch()
if game_state.vessel_deliveries.size() != 1:
return _fail("the dispatcher sent a second vessel for a berth already in the air")
if game_state.stored_vessels.size() != 5:
return _fail("the hangar holds %d, expected 5 left" % game_state.stored_vessels.size())
_simulate(30.0)
if game_state.garrison_crew(outpost_id).size() != 2:
return _fail("the outpost holds %d of its 2 berths" \
% game_state.garrison_crew(outpost_id).size())
game_state._advance_garrison_dispatch()
if not game_state.vessel_deliveries.is_empty():
return _fail("a full outpost is still being sent vessels")
print("2 berths, 6 spare hulls: it sent exactly 1 and then stopped")
# --- D. lowering the order releases the surplus ----------------------
var hangar_before: int = game_state.stored_vessels.size()
game_state.set_garrison_desired(outpost_id, GameState.VESSEL_INTERCEPTOR, 1)
game_state._advance_garrison_dispatch()
if game_state.garrison_crew(outpost_id).size() != 1:
return _fail("lowering the order to 1 left %d aboard" \
% game_state.garrison_crew(outpost_id).size())
_simulate(30.0)
if game_state.stored_vessels.size() != hangar_before + 1:
return _fail("the released vessel did not come home (hangar %d -> %d)" \
% [hangar_before, game_state.stored_vessels.size()])
print("lowering the order flies the surplus back to the hangar")
# --- E. a destination that stops existing ---------------------------
second_id = game_state.add_building(GameState.BUILDING_OUTPOST, Vector2(-1400.0, 0.0))
game_state.set_garrison_desired(second_id, GameState.VESSEL_INTERCEPTOR, 1)
game_state._advance_garrison_dispatch()
if game_state.garrison_inbound(second_id) != 1:
return _fail("nothing was dispatched to the second outpost")
hangar_before = game_state.stored_vessels.size()
if not game_state.destroy_building(second_id):
return _fail("the second outpost could not be destroyed")
_simulate(30.0)
if game_state.stored_vessels.size() != hangar_before + 1:
return _fail("a vessel inbound to a destroyed outpost was lost")
print("a vessel whose outpost was levelled under it lands back in the hangar")
# --- A (again). the fleet is never raided ---------------------------
game_state.stored_vessels.clear()
game_state.set_garrison_desired(outpost_id, GameState.VESSEL_INTERCEPTOR, 3)
fleet_before = game_state.surviving_ship_count
_simulate(20.0)
if game_state.surviving_ship_count != fleet_before:
return _fail("the dispatcher took a vessel out of the fleet")
if game_state.garrison_crew(outpost_id).size() != 1:
return _fail("the outpost gained a vessel from nowhere")
print("with an empty hangar it takes nothing from the fleet")
print("TEST_OUTPOST_CREWING: PASS")
quit(0)
return true