mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Initial support for Armed Recon flight plan
This commit is contained in:
parent
c24fba0ba4
commit
f405ffdfe2
@ -25,6 +25,7 @@
|
||||
* **[Modding]** Added support for Su-15 Flagon mod (v1.0)
|
||||
* **[Plugins]** Support for Carsten's Arty Spotter script
|
||||
* **[Modding]** Added support for SK-60 mod (v1.2.1)
|
||||
* **[Mission Generation]** Introducing the Armed Recon flight plan, i.e. CAS against any Theater Ground Object
|
||||
|
||||
## Fixes
|
||||
* **[UI/UX]** A-10A flights can be edited again
|
||||
|
||||
34
game/ato/flightplans/armedrecon.py
Normal file
34
game/ato/flightplans/armedrecon.py
Normal file
@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Type
|
||||
|
||||
from .formationattack import (
|
||||
FormationAttackBuilder,
|
||||
FormationAttackFlightPlan,
|
||||
FormationAttackLayout,
|
||||
)
|
||||
from .uizonedisplay import UiZone, UiZoneDisplay
|
||||
from ..flightwaypointtype import FlightWaypointType
|
||||
from ...utils import nautical_miles
|
||||
|
||||
|
||||
class ArmedReconFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
|
||||
@staticmethod
|
||||
def builder_type() -> Type[Builder]:
|
||||
return Builder
|
||||
|
||||
def ui_zone(self) -> UiZone:
|
||||
return UiZone(
|
||||
[self.tot_waypoint.position],
|
||||
nautical_miles(
|
||||
self.flight.coalition.game.settings.armed_recon_engagement_range_distance
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class Builder(FormationAttackBuilder[ArmedReconFlightPlan, FormationAttackLayout]):
|
||||
def layout(self) -> FormationAttackLayout:
|
||||
return self._build(FlightWaypointType.INGRESS_ARMED_RECON)
|
||||
|
||||
def build(self, dump_debug_info: bool = False) -> ArmedReconFlightPlan:
|
||||
return ArmedReconFlightPlan(self.flight, self.layout())
|
||||
@ -125,16 +125,12 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
|
||||
ingress_point_shapely.x, ingress_point_shapely.y
|
||||
)
|
||||
|
||||
patrol_start_waypoint = builder.nav(
|
||||
patrol_start, ingress_egress_altitude, use_agl_patrol_altitude
|
||||
)
|
||||
patrol_start_waypoint = builder.cas(patrol_start, ingress_egress_altitude)
|
||||
patrol_start_waypoint.name = "FLOT START"
|
||||
patrol_start_waypoint.pretty_name = "FLOT start"
|
||||
patrol_start_waypoint.description = "FLOT boundary"
|
||||
|
||||
patrol_end_waypoint = builder.nav(
|
||||
patrol_end, ingress_egress_altitude, use_agl_patrol_altitude
|
||||
)
|
||||
patrol_end_waypoint = builder.cas(patrol_end, ingress_egress_altitude)
|
||||
patrol_end_waypoint.name = "FLOT END"
|
||||
patrol_end_waypoint.pretty_name = "FLOT end"
|
||||
patrol_end_waypoint.description = "FLOT boundary"
|
||||
|
||||
@ -7,6 +7,7 @@ from .aewc import AewcFlightPlan
|
||||
from .airassault import AirAssaultFlightPlan
|
||||
from .airlift import AirliftFlightPlan
|
||||
from .antiship import AntiShipFlightPlan
|
||||
from .armedrecon import ArmedReconFlightPlan
|
||||
from .bai import BaiFlightPlan
|
||||
from .barcap import BarCapFlightPlan
|
||||
from .cas import CasFlightPlan
|
||||
@ -60,6 +61,7 @@ class FlightPlanBuilderTypes:
|
||||
FlightType.TRANSPORT: AirliftFlightPlan.builder_type(),
|
||||
FlightType.FERRY: FerryFlightPlan.builder_type(),
|
||||
FlightType.AIR_ASSAULT: AirAssaultFlightPlan.builder_type(),
|
||||
FlightType.ARMED_RECON: ArmedReconFlightPlan.builder_type(),
|
||||
}
|
||||
try:
|
||||
return builder_dict[flight.flight_type]
|
||||
|
||||
@ -286,6 +286,8 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
|
||||
return builder.sead_area(location)
|
||||
elif flight.flight_type == FlightType.OCA_AIRCRAFT:
|
||||
return builder.oca_strike_area(location)
|
||||
elif flight.flight_type == FlightType.ARMED_RECON:
|
||||
return builder.armed_recon_area(location)
|
||||
else:
|
||||
return builder.strike_area(location)
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
from typing import (
|
||||
@ -252,18 +253,9 @@ class WaypointBuilder:
|
||||
if ingress_type in [
|
||||
FlightWaypointType.INGRESS_CAS,
|
||||
FlightWaypointType.INGRESS_OCA_AIRCRAFT,
|
||||
FlightWaypointType.INGRESS_ARMED_RECON,
|
||||
]:
|
||||
weather = self.flight.coalition.game.conditions.weather
|
||||
max_alt = feet(30000)
|
||||
if weather.clouds and (
|
||||
weather.clouds.preset
|
||||
and "overcast" in weather.clouds.preset.description.lower()
|
||||
or weather.clouds.density > 5
|
||||
):
|
||||
max_alt = meters(
|
||||
max(feet(500).meters, weather.clouds.base - feet(500).meters)
|
||||
)
|
||||
alt = min(alt, max_alt)
|
||||
alt = self._adjust_altitude_for_clouds(alt)
|
||||
|
||||
alt_type: AltitudeReference = "BARO"
|
||||
if self.is_helo or self.flight.is_hercules:
|
||||
@ -291,6 +283,19 @@ class WaypointBuilder:
|
||||
targets=objective.strike_targets,
|
||||
)
|
||||
|
||||
def _adjust_altitude_for_clouds(self, alt: Distance) -> Distance:
|
||||
weather = self.flight.coalition.game.conditions.weather
|
||||
max_alt = feet(math.inf)
|
||||
if weather.clouds and (
|
||||
weather.clouds.preset
|
||||
and "overcast" in weather.clouds.preset.description.lower()
|
||||
or weather.clouds.density > 5
|
||||
):
|
||||
max_alt = meters(
|
||||
max(feet(500).meters, weather.clouds.base - feet(500).meters)
|
||||
)
|
||||
return min(alt, max_alt)
|
||||
|
||||
def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint:
|
||||
alt_type: AltitudeReference = "BARO"
|
||||
if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT:
|
||||
@ -354,6 +359,21 @@ class WaypointBuilder:
|
||||
def dead_area(self, target: MissionTarget) -> FlightWaypoint:
|
||||
return self._target_area(f"DEAD on {target.name}", target)
|
||||
|
||||
def armed_recon_area(self, target: MissionTarget) -> FlightWaypoint:
|
||||
# Force AI aircraft to fly towards target area
|
||||
alt = self.get_combat_altitude
|
||||
alt = self._adjust_altitude_for_clouds(alt)
|
||||
alt_type: AltitudeReference = "BARO"
|
||||
if self.is_helo or alt.feet <= AGL_TRANSITION_ALT:
|
||||
alt_type = "RADIO"
|
||||
return self._target_area(
|
||||
f"ARMED RECON {target.name}",
|
||||
target,
|
||||
altitude=alt,
|
||||
alt_type=alt_type,
|
||||
flyover=True,
|
||||
)
|
||||
|
||||
def oca_strike_area(self, target: MissionTarget) -> FlightWaypoint:
|
||||
return self._target_area(f"ATTACK {target.name}", target, flyover=True)
|
||||
|
||||
@ -398,15 +418,14 @@ class WaypointBuilder:
|
||||
waypoint.only_for_player = True
|
||||
return waypoint
|
||||
|
||||
def cas(self, position: Point) -> FlightWaypoint:
|
||||
def cas(self, position: Point, altitude: Distance) -> FlightWaypoint:
|
||||
weather = self.flight.coalition.game.conditions.weather
|
||||
max_alt = feet(30000)
|
||||
if weather.clouds and (
|
||||
weather.clouds.preset
|
||||
and "overcast" in weather.clouds.preset.description.lower()
|
||||
or weather.clouds.density > 5
|
||||
):
|
||||
max_alt = meters(
|
||||
altitude = meters(
|
||||
max(feet(500).meters, weather.clouds.base - feet(500).meters)
|
||||
)
|
||||
return FlightWaypoint(
|
||||
@ -415,7 +434,7 @@ class WaypointBuilder:
|
||||
position,
|
||||
feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
|
||||
if self.is_helo
|
||||
else min(meters(1000), max_alt),
|
||||
else max(meters(1000), altitude),
|
||||
"RADIO",
|
||||
description="Provide CAS",
|
||||
pretty_name="CAS",
|
||||
@ -667,14 +686,13 @@ class WaypointBuilder:
|
||||
This waypoint is used to generate the Trigger Zone used for AirAssault and
|
||||
AirLift using the CTLD plugin (see LogisticsGenerator)
|
||||
"""
|
||||
heli_alt = feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
|
||||
altitude = heli_alt if self.flight.is_helo else meters(0)
|
||||
alt = self.get_combat_altitude if self.flight.is_helo else meters(0)
|
||||
|
||||
return FlightWaypoint(
|
||||
"DROPOFFZONE",
|
||||
FlightWaypointType.DROPOFF_ZONE,
|
||||
drop_off.position,
|
||||
altitude,
|
||||
alt,
|
||||
"RADIO",
|
||||
description=f"Drop off cargo at {drop_off.name}",
|
||||
pretty_name="Drop-off zone",
|
||||
|
||||
@ -58,6 +58,7 @@ class FlightType(Enum):
|
||||
FERRY = "Ferry"
|
||||
AIR_ASSAULT = "Air Assault"
|
||||
SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD
|
||||
ARMED_RECON = "Armed Recon"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
@ -93,6 +94,7 @@ class FlightType(Enum):
|
||||
FlightType.SEAD_ESCORT,
|
||||
FlightType.AIR_ASSAULT,
|
||||
FlightType.SEAD_SWEEP,
|
||||
FlightType.ARMED_RECON,
|
||||
}
|
||||
|
||||
@property
|
||||
@ -104,6 +106,7 @@ class FlightType(Enum):
|
||||
return {
|
||||
FlightType.AEWC: AirEntity.AIRBORNE_EARLY_WARNING,
|
||||
FlightType.ANTISHIP: AirEntity.ANTISURFACE_WARFARE,
|
||||
FlightType.ARMED_RECON: AirEntity.ATTACK_STRIKE,
|
||||
FlightType.BAI: AirEntity.ATTACK_STRIKE,
|
||||
FlightType.BARCAP: AirEntity.FIGHTER,
|
||||
FlightType.CAS: AirEntity.ATTACK_STRIKE,
|
||||
|
||||
@ -51,3 +51,4 @@ class FlightWaypointType(IntEnum):
|
||||
INGRESS_AIR_ASSAULT = 31
|
||||
INGRESS_ANTI_SHIP = 32
|
||||
INGRESS_SEAD_SWEEP = 33
|
||||
INGRESS_ARMED_RECON = 34
|
||||
|
||||
@ -208,6 +208,7 @@ class Loadout:
|
||||
loadout_names[FlightType.INTERCEPTION].extend(loadout_names[FlightType.BARCAP])
|
||||
# OCA/Aircraft falls back to BAI, which falls back to CAS.
|
||||
loadout_names[FlightType.BAI].extend(loadout_names[FlightType.CAS])
|
||||
loadout_names[FlightType.ARMED_RECON].extend(loadout_names[FlightType.CAS])
|
||||
loadout_names[FlightType.OCA_AIRCRAFT].extend(loadout_names[FlightType.BAI])
|
||||
# DEAD also falls back to BAI.
|
||||
loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI])
|
||||
|
||||
@ -183,6 +183,7 @@ class Package(RadioFrequencyContainer):
|
||||
FlightType.SEAD_SWEEP,
|
||||
FlightType.TARCAP,
|
||||
FlightType.BARCAP,
|
||||
FlightType.ARMED_RECON,
|
||||
FlightType.AEWC,
|
||||
FlightType.FERRY,
|
||||
FlightType.REFUELING,
|
||||
|
||||
@ -167,24 +167,6 @@ class ObjectiveFinder:
|
||||
yield cp
|
||||
break
|
||||
|
||||
def vulnerable_enemy_control_points(self) -> Iterator[ControlPoint]:
|
||||
"""Iterates over enemy CPs that are vulnerable to Air Assault.
|
||||
Vulnerability is defined as any unit being alive in the CP's "blocking_capture" groups.
|
||||
"""
|
||||
for cp in self.enemy_control_points():
|
||||
include = True
|
||||
for tgo in cp.connected_objectives:
|
||||
if tgo.distance_to(cp) > cp.CAPTURE_DISTANCE.meters:
|
||||
continue
|
||||
for u in tgo.units:
|
||||
if u.is_vehicle and u.alive:
|
||||
include = False
|
||||
break
|
||||
if not include:
|
||||
break
|
||||
if include:
|
||||
yield cp
|
||||
|
||||
def oca_targets(self, min_aircraft: int) -> Iterator[ControlPoint]:
|
||||
parking_type = ParkingType()
|
||||
parking_type.include_rotary_wing = True
|
||||
|
||||
@ -6,7 +6,7 @@ import math
|
||||
from collections.abc import Iterator
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional, TYPE_CHECKING, Union
|
||||
from typing import Optional, TYPE_CHECKING, Union, Dict
|
||||
|
||||
from game.commander.battlepositions import BattlePositions
|
||||
from game.commander.objectivefinder import ObjectiveFinder
|
||||
@ -163,6 +163,15 @@ class TheaterState(WorldState["TheaterState"]):
|
||||
barcap_duration = coalition.doctrine.cap_duration.total_seconds()
|
||||
barcap_rounds = math.ceil(mission_duration / barcap_duration)
|
||||
|
||||
battle_postitions: Dict[ControlPoint, BattlePositions] = {
|
||||
cp: BattlePositions.for_control_point(cp)
|
||||
for cp in ordered_capturable_points
|
||||
}
|
||||
|
||||
vulnerable_control_points = [
|
||||
cp for cp, bp in battle_postitions.items() if not bp.blocking_capture
|
||||
]
|
||||
|
||||
return TheaterState(
|
||||
context=context,
|
||||
barcaps_needed={
|
||||
@ -179,10 +188,7 @@ class TheaterState(WorldState["TheaterState"]):
|
||||
enemy_convoys=list(finder.convoys()),
|
||||
enemy_shipping=list(finder.cargo_ships()),
|
||||
enemy_ships=list(finder.enemy_ships()),
|
||||
enemy_battle_positions={
|
||||
cp: BattlePositions.for_control_point(cp)
|
||||
for cp in ordered_capturable_points
|
||||
},
|
||||
enemy_battle_positions=battle_postitions,
|
||||
oca_targets=list(
|
||||
finder.oca_targets(
|
||||
min_aircraft=game.settings.oca_target_autoplanner_min_aircraft_count
|
||||
@ -191,5 +197,5 @@ class TheaterState(WorldState["TheaterState"]):
|
||||
strike_targets=list(finder.strike_targets()),
|
||||
enemy_barcaps=list(game.theater.control_points_for(not player)),
|
||||
threat_zones=game.threat_zone_for(not player),
|
||||
vulnerable_control_points=list(finder.vulnerable_enemy_control_points()),
|
||||
vulnerable_control_points=vulnerable_control_points,
|
||||
)
|
||||
|
||||
@ -241,9 +241,18 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
enrich = {}
|
||||
for t in self.task_priorities:
|
||||
if t == FlightType.SEAD:
|
||||
enrich[FlightType.SEAD_SWEEP] = self.task_priorities[t]
|
||||
if FlightType.SEAD_SWEEP not in self.task_priorities:
|
||||
if (value := self.task_priorities.get(FlightType.SEAD)) or (
|
||||
value := self.task_priorities.get(FlightType.SEAD_ESCORT)
|
||||
):
|
||||
enrich[FlightType.SEAD_SWEEP] = value
|
||||
|
||||
if FlightType.ARMED_RECON not in self.task_priorities:
|
||||
if (value := self.task_priorities.get(FlightType.CAS)) or (
|
||||
value := self.task_priorities.get(FlightType.BAI)
|
||||
):
|
||||
enrich[FlightType.ARMED_RECON] = value
|
||||
|
||||
self.task_priorities.update(enrich)
|
||||
|
||||
@classmethod
|
||||
@ -526,17 +535,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
||||
if prop_overrides is not None:
|
||||
cls._set_props_overrides(prop_overrides, aircraft)
|
||||
|
||||
from game.ato.flighttype import FlightType
|
||||
|
||||
task_priorities: dict[FlightType, int] = {}
|
||||
for task_name, priority in data.get("tasks", {}).items():
|
||||
task_priorities[FlightType(task_name)] = priority
|
||||
|
||||
if (
|
||||
FlightType.SEAD_SWEEP not in task_priorities
|
||||
and FlightType.SEAD in task_priorities
|
||||
):
|
||||
task_priorities[FlightType.SEAD_SWEEP] = task_priorities[FlightType.SEAD]
|
||||
task_priorities = cls.get_task_priorities(data)
|
||||
|
||||
cls._custom_weapon_injections(aircraft, data)
|
||||
cls._user_weapon_injections(aircraft)
|
||||
@ -583,6 +582,27 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
||||
use_f15e_waypoint_names=data.get("use_f15e_waypoint_names", False),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_task_priorities(cls, data: dict[str, Any]) -> dict[FlightType, int]:
|
||||
task_priorities: dict[FlightType, int] = {}
|
||||
for task_name, priority in data.get("tasks", {}).items():
|
||||
task_priorities[FlightType(task_name)] = priority
|
||||
if (
|
||||
FlightType.SEAD_SWEEP not in task_priorities
|
||||
and FlightType.SEAD in task_priorities
|
||||
):
|
||||
task_priorities[FlightType.SEAD_SWEEP] = task_priorities[FlightType.SEAD]
|
||||
if FlightType.ARMED_RECON not in task_priorities:
|
||||
if FlightType.CAS in task_priorities:
|
||||
task_priorities[FlightType.ARMED_RECON] = task_priorities[
|
||||
FlightType.CAS
|
||||
]
|
||||
elif FlightType.BAI in task_priorities:
|
||||
task_priorities[FlightType.ARMED_RECON] = task_priorities[
|
||||
FlightType.BAI
|
||||
]
|
||||
return task_priorities
|
||||
|
||||
@staticmethod
|
||||
def _custom_weapon_injections(
|
||||
aircraft: Type[FlyingType], data: Dict[str, Any]
|
||||
|
||||
@ -57,6 +57,8 @@ class AircraftBehavior:
|
||||
self.configure_refueling(group, flight)
|
||||
elif self.task in [FlightType.CAS, FlightType.BAI]:
|
||||
self.configure_cas(group, flight)
|
||||
elif self.task == FlightType.ARMED_RECON:
|
||||
self.configure_armed_recon(group, flight)
|
||||
elif self.task == FlightType.DEAD:
|
||||
self.configure_dead(group, flight)
|
||||
elif self.task in [FlightType.SEAD, FlightType.SEAD_SWEEP]:
|
||||
@ -183,6 +185,17 @@ class AircraftBehavior:
|
||||
restrict_jettison=True,
|
||||
)
|
||||
|
||||
def configure_armed_recon(self, group: FlyingGroup[Any], flight: Flight) -> None:
|
||||
self.configure_task(flight, group, CAS, [AFAC, AntishipStrike])
|
||||
self.configure_behavior(
|
||||
flight,
|
||||
group,
|
||||
react_on_threat=OptReactOnThreat.Values.EvadeFire,
|
||||
roe=OptROE.Values.OpenFire,
|
||||
rtb_winchester=OptRTBOnOutOfAmmo.Values.All,
|
||||
restrict_jettison=True,
|
||||
)
|
||||
|
||||
def configure_dead(self, group: FlyingGroup[Any], flight: Flight) -> None:
|
||||
# Only CAS and SEAD are capable of the Attack Group task. SEAD is arguably more
|
||||
# appropriate but it has an extremely limited list of capable aircraft, whereas
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
from dcs.point import MovingPoint
|
||||
from dcs.task import (
|
||||
OptECMUsing,
|
||||
ControlledTask,
|
||||
Targets,
|
||||
EngageTargetsInZone,
|
||||
)
|
||||
|
||||
from game.utils import nautical_miles
|
||||
from .pydcswaypointbuilder import PydcsWaypointBuilder
|
||||
|
||||
|
||||
class ArmedReconIngressBuilder(PydcsWaypointBuilder):
|
||||
def add_tasks(self, waypoint: MovingPoint) -> None:
|
||||
self.register_special_ingress_points()
|
||||
# Preemptively use ECM to better avoid getting swatted.
|
||||
ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar)
|
||||
waypoint.tasks.append(ecm_option)
|
||||
|
||||
waypoint.add_task(
|
||||
ControlledTask(
|
||||
EngageTargetsInZone(
|
||||
position=self.flight.flight_plan.tot_waypoint.position,
|
||||
radius=int(
|
||||
nautical_miles(
|
||||
self.flight.coalition.game.settings.armed_recon_engagement_range_distance
|
||||
).meters
|
||||
),
|
||||
targets=[
|
||||
Targets.All.GroundUnits,
|
||||
Targets.All.Air.Helicopters,
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -22,6 +22,7 @@ from game.settings import Settings
|
||||
from game.utils import pairwise
|
||||
from .airassaultingress import AirAssaultIngressBuilder
|
||||
from .antishipingress import AntiShipIngressBuilder
|
||||
from .armedreconingress import ArmedReconIngressBuilder
|
||||
from .baiingress import BaiIngressBuilder
|
||||
from .casingress import CasIngressBuilder
|
||||
from .deadingress import DeadIngressBuilder
|
||||
@ -136,6 +137,7 @@ class WaypointGenerator:
|
||||
FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder,
|
||||
FlightWaypointType.INGRESS_AIR_ASSAULT: AirAssaultIngressBuilder,
|
||||
FlightWaypointType.INGRESS_ANTI_SHIP: AntiShipIngressBuilder,
|
||||
FlightWaypointType.INGRESS_ARMED_RECON: ArmedReconIngressBuilder,
|
||||
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
|
||||
FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
|
||||
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,
|
||||
|
||||
@ -329,6 +329,14 @@ class Settings:
|
||||
min=0,
|
||||
max=100,
|
||||
)
|
||||
armed_recon_engagement_range_distance: int = bounded_int_option(
|
||||
"Armed Recon engagement range (NM)",
|
||||
page=CAMPAIGN_DOCTRINE_PAGE,
|
||||
section=DOCTRINE_DISTANCES_SECTION,
|
||||
default=5,
|
||||
min=0,
|
||||
max=25,
|
||||
)
|
||||
sead_sweep_engagement_range_distance: int = bounded_int_option(
|
||||
"SEAD Sweep engagement range (NM)",
|
||||
page=CAMPAIGN_DOCTRINE_PAGE,
|
||||
|
||||
@ -39,6 +39,7 @@ class MissionTarget:
|
||||
FlightType.TARCAP,
|
||||
FlightType.SEAD_ESCORT,
|
||||
FlightType.SEAD_SWEEP,
|
||||
FlightType.ARMED_RECON,
|
||||
FlightType.SWEEP,
|
||||
# TODO: FlightType.ELINT,
|
||||
# TODO: FlightType.EWAR,
|
||||
|
||||
@ -161,6 +161,7 @@ class QAutoCreateDialog(QDialog):
|
||||
FlightType.ANTISHIP,
|
||||
FlightType.BAI,
|
||||
FlightType.CAS,
|
||||
FlightType.ARMED_RECON,
|
||||
}
|
||||
for mt in self.package.target.mission_types(self.is_ownfor):
|
||||
if mt in primary_tasks:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user