Pick divert airfields when planning.

https://github.com/Khopa/dcs_liberation/issues/342
This commit is contained in:
Dan Albert 2020-11-20 17:29:58 -08:00
parent ae68a35a1a
commit a594f45aae
6 changed files with 47 additions and 29 deletions

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from collections import defaultdict from collections import defaultdict
from typing import Dict, Iterable, Iterator, Set, Tuple, TYPE_CHECKING from typing import Dict, Iterable, Iterator, Set, Tuple, TYPE_CHECKING
from dcs.unittype import UnitType from dcs.unittype import FlyingType
from gen.flights.flight import Flight from gen.flights.flight import Flight
@ -17,9 +17,9 @@ class ControlPointAircraftInventory:
def __init__(self, control_point: ControlPoint) -> None: def __init__(self, control_point: ControlPoint) -> None:
self.control_point = control_point self.control_point = control_point
self.inventory: Dict[UnitType, int] = defaultdict(int) self.inventory: Dict[FlyingType, int] = defaultdict(int)
def add_aircraft(self, aircraft: UnitType, count: int) -> None: def add_aircraft(self, aircraft: FlyingType, count: int) -> None:
"""Adds aircraft to the inventory. """Adds aircraft to the inventory.
Args: Args:
@ -28,7 +28,7 @@ class ControlPointAircraftInventory:
""" """
self.inventory[aircraft] += count self.inventory[aircraft] += count
def remove_aircraft(self, aircraft: UnitType, count: int) -> None: def remove_aircraft(self, aircraft: FlyingType, count: int) -> None:
"""Removes aircraft from the inventory. """Removes aircraft from the inventory.
Args: Args:
@ -47,7 +47,7 @@ class ControlPointAircraftInventory:
) )
self.inventory[aircraft] -= count self.inventory[aircraft] -= count
def available(self, aircraft: UnitType) -> int: def available(self, aircraft: FlyingType) -> int:
"""Returns the number of available aircraft of the given type. """Returns the number of available aircraft of the given type.
Args: Args:
@ -59,14 +59,14 @@ class ControlPointAircraftInventory:
return 0 return 0
@property @property
def types_available(self) -> Iterator[UnitType]: def types_available(self) -> Iterator[FlyingType]:
"""Iterates over all available aircraft types.""" """Iterates over all available aircraft types."""
for aircraft, count in self.inventory.items(): for aircraft, count in self.inventory.items():
if count > 0: if count > 0:
yield aircraft yield aircraft
@property @property
def all_aircraft(self) -> Iterator[Tuple[UnitType, int]]: def all_aircraft(self) -> Iterator[Tuple[FlyingType, int]]:
"""Iterates over all available aircraft types, including amounts.""" """Iterates over all available aircraft types, including amounts."""
for aircraft, count in self.inventory.items(): for aircraft, count in self.inventory.items():
if count > 0: if count > 0:
@ -106,9 +106,9 @@ class GlobalAircraftInventory:
return self.inventories[control_point] return self.inventories[control_point]
@property @property
def available_types_for_player(self) -> Iterator[UnitType]: def available_types_for_player(self) -> Iterator[FlyingType]:
"""Iterates over all aircraft types available to the player.""" """Iterates over all aircraft types available to the player."""
seen: Set[UnitType] = set() seen: Set[FlyingType] = set()
for control_point, inventory in self.inventories.items(): for control_point, inventory in self.inventories.items():
if control_point.captured: if control_point.captured:
for aircraft in inventory.types_available: for aircraft in inventory.types_available:

View File

