Initial support for Armed Recon flight plan

This commit is contained in:
Raffson 2024-07-20 20:24:21 +02:00
parent c24fba0ba4
commit f405ffdfe2
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
19 changed files with 189 additions and 62 deletions

View File

@ -25,6 +25,7 @@
* **[Modding]** Added support for Su-15 Flagon mod (v1.0) * **[Modding]** Added support for Su-15 Flagon mod (v1.0)
* **[Plugins]** Support for Carsten's Arty Spotter script * **[Plugins]** Support for Carsten's Arty Spotter script
* **[Modding]** Added support for SK-60 mod (v1.2.1) * **[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 ## Fixes
* **[UI/UX]** A-10A flights can be edited again * **[UI/UX]** A-10A flights can be edited again

View 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())

View File

@ -125,16 +125,12 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
ingress_point_shapely.x, ingress_point_shapely.y ingress_point_shapely.x, ingress_point_shapely.y
) )
patrol_start_waypoint = builder.nav( patrol_start_waypoint = builder.cas(patrol_start, ingress_egress_altitude)
patrol_start, ingress_egress_altitude, use_agl_patrol_altitude
)
patrol_start_waypoint.name = "FLOT START" patrol_start_waypoint.name = "FLOT START"
patrol_start_waypoint.pretty_name = "FLOT start" patrol_start_waypoint.pretty_name = "FLOT start"
patrol_start_waypoint.description = "FLOT boundary" patrol_start_waypoint.description = "FLOT boundary"
patrol_end_waypoint = builder.nav( patrol_end_waypoint = builder.cas(patrol_end, ingress_egress_altitude)
patrol_end, ingress_egress_altitude, use_agl_patrol_altitude
)
patrol_end_waypoint.name = "FLOT END" patrol_end_waypoint.name = "FLOT END"
patrol_end_waypoint.pretty_name = "FLOT end" patrol_end_waypoint.pretty_name = "FLOT end"
patrol_end_waypoint.description = "FLOT boundary" patrol_end_waypoint.description = "FLOT boundary"

View File

@ -7,6 +7,7 @@ from .aewc import AewcFlightPlan
from .airassault import AirAssaultFlightPlan from .airassault import AirAssaultFlightPlan
from .airlift import AirliftFlightPlan from .airlift import AirliftFlightPlan
from .antiship import AntiShipFlightPlan from .antiship import AntiShipFlightPlan
from .armedrecon import ArmedReconFlightPlan
from .bai import BaiFlightPlan from .bai import BaiFlightPlan
from .barcap import BarCapFlightPlan from .barcap import BarCapFlightPlan
from .cas import CasFlightPlan from .cas import CasFlightPlan
@ -60,6 +61,7 @@ class FlightPlanBuilderTypes:
FlightType.TRANSPORT: AirliftFlightPlan.builder_type(), FlightType.TRANSPORT: AirliftFlightPlan.builder_type(),
FlightType.FERRY: FerryFlightPlan.builder_type(), FlightType.FERRY: FerryFlightPlan.builder_type(),
FlightType.AIR_ASSAULT: AirAssaultFlightPlan.builder_type(), FlightType.AIR_ASSAULT: AirAssaultFlightPlan.builder_type(),
FlightType.ARMED_RECON: ArmedReconFlightPlan.builder_type(),
} }
try: try:
return builder_dict[flight.flight_type] return builder_dict[flight.flight_type]

View File

