mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Use unplanned missions to guide aircraft purchase.
AI aircraft purchase decisions are now driven by the missions that the flight planner was unable to fulfill. This way we're buying the aircraft we actually need instead of buying them at random, in the locations we need them, in the order they're needed. There's a bit more that could be improved here: * Unused aircraft could be ferried to where they would be more useful. * Partial squadrons could be completed rather than buying a whole flight at a time. * Aircraft could be ranked by their usefulness so we're not buying so many Hueys when a Hornet would do better. * Purchase a buffer of CAP capable aircraft in case too many are shot down and they are not available next turn. https://github.com/Khopa/dcs_liberation/issues/361
This commit is contained in:
@@ -21,6 +21,7 @@ from dcs.unittype import FlyingType
|
||||
from game import db
|
||||
from game.data.radar_db import UNITS_WITH_RADAR
|
||||
from game.infos.information import Information
|
||||
from game.procurement import AircraftProcurementRequest
|
||||
from game.theater import (
|
||||
Airfield,
|
||||
ControlPoint,
|
||||
@@ -50,7 +51,7 @@ from gen.flights.ai_flight_planner_db import (
|
||||
SEAD_CAPABLE,
|
||||
SEAD_PREFERRED,
|
||||
STRIKE_CAPABLE,
|
||||
STRIKE_PREFERRED,
|
||||
STRIKE_PREFERRED, capable_aircraft_for_task, preferred_aircraft_for_task,
|
||||
)
|
||||
from gen.flights.closestairfields import (
|
||||
ClosestAirfields,
|
||||
@@ -142,63 +143,14 @@ class AircraftAllocator:
|
||||
responsible for returning them to the inventory.
|
||||
"""
|
||||
result = self.find_aircraft_of_type(
|
||||
flight, self.preferred_aircraft_for_task(flight.task)
|
||||
flight, preferred_aircraft_for_task(flight.task)
|
||||
)
|
||||
if result is not None:
|
||||
return result
|
||||
return self.find_aircraft_of_type(
|
||||
flight, self.capable_aircraft_for_task(flight.task)
|
||||
flight, capable_aircraft_for_task(flight.task)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def preferred_aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
|
||||
cap_missions = (FlightType.BARCAP, FlightType.TARCAP)
|
||||
if task in cap_missions:
|
||||
return CAP_PREFERRED
|
||||
elif task == FlightType.ANTISHIP:
|
||||
return ANTISHIP_PREFERRED
|
||||
elif task == FlightType.BAI:
|
||||
return CAS_CAPABLE
|
||||
elif task == FlightType.CAS:
|
||||
return CAS_PREFERRED
|
||||
elif task in (FlightType.DEAD, FlightType.SEAD):
|
||||
return SEAD_PREFERRED
|
||||
elif task == FlightType.OCA_AIRCRAFT:
|
||||
return CAS_PREFERRED
|
||||
elif task == FlightType.OCA_RUNWAY:
|
||||
return RUNWAY_ATTACK_PREFERRED
|
||||
elif task == FlightType.STRIKE:
|
||||
return STRIKE_PREFERRED
|
||||
elif task == FlightType.ESCORT:
|
||||
return CAP_PREFERRED
|
||||
else:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def capable_aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
|
||||
cap_missions = (FlightType.BARCAP, FlightType.TARCAP)
|
||||
if task in cap_missions:
|
||||
return CAP_CAPABLE
|
||||
elif task == FlightType.ANTISHIP:
|
||||
return ANTISHIP_CAPABLE
|
||||
elif task == FlightType.BAI:
|
||||
return CAS_CAPABLE
|
||||
elif task == FlightType.CAS:
|
||||
return CAS_CAPABLE
|
||||
elif task in (FlightType.DEAD, FlightType.SEAD):
|
||||
return SEAD_CAPABLE
|
||||
elif task == FlightType.OCA_AIRCRAFT:
|
||||
return CAS_CAPABLE
|
||||
elif task == FlightType.OCA_RUNWAY:
|
||||
return RUNWAY_ATTACK_CAPABLE
|
||||
elif task == FlightType.STRIKE:
|
||||
return STRIKE_CAPABLE
|
||||
elif task == FlightType.ESCORT:
|
||||
return CAP_CAPABLE
|
||||
else:
|
||||
logging.error(f"Unplannable flight type: {task}")
|
||||
return []
|
||||
|
||||
def find_aircraft_of_type(
|
||||
self, flight: ProposedFlight, types: List[Type[FlyingType]],
|
||||
) -> Optional[Tuple[ControlPoint, Type[FlyingType]]]:
|
||||
@@ -528,6 +480,7 @@ class CoalitionMissionPlanner:
|
||||
self.is_player = is_player
|
||||
self.objective_finder = ObjectiveFinder(self.game, self.is_player)
|
||||
self.ato = self.game.blue_ato if is_player else self.game.red_ato
|
||||
self.procurement_requests: List[AircraftProcurementRequest] = []
|
||||
|
||||
def propose_missions(self) -> Iterator[ProposedMission]:
|
||||
"""Identifies and iterates over potential mission in priority order."""
|
||||
@@ -620,6 +573,12 @@ class CoalitionMissionPlanner:
|
||||
for proposed_flight in mission.flights:
|
||||
if not builder.plan_flight(proposed_flight):
|
||||
missing_types.add(proposed_flight.task)
|
||||
self.procurement_requests.append(AircraftProcurementRequest(
|
||||
near=mission.location,
|
||||
range=proposed_flight.max_distance,
|
||||
task_capability=proposed_flight.task,
|
||||
number=proposed_flight.num_aircraft
|
||||
))
|
||||
|
||||
if missing_types:
|
||||
missing_types_str = ", ".join(
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import logging
|
||||
from typing import List, Type
|
||||
|
||||
from dcs.helicopters import (
|
||||
AH_1W,
|
||||
AH_64A,
|
||||
@@ -80,19 +83,22 @@ from dcs.planes import (
|
||||
WingLoong_I,
|
||||
I_16
|
||||
)
|
||||
from dcs.unittype import FlyingType
|
||||
|
||||
from gen.flights.flight import FlightType
|
||||
|
||||
# Interceptor are the aircraft prioritized for interception tasks
|
||||
# If none is available, the AI will use regular CAP-capable aircraft instead
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from pydcs_extensions.f22a.f22a import F_22A
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M, Rafale_B
|
||||
from pydcs_extensions.su57.su57 import Su_57
|
||||
|
||||
# TODO: These lists really ought to be era (faction) dependent.
|
||||
# Factions which have F-5s, F-86s, and A-4s will should prefer F-5s for CAP, but
|
||||
# factions that also have F-4s should not.
|
||||
from pydcs_extensions.su57.su57 import Su_57
|
||||
|
||||
# Interceptor are the aircraft prioritized for interception tasks
|
||||
# If none is available, the AI will use regular CAP-capable aircraft instead
|
||||
INTERCEPT_CAPABLE = [
|
||||
MiG_21Bis,
|
||||
MiG_25PD,
|
||||
@@ -528,4 +534,53 @@ DRONES = [
|
||||
MQ_9_Reaper,
|
||||
RQ_1A_Predator,
|
||||
WingLoong_I
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def preferred_aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
|
||||
cap_missions = (FlightType.BARCAP, FlightType.TARCAP)
|
||||
if task in cap_missions:
|
||||
return CAP_PREFERRED
|
||||
elif task == FlightType.ANTISHIP:
|
||||
return ANTISHIP_PREFERRED
|
||||
elif task == FlightType.BAI:
|
||||
return CAS_CAPABLE
|
||||
elif task == FlightType.CAS:
|
||||
return CAS_PREFERRED
|
||||
elif task in (FlightType.DEAD, FlightType.SEAD):
|
||||
return SEAD_PREFERRED
|
||||
elif task == FlightType.OCA_AIRCRAFT:
|
||||
return CAS_PREFERRED
|
||||
elif task == FlightType.OCA_RUNWAY:
|
||||
return RUNWAY_ATTACK_PREFERRED
|
||||
elif task == FlightType.STRIKE:
|
||||
return STRIKE_PREFERRED
|
||||
elif task == FlightType.ESCORT:
|
||||
return CAP_PREFERRED
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def capable_aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
|
||||
cap_missions = (FlightType.BARCAP, FlightType.TARCAP)
|
||||
if task in cap_missions:
|
||||
return CAP_CAPABLE
|
||||
elif task == FlightType.ANTISHIP:
|
||||
return ANTISHIP_CAPABLE
|
||||
elif task == FlightType.BAI:
|
||||
return CAS_CAPABLE
|
||||
elif task == FlightType.CAS:
|
||||
return CAS_CAPABLE
|
||||
elif task in (FlightType.DEAD, FlightType.SEAD):
|
||||
return SEAD_CAPABLE
|
||||
elif task == FlightType.OCA_AIRCRAFT:
|
||||
return CAS_CAPABLE
|
||||
elif task == FlightType.OCA_RUNWAY:
|
||||
return RUNWAY_ATTACK_CAPABLE
|
||||
elif task == FlightType.STRIKE:
|
||||
return STRIKE_CAPABLE
|
||||
elif task == FlightType.ESCORT:
|
||||
return CAP_CAPABLE
|
||||
else:
|
||||
logging.error(f"Unplannable flight type: {task}")
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user