@ -16,6 +16,7 @@ from dcs.ships import (
Type_071_Amphibious_Transport_Dock, Type_071_Amphibious_Transport_Dock,
) )
from dcs.terrain.terrain import Airport from dcs.terrain.terrain import Airport
from dcs.unittype import FlyingType
from game import db from game import db
from gen.ground_forces.combat_stance import CombatStance from gen.ground_forces.combat_stance import CombatStance
@ -366,6 +367,13 @@ class ControlPoint(MissionTarget):
# TODO: FlightType.STRIKE # TODO: FlightType.STRIKE
] ]
def can_land(self, aircraft: FlyingType) -> bool:
if self.is_carrier and aircraft not in db.CARRIER_CAPABLE:
return False
if self.is_lha and aircraft not in db.LHA_CAPABLE:
return False
return True
class OffMapSpawn(ControlPoint): class OffMapSpawn(ControlPoint):
def __init__(self, id: int, name: str, position: Point): def __init__(self, id: int, name: str, position: Point):

View File

@ -16,7 +16,7 @@ from typing import (
Type, Type,
) )
from dcs.unittype import FlyingType, UnitType from dcs.unittype import FlyingType
from game import db from game import db
from game.data.radar_db import UNITS_WITH_RADAR from game.data.radar_db import UNITS_WITH_RADAR
@ -119,7 +119,7 @@ class AircraftAllocator:
def find_aircraft_for_flight( def find_aircraft_for_flight(
self, flight: ProposedFlight self, flight: ProposedFlight
) -> Optional[Tuple[ControlPoint, UnitType]]: ) -> Optional[Tuple[ControlPoint, FlyingType]]:
"""Finds aircraft suitable for the given mission. """Finds aircraft suitable for the given mission.
Searches for aircraft capable of performing the given mission within the Searches for aircraft capable of performing the given mission within the
@ -190,7 +190,7 @@ class AircraftAllocator:
def find_aircraft_of_type( def find_aircraft_of_type(
self, flight: ProposedFlight, types: List[Type[FlyingType]], self, flight: ProposedFlight, types: List[Type[FlyingType]],
) -> Optional[Tuple[ControlPoint, UnitType]]: ) -> Optional[Tuple[ControlPoint, FlyingType]]:
airfields_in_range = self.closest_airfields.airfields_within( airfields_in_range = self.closest_airfields.airfields_within(
flight.max_distance flight.max_distance
) )
@ -214,6 +214,8 @@ class PackageBuilder:
global_inventory: GlobalAircraftInventory, global_inventory: GlobalAircraftInventory,
is_player: bool, is_player: bool,
start_type: str) -> None: start_type: str) -> None:
self.closest_airfields = closest_airfields
self.is_player = is_player
self.package = Package(location) self.package = Package(location)
self.allocator = AircraftAllocator(closest_airfields, global_inventory, self.allocator = AircraftAllocator(closest_airfields, global_inventory,
is_player) is_player)
@ -239,10 +241,25 @@ class PackageBuilder:
flight = Flight(self.package, aircraft, plan.num_aircraft, plan.task, flight = Flight(self.package, aircraft, plan.num_aircraft, plan.task,
start_type, departure=airfield, arrival=airfield, start_type, departure=airfield, arrival=airfield,
divert=None) divert=self.find_divert_field(aircraft, airfield))
self.package.add_flight(flight) self.package.add_flight(flight)
return True return True
def find_divert_field(self, aircraft: FlyingType,
arrival: ControlPoint) -> Optional[ControlPoint]:
divert_limit = nm_to_meter(150)
for airfield in self.closest_airfields.airfields_within(divert_limit):
if airfield.captured != self.is_player:
continue
if airfield == arrival:
continue
if not airfield.can_land(aircraft):
continue
if isinstance(airfield, OffMapSpawn):
continue
return airfield
return None
def build(self) -> Package: def build(self) -> Package:
"""Returns the built package.""" """Returns the built package."""
return self.package return self.package

View File

@ -3,13 +3,13 @@ from typing import Iterable
from PySide2.QtWidgets import QComboBox from PySide2.QtWidgets import QComboBox
from dcs.planes import PlaneType from dcs.unittype import FlyingType
class QAircraftTypeSelector(QComboBox): class QAircraftTypeSelector(QComboBox):
"""Combo box for selecting among the given aircraft types.""" """Combo box for selecting among the given aircraft types."""
def __init__(self, aircraft_types: Iterable[PlaneType]) -> None: def __init__(self, aircraft_types: Iterable[FlyingType]) -> None:
super().__init__() super().__init__()
for aircraft in aircraft_types: for aircraft in aircraft_types:
self.addItem(f"{aircraft.id}", userData=aircraft) self.addItem(f"{aircraft.id}", userData=aircraft)