@ -286,6 +286,8 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
return builder.sead_area(location) return builder.sead_area(location)
elif flight.flight_type == FlightType.OCA_AIRCRAFT: elif flight.flight_type == FlightType.OCA_AIRCRAFT:
return builder.oca_strike_area(location) return builder.oca_strike_area(location)
elif flight.flight_type == FlightType.ARMED_RECON:
return builder.armed_recon_area(location)
else: else:
return builder.strike_area(location) return builder.strike_area(location)

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import math
import random import random
from dataclasses import dataclass from dataclasses import dataclass
from typing import ( from typing import (
@ -252,18 +253,9 @@ class WaypointBuilder:
if ingress_type in [ if ingress_type in [
FlightWaypointType.INGRESS_CAS, FlightWaypointType.INGRESS_CAS,
FlightWaypointType.INGRESS_OCA_AIRCRAFT, FlightWaypointType.INGRESS_OCA_AIRCRAFT,
FlightWaypointType.INGRESS_ARMED_RECON,
]: ]:
weather = self.flight.coalition.game.conditions.weather alt = self._adjust_altitude_for_clouds(alt)
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_type: AltitudeReference = "BARO" alt_type: AltitudeReference = "BARO"
if self.is_helo or self.flight.is_hercules: if self.is_helo or self.flight.is_hercules:
@ -291,6 +283,19 @@ class WaypointBuilder:
targets=objective.strike_targets, 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: def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO" alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT: 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: def dead_area(self, target: MissionTarget) -> FlightWaypoint:
return self._target_area(f"DEAD on {target.name}", target) 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: def oca_strike_area(self, target: MissionTarget) -> FlightWaypoint:
return self._target_area(f"ATTACK {target.name}", target, flyover=True) return self._target_area(f"ATTACK {target.name}", target, flyover=True)
@ -398,15 +418,14 @@ class WaypointBuilder:
waypoint.only_for_player = True waypoint.only_for_player = True
return waypoint return waypoint
def cas(self, position: Point) -> FlightWaypoint: def cas(self, position: Point, altitude: Distance) -> FlightWaypoint:
weather = self.flight.coalition.game.conditions.weather weather = self.flight.coalition.game.conditions.weather
max_alt = feet(30000)
if weather.clouds and ( if weather.clouds and (
weather.clouds.preset weather.clouds.preset
and "overcast" in weather.clouds.preset.description.lower() and "overcast" in weather.clouds.preset.description.lower()
or weather.clouds.density > 5 or weather.clouds.density > 5
): ):
max_alt = meters( altitude = meters(
max(feet(500).meters, weather.clouds.base - feet(500).meters) max(feet(500).meters, weather.clouds.base - feet(500).meters)
) )
return FlightWaypoint( return FlightWaypoint(
@ -415,7 +434,7 @@ class WaypointBuilder:
position, position,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl) feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
if self.is_helo if self.is_helo
else min(meters(1000), max_alt), else max(meters(1000), altitude),
"RADIO", "RADIO",
description="Provide CAS", description="Provide CAS",
pretty_name="CAS", pretty_name="CAS",
@ -667,14 +686,13 @@ class WaypointBuilder:
This waypoint is used to generate the Trigger Zone used for AirAssault and This waypoint is used to generate the Trigger Zone used for AirAssault and
AirLift using the CTLD plugin (see LogisticsGenerator) AirLift using the CTLD plugin (see LogisticsGenerator)
""" """
heli_alt = feet(self.flight.coalition.game.settings.heli_cruise_alt_agl) alt = self.get_combat_altitude if self.flight.is_helo else meters(0)
altitude = heli_alt if self.flight.is_helo else meters(0)
return FlightWaypoint( return FlightWaypoint(
"DROPOFFZONE", "DROPOFFZONE",
FlightWaypointType.DROPOFF_ZONE, FlightWaypointType.DROPOFF_ZONE,
drop_off.position, drop_off.position,
altitude, alt,
"RADIO", "RADIO",
description=f"Drop off cargo at {drop_off.name}", description=f"Drop off cargo at {drop_off.name}",
pretty_name="Drop-off zone", pretty_name="Drop-off zone",

View File

@ -58,6 +58,7 @@ class FlightType(Enum):
FERRY = "Ferry" FERRY = "Ferry"
AIR_ASSAULT = "Air Assault" AIR_ASSAULT = "Air Assault"
SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD
ARMED_RECON = "Armed Recon"
def __str__(self) -> str: def __str__(self) -> str:
return self.value return self.value
@ -93,6 +94,7 @@ class FlightType(Enum):
FlightType.SEAD_ESCORT, FlightType.SEAD_ESCORT,
FlightType.AIR_ASSAULT, FlightType.AIR_ASSAULT,
FlightType.SEAD_SWEEP, FlightType.SEAD_SWEEP,
FlightType.ARMED_RECON,
} }
@property @property
@ -104,6 +106,7 @@ class FlightType(Enum):
return { return {
FlightType.AEWC: AirEntity.AIRBORNE_EARLY_WARNING, FlightType.AEWC: AirEntity.AIRBORNE_EARLY_WARNING,
FlightType.ANTISHIP: AirEntity.ANTISURFACE_WARFARE, FlightType.ANTISHIP: AirEntity.ANTISURFACE_WARFARE,
FlightType.ARMED_RECON: AirEntity.ATTACK_STRIKE,
FlightType.BAI: AirEntity.ATTACK_STRIKE, FlightType.BAI: AirEntity.ATTACK_STRIKE,
FlightType.BARCAP: AirEntity.FIGHTER, FlightType.BARCAP: AirEntity.FIGHTER,
FlightType.CAS: AirEntity.ATTACK_STRIKE, FlightType.CAS: AirEntity.ATTACK_STRIKE,

View File

@ -51,3 +51,4 @@ class FlightWaypointType(IntEnum):
INGRESS_AIR_ASSAULT = 31 INGRESS_AIR_ASSAULT = 31
INGRESS_ANTI_SHIP = 32 INGRESS_ANTI_SHIP = 32
INGRESS_SEAD_SWEEP = 33 INGRESS_SEAD_SWEEP = 33
INGRESS_ARMED_RECON = 34

View File

@ -208,6 +208,7 @@ class Loadout:
loadout_names[FlightType.INTERCEPTION].extend(loadout_names[FlightType.BARCAP]) loadout_names[FlightType.INTERCEPTION].extend(loadout_names[FlightType.BARCAP])
# OCA/Aircraft falls back to BAI, which falls back to CAS. # OCA/Aircraft falls back to BAI, which falls back to CAS.
loadout_names[FlightType.BAI].extend(loadout_names[FlightType.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]) loadout_names[FlightType.OCA_AIRCRAFT].extend(loadout_names[FlightType.BAI])
# DEAD also falls back to BAI. # DEAD also falls back to BAI.
loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI]) loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI])

View File

@ -183,6 +183,7 @@ class Package(RadioFrequencyContainer):
FlightType.SEAD_SWEEP, FlightType.SEAD_SWEEP,
FlightType.TARCAP, FlightType.TARCAP,
FlightType.BARCAP, FlightType.BARCAP,
FlightType.ARMED_RECON,
FlightType.AEWC, FlightType.AEWC,
FlightType.FERRY, FlightType.FERRY,
FlightType.REFUELING, FlightType.REFUELING,

View File

@ -167,24 +167,6 @@ class ObjectiveFinder:
yield cp yield cp
break 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]: def oca_targets(self, min_aircraft: int) -> Iterator[ControlPoint]:
parking_type = ParkingType() parking_type = ParkingType()
parking_type.include_rotary_wing = True parking_type.include_rotary_wing = True

