diff --git a/changelog.md b/changelog.md index 19281988..18c44702 100644 --- a/changelog.md +++ b/changelog.md @@ -36,6 +36,7 @@ Saves from 4.0.0 are compatible with 4.1.0. * **[Mission Generation]** Prevent the creation of a transfer order with 0 units for a rare situtation when a point was captured. * **[Mission Generation]** Planned transfers which will be impossible after a base capture will no longer prevent the mission result submit. * **[Mission Generation]** Fix occasional KeyError preventing mission generation when all units of the same type in a convoy were killed. +* **[Campaign AI]** Transport aircraft will now be bought only if necessary at control points which can produce ground units and are capable to operate transport aircraft. * **[UI]** Statistics window tick marks are now always integers. * **[UI]** Statistics window now shows the correct info for the turn * **[UI]** Toggling custom loadout for an aircraft with no preset loadouts no longer breaks the flight. diff --git a/game/transfers.py b/game/transfers.py index a1f73b49..eb07ecee 100644 --- a/game/transfers.py +++ b/game/transfers.py @@ -686,9 +686,39 @@ class PendingTransfers: ): self.order_airlift_assets_at(control_point) - @staticmethod - def desired_airlift_capacity(control_point: ControlPoint) -> int: - return 4 if control_point.has_factory else 0 + def desired_airlift_capacity(self, control_point: ControlPoint) -> int: + + if control_point.has_factory: + is_major_hub = control_point.total_aircraft_parking > 0 + # Check if there is a CP which is only reachable via Airlift + transit_network = self.network_for(control_point) + for cp in self.game.theater.control_points_for(control_point.captured): + # check if the CP has no factory, is reachable from the current + # position and can only be reached with airlift connections + if ( + cp.can_deploy_ground_units + and not cp.has_factory + and transit_network.has_link(control_point, cp) + and not any( + link_type + for link, link_type in transit_network.nodes[cp].items() + if not link_type == TransitConnection.Airlift + ) + ): + return 4 + + if ( + is_major_hub + and cp.has_factory + and cp.total_aircraft_parking > control_point.total_aircraft_parking + ): + is_major_hub = False + + if is_major_hub: + # If the current CP is a major hub keep always 2 planes on reserve + return 2 + + return 0 def current_airlift_capacity(self, control_point: ControlPoint) -> int: inventory = self.game.aircraft_inventory.for_control_point(control_point) @@ -703,9 +733,16 @@ class PendingTransfers: ) def order_airlift_assets_at(self, control_point: ControlPoint) -> None: - gap = self.desired_airlift_capacity( - control_point - ) - self.current_airlift_capacity(control_point) + unclaimed_parking = control_point.unclaimed_parking(self.game) + # Buy a maximum of unclaimed_parking only to prevent that aircraft procurement + # take place at another base + gap = min( + [ + self.desired_airlift_capacity(control_point) + - self.current_airlift_capacity(control_point), + unclaimed_parking, + ] + ) if gap <= 0: return @@ -715,6 +752,10 @@ class PendingTransfers: # aesthetic. gap += 1 + if gap > unclaimed_parking: + # Prevent to buy more aircraft than possible + return + self.game.procurement_requests_for(player=control_point.captured).append( AircraftProcurementRequest( control_point, nautical_miles(200), FlightType.TRANSPORT, gap