Dedup purchase requests.

Since the theater commander runs once per campaign action, missions that
do not have aircraft available may be checked more than once a turn.
Without deduping requests this can lead to cases where the AI buys
dozens of tankers on turn 0.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1470
This commit is contained in:
Dan Albert
2021-08-01 13:09:48 -07:00
parent a3e3e9046f
commit edf95ea9fb
9 changed files with 93 additions and 19 deletions

View File

@@ -9,4 +9,4 @@ class ProtectAirSpace(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
for cp, needed in state.barcaps_needed.items():
if needed > 0:
yield [PlanBarcap(cp)]
yield [PlanBarcap(cp, needed)]

View File

@@ -74,6 +74,27 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
def asap(self) -> bool:
return False
@property
def purchase_multiplier(self) -> int:
"""The multiplier for aircraft quantity when missions could not be fulfilled.
For missions that do not schedule in rounds like BARCAPs do, this should be one
to ensure that the we only purchase enough aircraft to plan the mission once.
For missions that repeat within the same turn, however, we may need to buy for
the same mission more than once. If three rounds of BARCAP still need to be
fulfilled, this would return 3, and we'd triplicate the purchase order.
There is a small misbehavior here that's not symptomatic for our current mission
planning: multi-round, multi-flight packages will only purchase multiple sets of
aircraft for whatever is unavailable for the *first* failed package. For
example, if we extend this to CAS and have no CAS aircraft but enough TARCAP
aircraft for one round, we'll order CAS for every round but will not order any
TARCAP aircraft, since we can't know that TARCAP aircraft are needed until we
attempt to plan the second mission *without returning the first round aircraft*.
"""
return 1
def fulfill_mission(self, state: TheaterState) -> bool:
self.propose_flights()
fulfiller = PackageFulfiller(
@@ -83,7 +104,9 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
state.context.settings,
)
self.package = fulfiller.plan_mission(
ProposedMission(self.target, self.flights), state.context.tracer
ProposedMission(self.target, self.flights),
self.purchase_multiplier,
state.context.tracer,
)
return self.package is not None

View File

@@ -10,6 +10,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanBarcap(PackagePlanningTask[ControlPoint]):
max_orders: int
def preconditions_met(self, state: TheaterState) -> bool:
if not state.barcaps_needed[self.target]:
return False
@@ -20,3 +22,7 @@ class PlanBarcap(PackagePlanningTask[ControlPoint]):
def propose_flights(self) -> None:
self.propose_flight(FlightType.BARCAP, 2)
@property
def purchase_multiplier(self) -> int:
return self.max_orders