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. 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