Initial implementation of AEW&C missions.

Still a work in progress (the missions don't actually perform their task, just orbit). Currently:

* AEW&C aircraft can be bought.
* AEW&C missions can be planned at any control point and at front lines.
* AEW&C will return after 4H or Bingo.
This commit is contained in:
Simon Krüger 2021-02-06 02:18:50 +01:00 committed by GitHub
parent 07ce20fd29
commit a004f62fe8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 213 additions and 43 deletions

View File

@ -4,6 +4,8 @@ Saves from 2.4 are not compatible with 2.5.
## Features/Improvements ## Features/Improvements
* **[Flight Planner]** (WIP) Added AEW&C missions.
## Fixes ## Fixes
# 2.4.0 # 2.4.0

View File

@ -1117,7 +1117,8 @@ COMMON_OVERRIDE = {
GroundAttack: "STRIKE", GroundAttack: "STRIKE",
Escort: "CAP", Escort: "CAP",
RunwayAttack: "RUNWAY_ATTACK", RunwayAttack: "RUNWAY_ATTACK",
FighterSweep: "CAP" FighterSweep: "CAP",
AWACS: "AEW&C",
} }
""" """
@ -1328,6 +1329,7 @@ CARRIER_CAPABLE = [
A_4E_C, A_4E_C,
Rafale_M, Rafale_M,
S_3B, S_3B,
E_2C,
UH_1H, UH_1H,
Mi_8MT, Mi_8MT,

View File

@ -4,7 +4,7 @@ import math
import typing import typing
from typing import Dict, Type from typing import Dict, Type
from dcs.task import CAP, CAS, Embarking, PinpointStrike, Task from dcs.task import AWACS, CAP, CAS, Embarking, PinpointStrike, Task
from dcs.unittype import FlyingType, UnitType, VehicleType from dcs.unittype import FlyingType, UnitType, VehicleType
from dcs.vehicles import AirDefence, Armor from dcs.vehicles import AirDefence, Armor
@ -122,7 +122,7 @@ class Base:
for_task = db.unit_task(unit_type) for_task = db.unit_task(unit_type)
target_dict = None target_dict = None
if for_task == CAS or for_task == CAP or for_task == Embarking: if for_task == AWACS or for_task == CAS or for_task == CAP or for_task == Embarking:
target_dict = self.aircraft target_dict = self.aircraft
elif for_task == PinpointStrike: elif for_task == PinpointStrike:
target_dict = self.armor target_dict = self.armor

View File

@ -812,6 +812,7 @@ class FrontLine(MissionTarget):
def mission_types(self, for_player: bool) -> Iterator[FlightType]: def mission_types(self, for_player: bool) -> Iterator[FlightType]:
yield from [ yield from [
FlightType.CAS, FlightType.CAS,
FlightType.AEWC,
# TODO: FlightType.TROOP_TRANSPORT # TODO: FlightType.TROOP_TRANSPORT
# TODO: FlightType.EVAC # TODO: FlightType.EVAC
] ]

View File

@ -603,6 +603,14 @@ class ControlPoint(MissionTarget, ABC):
def income_per_turn(self) -> int: def income_per_turn(self) -> int:
return 0 return 0
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if self.is_friendly(for_player):
yield from [
FlightType.AEWC,
]
yield from super().mission_types(for_player)
class Airfield(ControlPoint): class Airfield(ControlPoint):

View File

@ -41,6 +41,7 @@ from dcs.planes import (
) )
from dcs.point import MovingPoint, PointAction from dcs.point import MovingPoint, PointAction
from dcs.task import ( from dcs.task import (
AWACS,
AntishipStrike, AntishipStrike,
AttackGroup, AttackGroup,
Bombing, Bombing,
@ -90,7 +91,6 @@ from game.utils import Distance, meters, nautical_miles
from gen.airsupportgen import AirSupport from gen.airsupportgen import AirSupport
from gen.ato import AirTaskingOrder, Package from gen.ato import AirTaskingOrder, Package
from gen.callsigns import create_group_callsign_from_unit from gen.callsigns import create_group_callsign_from_unit
from gen.conflictgen import FRONTLINE_LENGTH
from gen.flights.flight import ( from gen.flights.flight import (
Flight, Flight,
FlightType, FlightType,
@ -129,10 +129,11 @@ HELICOPTER_CHANNEL = MHz(127)
UHF_FALLBACK_CHANNEL = MHz(251) UHF_FALLBACK_CHANNEL = MHz(251)
TARGET_WAYPOINTS = ( TARGET_WAYPOINTS = (
FlightWaypointType.TARGET_GROUP_LOC, FlightWaypointType.TARGET_GROUP_LOC,
FlightWaypointType.TARGET_POINT, FlightWaypointType.TARGET_POINT,
FlightWaypointType.TARGET_SHIP, FlightWaypointType.TARGET_SHIP,
) )
# TODO: Get radio information for all the special cases. # TODO: Get radio information for all the special cases.
def get_fallback_channel(unit_type: UnitType) -> RadioFrequency: def get_fallback_channel(unit_type: UnitType) -> RadioFrequency:
@ -731,11 +732,14 @@ class AircraftConflictGenerator:
group.load_loadout(payload_name) group.load_loadout(payload_name)
if not group.units[0].pylons and for_task == RunwayAttack: if not group.units[0].pylons and for_task == RunwayAttack:
if PinpointStrike in db.PLANE_PAYLOAD_OVERRIDES[unit_type]: if PinpointStrike in db.PLANE_PAYLOAD_OVERRIDES[unit_type]:
logging.warning("No loadout for \"Runway Attack\" for the {}, defaulting to Strike loadout".format(str(unit_type))) logging.warning(
"No loadout for \"Runway Attack\" for the {}, defaulting to Strike loadout".format(
str(unit_type)))
payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][PinpointStrike] payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][PinpointStrike]
group.load_loadout(payload_name) group.load_loadout(payload_name)
did_load_loadout = True did_load_loadout = True
logging.info("Loaded overridden payload for {} - {} for task {}".format(unit_type, payload_name, for_task)) logging.info(
"Loaded overridden payload for {} - {} for task {}".format(unit_type, payload_name, for_task))
if not did_load_loadout: if not did_load_loadout:
group.load_task_default_loadout(for_task) group.load_task_default_loadout(for_task)
@ -995,7 +999,7 @@ class AircraftConflictGenerator:
group = self._generate_at_airport( group = self._generate_at_airport(
name=namegen.next_aircraft_name(country, control_point.id, name=namegen.next_aircraft_name(country, control_point.id,
flight), flight),
side=country, side=country,
unit_type=aircraft, unit_type=aircraft,
count=1, count=1,
@ -1058,7 +1062,7 @@ class AircraftConflictGenerator:
trigger.add_condition( trigger.add_condition(
CoalitionHasAirdrome(coalition, flight.from_cp.id)) CoalitionHasAirdrome(coalition, flight.from_cp.id))
def generate_planned_flight(self, cp, country, flight:Flight): def generate_planned_flight(self, cp, country, flight: Flight):
name = namegen.next_aircraft_name(country, cp.id, flight) name = namegen.next_aircraft_name(country, cp.id, flight)
try: try:
if flight.start_type == "In Flight": if flight.start_type == "In Flight":
@ -1249,6 +1253,17 @@ class AircraftConflictGenerator:
roe=OptROE.Values.OpenFire, roe=OptROE.Values.OpenFire,
restrict_jettison=True) restrict_jettison=True)
def configure_awacs(
self, group: FlyingGroup, package: Package, flight: Flight,
dynamic_runways: Dict[str, RunwayData]) -> None:
group.task = AWACS.name
self._setup_group(group, AWACS, package, flight, dynamic_runways)
self.configure_behavior(
group,
react_on_threat=OptReactOnThreat.Values.EvadeFire,
roe=OptROE.Values.WeaponHold,
restrict_jettison=True)
def configure_escort(self, group: FlyingGroup, package: Package, def configure_escort(self, group: FlyingGroup, package: Package,
flight: Flight, flight: Flight,
dynamic_runways: Dict[str, RunwayData]) -> None: dynamic_runways: Dict[str, RunwayData]) -> None:
@ -1274,6 +1289,8 @@ class AircraftConflictGenerator:
self.configure_cap(group, package, flight, dynamic_runways) self.configure_cap(group, package, flight, dynamic_runways)
elif flight_type == FlightType.SWEEP: elif flight_type == FlightType.SWEEP:
self.configure_sweep(group, package, flight, dynamic_runways) self.configure_sweep(group, package, flight, dynamic_runways)
elif flight_type == FlightType.AEWC:
self.configure_awacs(group, package, flight, dynamic_runways)
elif flight_type in [FlightType.CAS, FlightType.BAI]: elif flight_type in [FlightType.CAS, FlightType.BAI]:
self.configure_cas(group, package, flight, dynamic_runways) self.configure_cas(group, package, flight, dynamic_runways)
elif flight_type == FlightType.DEAD: elif flight_type == FlightType.DEAD:
@ -1326,8 +1343,8 @@ class AircraftConflictGenerator:
filtered_points = [ filtered_points = [
point for idx, point in enumerate(filtered_points) if ( point for idx, point in enumerate(filtered_points) if (
point.waypoint_type not in TARGET_WAYPOINTS or idx == keep_target[0] point.waypoint_type not in TARGET_WAYPOINTS or idx == keep_target[0]
) )
] ]
for idx, point in enumerate(filtered_points): for idx, point in enumerate(filtered_points):
PydcsWaypointBuilder.for_waypoint( PydcsWaypointBuilder.for_waypoint(
@ -1458,8 +1475,8 @@ class PydcsWaypointBuilder:
If the flight is a player controlled Viggen flight, no TOT should be set on any waypoint except actual target waypoints. If the flight is a player controlled Viggen flight, no TOT should be set on any waypoint except actual target waypoints.
""" """
if ( if (
(self.flight.client_count > 0 and self.flight.unit_type == AJS37) and (self.flight.client_count > 0 and self.flight.unit_type == AJS37) and
(self.waypoint.waypoint_type not in TARGET_WAYPOINTS) (self.waypoint.waypoint_type not in TARGET_WAYPOINTS)
): ):
return True return True
else: else:
@ -1622,12 +1639,12 @@ class SeadIngressBuilder(PydcsWaypointBuilder):
tgroup = self.mission.find_group(target_group.group_name) tgroup = self.mission.find_group(target_group.group_name)
if tgroup is not None: if tgroup is not None:
waypoint.add_task(EngageTargetsInZone( waypoint.add_task(EngageTargetsInZone(
position=tgroup.position, position=tgroup.position,
radius=int(nautical_miles(30).meters), radius=int(nautical_miles(30).meters),
targets=[ targets=[
Targets.All.GroundUnits.AirDefence, Targets.All.GroundUnits.AirDefence,
]) ])
) )
else: else:
logging.error(f"Could not find group for DEAD mission {target_group.group_name}") logging.error(f"Could not find group for DEAD mission {target_group.group_name}")
self.register_special_waypoints(self.waypoint.targets) self.register_special_waypoints(self.waypoint.targets)

View File

@ -174,6 +174,7 @@ class Package:
FlightType.TARCAP, FlightType.TARCAP,
FlightType.BARCAP, FlightType.BARCAP,
FlightType.SWEEP, FlightType.SWEEP,
FlightType.AEWC,
FlightType.ESCORT, FlightType.ESCORT,
] ]
for task in task_priorities: for task in task_priorities:

View File

@ -12,8 +12,8 @@ from dcs.helicopters import (
OH_58D, OH_58D,
SA342L, SA342L,
SA342M, SA342M,
SH_60B,
UH_1H, UH_1H,
SH_60B
) )
from dcs.planes import ( from dcs.planes import (
AJS37, AJS37,
@ -22,11 +22,14 @@ from dcs.planes import (
A_10C, A_10C,
A_10C_2, A_10C_2,
A_20G, A_20G,
A_50,
B_17G, B_17G,
B_1B, B_1B,
B_52H, B_52H,
Bf_109K_4, Bf_109K_4,
C_101CC, C_101CC,
E_2C,
E_3A,
FA_18C_hornet, FA_18C_hornet,
FW_190A8, FW_190A8,
FW_190D9, FW_190D9,
@ -40,9 +43,11 @@ from dcs.planes import (
F_4E, F_4E,
F_5E_3, F_5E_3,
F_86F_Sabre, F_86F_Sabre,
I_16,
JF_17, JF_17,
J_11A, J_11A,
Ju_88A4, Ju_88A4,
KJ_2000,
L_39ZA, L_39ZA,
MQ_9_Reaper, MQ_9_Reaper,
M_2000C, M_2000C,
@ -83,18 +88,16 @@ from dcs.planes import (
Tu_22M3, Tu_22M3,
Tu_95MS, Tu_95MS,
WingLoong_I, WingLoong_I,
I_16
) )
from dcs.unittype import FlyingType from dcs.unittype import FlyingType
from gen.flights.flight import FlightType from gen.flights.flight import FlightType
from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.f22a.f22a import F_22A 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
from pydcs_extensions.hercules.hercules import Hercules from pydcs_extensions.hercules.hercules import Hercules
from pydcs_extensions.mb339.mb339 import MB_339PAN
from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_B, Rafale_M
from pydcs_extensions.su57.su57 import Su_57
# All aircraft lists are in priority order. Aircraft higher in the list will be # All aircraft lists are in priority order. Aircraft higher in the list will be
# preferred over those lower in the list. # preferred over those lower in the list.
@ -155,8 +158,8 @@ CAP_CAPABLE = [
# Used for CAS (Close air support) and BAI (Battlefield Interdiction) # Used for CAS (Close air support) and BAI (Battlefield Interdiction)
CAS_CAPABLE = [ CAS_CAPABLE = [
A_10C_2, A_10C_2,
A_10C,
B_1B, B_1B,
A_10C,
F_14B, F_14B,
F_14A_135_GR, F_14A_135_GR,
Su_25TM, Su_25TM,
@ -373,6 +376,13 @@ DRONES = [
WingLoong_I WingLoong_I
] ]
AEWC_CAPABLE = [
E_3A,
E_2C,
A_50,
KJ_2000,
]
def aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]: def aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
cap_missions = (FlightType.BARCAP, FlightType.TARCAP) cap_missions = (FlightType.BARCAP, FlightType.TARCAP)
@ -396,6 +406,8 @@ def aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
return STRIKE_CAPABLE return STRIKE_CAPABLE
elif task == FlightType.ESCORT: elif task == FlightType.ESCORT:
return CAP_CAPABLE return CAP_CAPABLE
elif task == FlightType.AEWC:
return AEWC_CAPABLE
else: else:
logging.error(f"Unplannable flight type: {task}") logging.error(f"Unplannable flight type: {task}")
return [] return []

View File

@ -20,6 +20,14 @@ if TYPE_CHECKING:
class FlightType(Enum): class FlightType(Enum):
"""Enumeration of mission types.
The value of each enumeration is the name that will be shown in the UI.
These values are persisted to the save game as well since they are a part of
each flight and thus a part of the ATO, so changing these values will break
save compat.
"""
TARCAP = "TARCAP" TARCAP = "TARCAP"
BARCAP = "BARCAP" BARCAP = "BARCAP"
CAS = "CAS" CAS = "CAS"
@ -33,6 +41,7 @@ class FlightType(Enum):
SWEEP = "Fighter sweep" SWEEP = "Fighter sweep"
OCA_RUNWAY = "OCA/Runway" OCA_RUNWAY = "OCA/Runway"
OCA_AIRCRAFT = "OCA/Aircraft" OCA_AIRCRAFT = "OCA/Aircraft"
AEWC = "AEW&C"
def __str__(self) -> str: def __str__(self) -> str:
return self.value return self.value

View File

@ -15,6 +15,13 @@ from datetime import timedelta
from functools import cached_property from functools import cached_property
from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple
from dcs.planes import (
E_3A,
E_2C,
A_50,
KJ_2000
)
from dcs.mapping import Point from dcs.mapping import Point
from dcs.unit import Unit from dcs.unit import Unit
from shapely.geometry import Point as ShapelyPoint from shapely.geometry import Point as ShapelyPoint
@ -29,7 +36,7 @@ from game.theater import (
TheaterGroundObject, TheaterGroundObject,
) )
from game.theater.theatergroundobject import EwrGroundObject from game.theater.theatergroundobject import EwrGroundObject
from game.utils import Distance, Speed, meters, nautical_miles from game.utils import Distance, Speed, feet, meters, nautical_miles
from .closestairfields import ObjectiveDistanceCache from .closestairfields import ObjectiveDistanceCache
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
from .traveltime import GroundSpeed, TravelTime from .traveltime import GroundSpeed, TravelTime
@ -121,7 +128,7 @@ class FlightPlan:
failed to generate. Nevertheless, we have to defend against it. failed to generate. Nevertheless, we have to defend against it.
""" """
raise NotImplementedError raise NotImplementedError
@cached_property @cached_property
def bingo_fuel(self) -> int: def bingo_fuel(self) -> int:
"""Bingo fuel value for the FlightPlan """Bingo fuel value for the FlightPlan
@ -145,7 +152,7 @@ class FlightPlan:
"""Joker fuel value for the FlightPlan """Joker fuel value for the FlightPlan
""" """
return self.bingo_fuel + 1000 return self.bingo_fuel + 1000
def max_distance_from(self, cp: ControlPoint) -> Distance: def max_distance_from(self, cp: ControlPoint) -> Distance:
"""Returns the farthest waypoint of the flight plan from a ControlPoint. """Returns the farthest waypoint of the flight plan from a ControlPoint.
:arg cp The ControlPoint to measure distance from. :arg cp The ControlPoint to measure distance from.
@ -280,11 +287,11 @@ class LoiterFlightPlan(FlightPlan):
travel_time = super().travel_time_between_waypoints(a, b) travel_time = super().travel_time_between_waypoints(a, b)
if a != self.hold: if a != self.hold:
return travel_time return travel_time
try: return travel_time + self.hold_duration
return travel_time + self.hold_duration
except AttributeError: @property
# Save compat for 2.3. def mission_departure_time(self) -> timedelta:
return travel_time + timedelta(minutes=5) raise NotImplementedError
@dataclass(frozen=True) @dataclass(frozen=True)
@ -542,10 +549,10 @@ class StrikeFlightPlan(FormationFlightPlan):
@property @property
def package_speed_waypoints(self) -> Set[FlightWaypoint]: def package_speed_waypoints(self) -> Set[FlightWaypoint]:
return { return {
self.ingress, self.ingress,
self.egress, self.egress,
self.split, self.split,
} | set(self.targets) } | set(self.targets)
def speed_between_waypoints(self, a: FlightWaypoint, def speed_between_waypoints(self, a: FlightWaypoint,
b: FlightWaypoint) -> Speed: b: FlightWaypoint) -> Speed:
@ -696,6 +703,41 @@ class SweepFlightPlan(LoiterFlightPlan):
return self.sweep_end_time return self.sweep_end_time
@dataclass(frozen=True)
class AwacsFlightPlan(LoiterFlightPlan):
takeoff: FlightWaypoint
nav_to: List[FlightWaypoint]
nav_from: List[FlightWaypoint]
land: FlightWaypoint
divert: Optional[FlightWaypoint]
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
yield from self.nav_to
yield self.hold
yield from self.nav_from
yield self.land
if self.divert is not None:
yield self.divert
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
if waypoint == self.hold:
return self.package.time_over_target
return None
@property
def tot_waypoint(self) -> Optional[FlightWaypoint]:
return self.hold
@property
def push_time(self) -> timedelta:
return self.package.time_over_target + self.hold_duration
@property
def mission_departure_time(self) -> timedelta:
return self.push_time
@dataclass(frozen=True) @dataclass(frozen=True)
class CustomFlightPlan(FlightPlan): class CustomFlightPlan(FlightPlan):
custom_waypoints: List[FlightWaypoint] custom_waypoints: List[FlightWaypoint]
@ -791,6 +833,8 @@ class FlightPlanBuilder:
return self.generate_sweep(flight) return self.generate_sweep(flight)
elif task == FlightType.TARCAP: elif task == FlightType.TARCAP:
return self.generate_tarcap(flight) return self.generate_tarcap(flight)
elif task == FlightType.AEWC:
return self.generate_aewc(flight)
raise PlanningError( raise PlanningError(
f"{task} flight plan generation not implemented") f"{task} flight plan generation not implemented")
@ -921,6 +965,45 @@ class FlightPlanBuilder:
FlightWaypointType.INGRESS_STRIKE, FlightWaypointType.INGRESS_STRIKE,
targets) targets)
def generate_aewc(self, flight: Flight) -> AwacsFlightPlan:
"""Generate a AWACS flight at a given location.
Args:
flight: The flight to generate the flight plan for.
"""
location = self.package.target
start = self.aewc_orbit(location)
# As high as possible to maximize detection and on-station time.
if flight.unit_type == E_2C:
patrol_alt = feet(30000)
elif flight.unit_type == E_3A:
patrol_alt = feet(35000)
elif flight.unit_type == A_50:
patrol_alt = feet(33000)
elif flight.unit_type == KJ_2000:
patrol_alt = feet(40000)
else:
patrol_alt = feet(25000)
builder = WaypointBuilder(flight, self.game, self.is_player)
start = builder.orbit(start, patrol_alt)
return AwacsFlightPlan(
package=self.package,
flight=flight,
takeoff=builder.takeoff(flight.departure),
nav_to=builder.nav_path(flight.departure.position, start.position,
patrol_alt),
nav_from=builder.nav_path(start.position, flight.arrival.position,
patrol_alt),
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
hold=start,
hold_duration=timedelta(hours=4),
)
def generate_bai(self, flight: Flight) -> StrikeFlightPlan: def generate_bai(self, flight: Flight) -> StrikeFlightPlan:
"""Generates a BAI flight plan. """Generates a BAI flight plan.
@ -1095,6 +1178,18 @@ class FlightPlanBuilder:
start = end.point_from_heading(heading - 180, diameter) start = end.point_from_heading(heading - 180, diameter)
return start, end return start, end
@staticmethod
def aewc_orbit(location: MissionTarget) -> Point:
closest_airfield = location
# TODO: This is a heading to itself.
# Place this either over the target or as close as possible outside the
# threat zone: https://github.com/Khopa/dcs_liberation/issues/842.
heading = location.position.heading_between_point(closest_airfield.position)
return location.position.point_from_heading(
heading,
5000
)
def racetrack_for_frontline(self, origin: Point, def racetrack_for_frontline(self, origin: Point,
front_line: FrontLine) -> Tuple[Point, Point]: front_line: FrontLine) -> Tuple[Point, Point]:
ally_cp, enemy_cp = front_line.control_points ally_cp, enemy_cp = front_line.control_points

View File

@ -366,6 +366,26 @@ class WaypointBuilder:
return (self.race_track_start(start, altitude), return (self.race_track_start(start, altitude),
self.race_track_end(end, altitude)) self.race_track_end(end, altitude))
@staticmethod
def orbit(start: Point, altitude: Distance) -> FlightWaypoint:
"""Creates an circular orbit point.
Args:
start: Position of the waypoint.
altitude: Altitude of the racetrack.
"""
waypoint = FlightWaypoint(
FlightWaypointType.LOITER,
start.x,
start.y,
altitude
)
waypoint.name = "ORBIT"
waypoint.description = "Anchor and hold at this point"
waypoint.pretty_name = "Orbit"
return waypoint
@staticmethod @staticmethod
def sweep_start(position: Point, altitude: Distance) -> FlightWaypoint: def sweep_start(position: Point, altitude: Distance) -> FlightWaypoint:
"""Creates a sweep start waypoint. """Creates a sweep start waypoint.

View File

@ -47,6 +47,9 @@ class QAircraftTypeSelector(QComboBox):
elif mission_type in [FlightType.OCA_RUNWAY]: elif mission_type in [FlightType.OCA_RUNWAY]:
if aircraft in gen.flights.ai_flight_planner_db.RUNWAY_ATTACK_CAPABLE: if aircraft in gen.flights.ai_flight_planner_db.RUNWAY_ATTACK_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
elif mission_type in [FlightType.AEWC]:
if aircraft in gen.flights.ai_flight_planner_db.AEWC_CAPABLE:
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
current_aircraft_index = self.findData(current_aircraft) current_aircraft_index = self.findData(current_aircraft)
if current_aircraft_index != -1: if current_aircraft_index != -1:
self.setCurrentIndex(current_aircraft_index) self.setCurrentIndex(current_aircraft_index)

View File

@ -12,7 +12,7 @@ from PySide2.QtWidgets import (
QVBoxLayout, QVBoxLayout,
QWidget, QWidget,
) )
from dcs.task import CAP, CAS from dcs.task import CAP, CAS, AWACS
from dcs.unittype import FlyingType, UnitType from dcs.unittype import FlyingType, UnitType
from game import db from game import db
@ -45,7 +45,7 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
def init_ui(self): def init_ui(self):
main_layout = QVBoxLayout() main_layout = QVBoxLayout()
tasks = [CAP, CAS] tasks = [CAP, CAS, AWACS]
scroll_content = QWidget() scroll_content = QWidget()
task_box_layout = QGridLayout() task_box_layout = QGridLayout()