View File

@ -6,7 +6,7 @@ import math
from collections.abc import Iterator from collections.abc import Iterator
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime 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.battlepositions import BattlePositions
from game.commander.objectivefinder import ObjectiveFinder from game.commander.objectivefinder import ObjectiveFinder
@ -163,6 +163,15 @@ class TheaterState(WorldState["TheaterState"]):
barcap_duration = coalition.doctrine.cap_duration.total_seconds() barcap_duration = coalition.doctrine.cap_duration.total_seconds()
barcap_rounds = math.ceil(mission_duration / barcap_duration) 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( return TheaterState(
context=context, context=context,
barcaps_needed={ barcaps_needed={
@ -179,10 +188,7 @@ class TheaterState(WorldState["TheaterState"]):
enemy_convoys=list(finder.convoys()), enemy_convoys=list(finder.convoys()),
enemy_shipping=list(finder.cargo_ships()), enemy_shipping=list(finder.cargo_ships()),
enemy_ships=list(finder.enemy_ships()), enemy_ships=list(finder.enemy_ships()),
enemy_battle_positions={ enemy_battle_positions=battle_postitions,
cp: BattlePositions.for_control_point(cp)
for cp in ordered_capturable_points
},
oca_targets=list( oca_targets=list(
finder.oca_targets( finder.oca_targets(
min_aircraft=game.settings.oca_target_autoplanner_min_aircraft_count min_aircraft=game.settings.oca_target_autoplanner_min_aircraft_count
@ -191,5 +197,5 @@ class TheaterState(WorldState["TheaterState"]):
strike_targets=list(finder.strike_targets()), strike_targets=list(finder.strike_targets()),
enemy_barcaps=list(game.theater.control_points_for(not player)), enemy_barcaps=list(game.theater.control_points_for(not player)),
threat_zones=game.threat_zone_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,
) )

View File

@ -241,9 +241,18 @@ class AircraftType(UnitType[Type[FlyingType]]):
def __post_init__(self) -> None: def __post_init__(self) -> None:
enrich = {} enrich = {}
for t in self.task_priorities: if FlightType.SEAD_SWEEP not in self.task_priorities:
if t == FlightType.SEAD: if (value := self.task_priorities.get(FlightType.SEAD)) or (
enrich[FlightType.SEAD_SWEEP] = self.task_priorities[t] 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) self.task_priorities.update(enrich)
@classmethod @classmethod
@ -526,17 +535,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
if prop_overrides is not None: if prop_overrides is not None:
cls._set_props_overrides(prop_overrides, aircraft) cls._set_props_overrides(prop_overrides, aircraft)
from game.ato.flighttype import FlightType task_priorities = cls.get_task_priorities(data)
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]
cls._custom_weapon_injections(aircraft, data) cls._custom_weapon_injections(aircraft, data)
cls._user_weapon_injections(aircraft) 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), 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 @staticmethod
def _custom_weapon_injections( def _custom_weapon_injections(
aircraft: Type[FlyingType], data: Dict[str, Any] aircraft: Type[FlyingType], data: Dict[str, Any]

View File

@ -57,6 +57,8 @@ class AircraftBehavior:
self.configure_refueling(group, flight) self.configure_refueling(group, flight)
elif self.task in [FlightType.CAS, FlightType.BAI]: elif self.task in [FlightType.CAS, FlightType.BAI]:
self.configure_cas(group, flight) self.configure_cas(group, flight)
elif self.task == FlightType.ARMED_RECON:
self.configure_armed_recon(group, flight)
elif self.task == FlightType.DEAD: elif self.task == FlightType.DEAD:
self.configure_dead(group, flight) self.configure_dead(group, flight)
elif self.task in [FlightType.SEAD, FlightType.SEAD_SWEEP]: elif self.task in [FlightType.SEAD, FlightType.SEAD_SWEEP]:
@ -183,6 +185,17 @@ class AircraftBehavior:
restrict_jettison=True, 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: 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 # 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 # appropriate but it has an extremely limited list of capable aircraft, whereas

View File

@ -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,
],
)
)
)

View File

@ -22,6 +22,7 @@ from game.settings import Settings
from game.utils import pairwise from game.utils import pairwise
from .airassaultingress import AirAssaultIngressBuilder from .airassaultingress import AirAssaultIngressBuilder
from .antishipingress import AntiShipIngressBuilder from .antishipingress import AntiShipIngressBuilder
from .armedreconingress import ArmedReconIngressBuilder
from .baiingress import BaiIngressBuilder from .baiingress import BaiIngressBuilder
from .casingress import CasIngressBuilder from .casingress import CasIngressBuilder
from .deadingress import DeadIngressBuilder from .deadingress import DeadIngressBuilder
@ -136,6 +137,7 @@ class WaypointGenerator:
FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder, FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder,
FlightWaypointType.INGRESS_AIR_ASSAULT: AirAssaultIngressBuilder, FlightWaypointType.INGRESS_AIR_ASSAULT: AirAssaultIngressBuilder,
FlightWaypointType.INGRESS_ANTI_SHIP: AntiShipIngressBuilder, FlightWaypointType.INGRESS_ANTI_SHIP: AntiShipIngressBuilder,
FlightWaypointType.INGRESS_ARMED_RECON: ArmedReconIngressBuilder,
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder, FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
FlightWaypointType.INGRESS_CAS: CasIngressBuilder, FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder, FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,

View File

@ -329,6 +329,14 @@ class Settings:
min=0, min=0,
max=100, 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_distance: int = bounded_int_option(
"SEAD Sweep engagement range (NM)", "SEAD Sweep engagement range (NM)",
page=CAMPAIGN_DOCTRINE_PAGE, page=CAMPAIGN_DOCTRINE_PAGE,

View File

@ -39,6 +39,7 @@ class MissionTarget:
FlightType.TARCAP, FlightType.TARCAP,
FlightType.SEAD_ESCORT, FlightType.SEAD_ESCORT,
FlightType.SEAD_SWEEP, FlightType.SEAD_SWEEP,
FlightType.ARMED_RECON,
FlightType.SWEEP, FlightType.SWEEP,
# TODO: FlightType.ELINT, # TODO: FlightType.ELINT,
# TODO: FlightType.EWAR, # TODO: FlightType.EWAR,

View File

@ -161,6 +161,7 @@ class QAutoCreateDialog(QDialog):
FlightType.ANTISHIP, FlightType.ANTISHIP,
FlightType.BAI, FlightType.BAI,
FlightType.CAS, FlightType.CAS,
FlightType.ARMED_RECON,
} }
for mt in self.package.target.mission_types(self.is_ownfor): for mt in self.package.target.mission_types(self.is_ownfor):
if mt in primary_tasks: if mt in primary_tasks: