From f5c60f8805ee62cc1e7dfaf1369a3e1ce35fa6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Wisniewski?= Date: Sat, 15 Aug 2026 10:41:18 +0200 Subject: [PATCH] Prioritize queued logistics deliveries --- GameState.gd | 63 ++++++++++++++++------------- tests/test_multi_resource_supply.gd | 18 +++++++-- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/GameState.gd b/GameState.gd index 5366a49..3854f56 100644 --- a/GameState.gd +++ b/GameState.gd @@ -1155,6 +1155,40 @@ func _dispatch_transporters() -> void: var mothership: Variant = get_mothership_position() if mothership == null: return # nowhere to load from + # Supply already-queued production and research before collecting passive + # extractor output. Extractors can refill forever, so servicing them first + # can otherwise consume every transporter each time one becomes idle and + # permanently starve a partly supplied order. + for b in buildings: + var needed_resource := "" + for resource_type in resource_types(): + if outstanding_resource_cost(int(b.id), resource_type) > 0 \ + and bank_inventory.amount(resource_type) > 0: + needed_resource = resource_type + break + if needed_resource == "": + continue + # One at a time per building: without this every idle transporter + # would be sent after the same shortfall and they would all arrive + # carrying resources nobody needs any more. + if _transporter_inbound_to(int(b.id)): + continue + var chosen: Dictionary = {} + var best_d: float = INF + for t in transporters: + if t.state != TRANSPORTER_IDLE: + continue + var here: Vector2 = transporter_position(t) + var d: float = here.distance_squared_to(b.position) + if d < best_d: + best_d = d + chosen = t + if chosen.is_empty(): + return # every transporter is busy + chosen.target_id = int(b.id) + chosen.resource_type = needed_resource + _begin_leg(chosen, transporter_position(chosen), mothership, TRANSPORTER_TO_MOTHERSHIP) + # A distant extractor is worth visiting only once it has a full hold ready. # This keeps the transporter system meaningful: mining is local until a # vessel physically carries a researched-sized load home. @@ -1180,35 +1214,6 @@ func _dispatch_transporters() -> void: chosen_extractor.target_id = 0 _begin_leg(chosen_extractor, transporter_position(chosen_extractor), extractor.position, TRANSPORTER_TO_EXTRACTOR) - for b in buildings: - var needed_resource := "" - for resource_type in resource_types(): - if outstanding_resource_cost(int(b.id), resource_type) > 0 \ - and bank_inventory.amount(resource_type) > 0: - needed_resource = resource_type - break - if needed_resource == "": - continue - # One at a time per building: without this every idle transporter - # would be sent after the same shortfall and they would all arrive - # carrying ore nobody needs any more. - if _transporter_inbound_to(int(b.id)): - continue - var chosen: Dictionary = {} - var best_d: float = INF - for t in transporters: - if t.state != TRANSPORTER_IDLE: - continue - var here: Vector2 = transporter_position(t) - var d: float = here.distance_squared_to(b.position) - if d < best_d: - best_d = d - chosen = t - if chosen.is_empty(): - return # every transporter is busy - chosen.target_id = int(b.id) - chosen.resource_type = needed_resource - _begin_leg(chosen, transporter_position(chosen), mothership, TRANSPORTER_TO_MOTHERSHIP) func _advance_transporters(delta: float) -> void: diff --git a/tests/test_multi_resource_supply.gd b/tests/test_multi_resource_supply.gd index 45a016a..d29b574 100644 --- a/tests/test_multi_resource_supply.gd +++ b/tests/test_multi_resource_supply.gd @@ -9,7 +9,7 @@ func _initialize() -> void: state.stored_vessels.clear() state.reset_progression() state.rocks = 0 - state.gas = 30 + state.gas = 10 state.build_time_scale = 0.01 state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO) @@ -18,8 +18,20 @@ func _initialize() -> void: if not state.queue_lab_research(lab_id, "unlock_gas_bomber"): _fail("could not queue gas research") return - if state.outstanding_resource_cost(lab_id, GameState.RESOURCE_GAS) != 30: - _fail("lab did not request 30 gas") + # Reproduce a first 20-unit delivery followed by the remaining 10 being + # available at the mothership. Full extractors must not take priority over + # completing this already-partly-supplied research order. + state.deposit_resource_to_building(lab_id, GameState.RESOURCE_GAS, 20) + state.add_extractor("Ore A", Vector2(240, 0), GameState.RESOURCE_ORE) + state.add_extractor("Ore B", Vector2(360, 0), GameState.RESOURCE_ORE) + for extractor in state.extractors: + extractor.inventory.deposit(GameState.RESOURCE_ORE, state.transporter_load_limit()) + if state.outstanding_resource_cost(lab_id, GameState.RESOURCE_GAS) != 10: + _fail("partly supplied lab did not request its remaining 10 gas") + return + state._dispatch_transporters() + if not state._transporter_inbound_to(lab_id): + _fail("extractor collection took priority over waiting research") return # Large steps make each physical leg complete in one call; multiple calls