View File

@ -2,7 +2,7 @@
from typing import Iterable from typing import Iterable
from PySide2.QtWidgets import QComboBox from PySide2.QtWidgets import QComboBox
from dcs.planes import PlaneType from dcs.unittype import FlyingType
from game import db from game import db
from game.theater.controlpoint import ControlPoint from game.theater.controlpoint import ControlPoint
@ -16,7 +16,7 @@ class QArrivalAirfieldSelector(QComboBox):
""" """
def __init__(self, destinations: Iterable[ControlPoint], def __init__(self, destinations: Iterable[ControlPoint],
aircraft: PlaneType, optional_text: str) -> None: aircraft: FlyingType, optional_text: str) -> None:
super().__init__() super().__init__()
self.destinations = list(destinations) self.destinations = list(destinations)
self.aircraft = aircraft self.aircraft = aircraft
@ -24,23 +24,16 @@ class QArrivalAirfieldSelector(QComboBox):
self.rebuild_selector() self.rebuild_selector()
self.setCurrentIndex(0) self.setCurrentIndex(0)
def change_aircraft(self, aircraft: PlaneType) -> None: def change_aircraft(self, aircraft: FlyingType) -> None:
if self.aircraft == aircraft: if self.aircraft == aircraft:
return return
self.aircraft = aircraft self.aircraft = aircraft
self.rebuild_selector() self.rebuild_selector()
def valid_destination(self, destination: ControlPoint) -> bool:
if destination.is_carrier and self.aircraft not in db.CARRIER_CAPABLE:
return False
if destination.is_lha and self.aircraft not in db.LHA_CAPABLE:
return False
return True
def rebuild_selector(self) -> None: def rebuild_selector(self) -> None:
self.clear() self.clear()
for destination in self.destinations: for destination in self.destinations:
if self.valid_destination(destination): if destination.can_land(self.aircraft):
self.addItem(destination.name, destination) self.addItem(destination.name, destination)
self.model().sort(0) self.model().sort(0)
self.insertItem(0, self.optional_text, None) self.insertItem(0, self.optional_text, None)

View File

@ -3,7 +3,7 @@ from typing import Iterable
from PySide2.QtCore import Signal from PySide2.QtCore import Signal
from PySide2.QtWidgets import QComboBox from PySide2.QtWidgets import QComboBox
from dcs.planes import PlaneType from dcs.unittype import FlyingType
from game.inventory import GlobalAircraftInventory from game.inventory import GlobalAircraftInventory
from game.theater.controlpoint import ControlPoint from game.theater.controlpoint import ControlPoint
@ -20,7 +20,7 @@ class QOriginAirfieldSelector(QComboBox):
def __init__(self, global_inventory: GlobalAircraftInventory, def __init__(self, global_inventory: GlobalAircraftInventory,
origins: Iterable[ControlPoint], origins: Iterable[ControlPoint],
aircraft: PlaneType) -> None: aircraft: FlyingType) -> None:
super().__init__() super().__init__()
self.global_inventory = global_inventory self.global_inventory = global_inventory
self.origins = list(origins) self.origins = list(origins)
@ -28,7 +28,7 @@ class QOriginAirfieldSelector(QComboBox):
self.rebuild_selector() self.rebuild_selector()
self.currentIndexChanged.connect(self.index_changed) self.currentIndexChanged.connect(self.index_changed)
def change_aircraft(self, aircraft: PlaneType) -> None: def change_aircraft(self, aircraft: FlyingType) -> None:
if self.aircraft == aircraft: if self.aircraft == aircraft:
return return
self.aircraft = aircraft self.aircraft = aircraft