mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Revert "Don't propose missions the air wing can't plan."
This is redundant because plan_mission already checks this. This reverts commit 3338df9836878714f8b72aeb176bed16a33eb65b.
This commit is contained in:
parent
63af28b016
commit
d074500109
@ -29,7 +29,6 @@ Saves from 3.x are not compatible with 4.0.
|
||||
## Fixes
|
||||
|
||||
* **[Campaign AI]** Fix procurement for factions that lack some unit types.
|
||||
* **[Campaign AI]** Improved pruning of unplannable missions which should improve turn cycle time and prevent the auto-planner from quitting early.
|
||||
* **[Mission Generation]** Fixed problem with mission load when control point name contained an apostrophe.
|
||||
* **[Mission Generation]** Fixed EWR group names so they contribute to Skynet again.
|
||||
* **[Mission Generation]** Fixed duplicate name error when generating convoys and cargo ships when creating manual transfers after loading a game.
|
||||
|
||||
@ -613,18 +613,33 @@ class CoalitionMissionPlanner:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def oca_aircraft_plannable(self) -> bool:
|
||||
return (
|
||||
self.air_wing_can_plan(FlightType.OCA_AIRCRAFT)
|
||||
and self.game.settings.default_start_type == "Cold"
|
||||
def critical_missions(self) -> Iterator[ProposedMission]:
|
||||
"""Identifies the most important missions to plan this turn.
|
||||
|
||||
Non-critical missions that cannot be fulfilled will create purchase
|
||||
orders for the next turn. Critical missions will create a purchase order
|
||||
unless the mission can be doubly fulfilled. In other words, the AI will
|
||||
attempt to have *double* the aircraft it needs for these missions to
|
||||
ensure that they can be planned again next turn even if all aircraft are
|
||||
eliminated this turn.
|
||||
"""
|
||||
|
||||
# Find farthest, friendly CP for AEWC.
|
||||
yield ProposedMission(
|
||||
self.objective_finder.farthest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)],
|
||||
# Supports all the early CAP flights, so should be in the air ASAP.
|
||||
asap=True,
|
||||
)
|
||||
|
||||
yield ProposedMission(
|
||||
self.objective_finder.closest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.REFUELING, 1, self.MAX_TANKER_RANGE)],
|
||||
)
|
||||
|
||||
def propose_barcap(self) -> Iterator[ProposedMission]:
|
||||
# Find friendly CPs within 100 nmi from an enemy airfield, plan CAP.
|
||||
for cp in self.objective_finder.vulnerable_control_points():
|
||||
# Plan CAP in such a way, that it is established during the whole desired
|
||||
# mission length.
|
||||
# Plan CAP in such a way, that it is established during the whole desired mission length
|
||||
for _ in range(
|
||||
0,
|
||||
int(self.game.settings.desired_player_mission_duration.total_seconds()),
|
||||
@ -637,31 +652,36 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_cas(self) -> Iterator[ProposedMission]:
|
||||
# Find front lines, plan CAS.
|
||||
for front_line in self.objective_finder.front_lines():
|
||||
flights = [ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE)]
|
||||
if self.air_wing_can_plan(FlightType.TARCAP):
|
||||
# This is *not* an escort because front lines don't create a threat
|
||||
# zone. Generating threat zones from front lines causes the front
|
||||
# line to push back BARCAPs as it gets closer to the base. While
|
||||
# front lines do have the same problem of potentially pulling
|
||||
# BARCAPs off bases to engage a front line TARCAP, that's probably
|
||||
# the one time where we do want that.
|
||||
#
|
||||
# TODO: Use intercepts and extra TARCAPs to cover bases near fronts.
|
||||
# We don't have intercept missions yet so this isn't something we
|
||||
# can do today, but we should probably return to having the front
|
||||
# line project a threat zone (so that strike missions will route
|
||||
# around it) and instead *not plan* a BARCAP at bases near the
|
||||
# front, since there isn't a place to put a barrier. Instead, the
|
||||
# aircraft that would have been a BARCAP could be used as additional
|
||||
# interceptors and TARCAPs which will defend the base but won't be
|
||||
# trying to avoid front line contacts.
|
||||
flights.append(ProposedFlight(FlightType.TARCAP, 2, self.MAX_CAP_RANGE))
|
||||
yield ProposedMission(front_line, flights)
|
||||
yield ProposedMission(
|
||||
front_line,
|
||||
[
|
||||
ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE),
|
||||
# This is *not* an escort because front lines don't create a threat
|
||||
# zone. Generating threat zones from front lines causes the front
|
||||
# line to push back BARCAPs as it gets closer to the base. While
|
||||
# front lines do have the same problem of potentially pulling
|
||||
# BARCAPs off bases to engage a front line TARCAP, that's probably
|
||||
# the one time where we do want that.
|
||||
#
|
||||
# TODO: Use intercepts and extra TARCAPs to cover bases near fronts.
|
||||
# We don't have intercept missions yet so this isn't something we
|
||||
# can do today, but we should probably return to having the front
|
||||
# line project a threat zone (so that strike missions will route
|
||||
# around it) and instead *not plan* a BARCAP at bases near the
|
||||
# front, since there isn't a place to put a barrier. Instead, the
|
||||
# aircraft that would have been a BARCAP could be used as additional
|
||||
# interceptors and TARCAPs which will defend the base but won't be
|
||||
# trying to avoid front line contacts.
|
||||
ProposedFlight(FlightType.TARCAP, 2, self.MAX_CAP_RANGE),
|
||||
],
|
||||
)
|
||||
|
||||
def propose_missions(self) -> Iterator[ProposedMission]:
|
||||
"""Identifies and iterates over potential mission in priority order."""
|
||||
yield from self.critical_missions()
|
||||
|
||||
def propose_dead(self) -> Iterator[ProposedMission]:
|
||||
# Find enemy SAM sites with ranges that cover friendly CPs, front lines,
|
||||
# or objects, plan DEAD.
|
||||
# Find enemy SAM sites with ranges that extend to within 50 nmi of
|
||||
@ -686,10 +706,7 @@ class CoalitionMissionPlanner:
|
||||
else:
|
||||
flights.append(
|
||||
ProposedFlight(
|
||||
FlightType.SEAD_ESCORT,
|
||||
2,
|
||||
self.MAX_SEAD_RANGE,
|
||||
EscortType.Sead,
|
||||
FlightType.SEAD_ESCORT, 2, self.MAX_SEAD_RANGE, EscortType.Sead
|
||||
)
|
||||
)
|
||||
# TODO: Max escort range.
|
||||
@ -700,7 +717,6 @@ class CoalitionMissionPlanner:
|
||||
)
|
||||
yield ProposedMission(sam, flights)
|
||||
|
||||
def propose_convoy_interdiction(self) -> Iterator[ProposedMission]:
|
||||
# These will only rarely get planned. When a convoy is travelling multiple legs,
|
||||
# they're targetable after the first leg. The reason for this is that
|
||||
# procurement happens *after* mission planning so that the missions that could
|
||||
@ -729,7 +745,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_shipping_interdiction(self) -> Iterator[ProposedMission]:
|
||||
for ship in self.objective_finder.cargo_ships():
|
||||
yield ProposedMission(
|
||||
ship,
|
||||
@ -745,7 +760,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_naval_strikes(self) -> Iterator[ProposedMission]:
|
||||
for group in self.objective_finder.threatening_ships():
|
||||
yield ProposedMission(
|
||||
group,
|
||||
@ -761,7 +775,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_bai(self) -> Iterator[ProposedMission]:
|
||||
for group in self.objective_finder.threatening_vehicle_groups():
|
||||
yield ProposedMission(
|
||||
group,
|
||||
@ -777,25 +790,16 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_oca_strikes(self) -> Iterator[ProposedMission]:
|
||||
for target in self.objective_finder.oca_targets(min_aircraft=20):
|
||||
flights = []
|
||||
if self.air_wing_can_plan(FlightType.OCA_RUNWAY):
|
||||
flights.append(
|
||||
ProposedFlight(FlightType.OCA_RUNWAY, 2, self.MAX_OCA_RANGE)
|
||||
)
|
||||
if self.oca_aircraft_plannable:
|
||||
flights = [
|
||||
ProposedFlight(FlightType.OCA_RUNWAY, 2, self.MAX_OCA_RANGE),
|
||||
]
|
||||
if self.game.settings.default_start_type == "Cold":
|
||||
# Only schedule if the default start type is Cold. If the player
|
||||
# has set anything else there are no targets to hit.
|
||||
flights.append(
|
||||
ProposedFlight(FlightType.OCA_AIRCRAFT, 2, self.MAX_OCA_RANGE)
|
||||
)
|
||||
if not flights:
|
||||
raise RuntimeError(
|
||||
"Attempted planning of OCA strikes but neither OCA/Runway nor "
|
||||
f"OCA/Aircraft are plannable for {self.faction.name} with the "
|
||||
"current game settings."
|
||||
)
|
||||
flights.extend(
|
||||
[
|
||||
# TODO: Max escort range.
|
||||
@ -809,7 +813,7 @@ class CoalitionMissionPlanner:
|
||||
)
|
||||
yield ProposedMission(target, flights)
|
||||
|
||||
def propose_building_strikes(self) -> Iterator[ProposedMission]:
|
||||
# Plan strike missions.
|
||||
for target in self.objective_finder.strike_targets():
|
||||
yield ProposedMission(
|
||||
target,
|
||||
@ -828,48 +832,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_missions(self) -> Iterator[ProposedMission]:
|
||||
"""Identifies and iterates over potential mission in priority order."""
|
||||
# Find farthest, friendly CP for AEWC.
|
||||
if self.air_wing_can_plan(FlightType.AEWC):
|
||||
yield ProposedMission(
|
||||
self.objective_finder.farthest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)],
|
||||
# Supports all the early CAP flights, so should be in the air ASAP.
|
||||
asap=True,
|
||||
)
|
||||
|
||||
if self.air_wing_can_plan(FlightType.REFUELING):
|
||||
yield ProposedMission(
|
||||
self.objective_finder.closest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.REFUELING, 1, self.MAX_TANKER_RANGE)],
|
||||
)
|
||||
|
||||
if self.air_wing_can_plan(FlightType.BARCAP):
|
||||
yield from self.propose_barcap()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.CAS):
|
||||
yield from self.propose_cas()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.DEAD):
|
||||
yield from self.propose_dead()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.BAI):
|
||||
yield from self.propose_convoy_interdiction()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.ANTISHIP):
|
||||
yield from self.propose_shipping_interdiction()
|
||||
yield from self.propose_naval_strikes()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.BAI):
|
||||
yield from self.propose_bai()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.OCA_RUNWAY) or self.oca_aircraft_plannable:
|
||||
yield from self.propose_oca_strikes()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.STRIKE):
|
||||
yield from self.propose_building_strikes()
|
||||
|
||||
def plan_missions(self) -> None:
|
||||
"""Identifies and plans mission for the turn."""
|
||||
player = "Blue" if self.is_player else "Red"
|
||||
@ -878,6 +840,11 @@ class CoalitionMissionPlanner:
|
||||
for proposed_mission in self.propose_missions():
|
||||
self.plan_mission(proposed_mission, tracer)
|
||||
|
||||
with logged_duration(f"{player} reserve mission planning"):
|
||||
with MultiEventTracer() as tracer:
|
||||
for critical_mission in self.critical_missions():
|
||||
self.plan_mission(critical_mission, tracer, reserves=True)
|
||||
|
||||
with logged_duration(f"{player} mission scheduling"):
|
||||
self.stagger_missions()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user