Add SEAD Sweep flight plan

Reintroduce legacy SEAD Escort flight plan, but under a separate type because it didn't really escort the primary flight...
This commit is contained in:
Raffson 2023-07-02 16:51:21 +02:00
parent e2d9a794b8
commit db038ecdea
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
13 changed files with 102 additions and 12 deletions

View File

@ -47,6 +47,7 @@
* **[Options]** Renamed Maximum frontline length -> Maximum frontline width.
* **[Squadrons]** Add livery selector in Squadron Dialog, allowing you to change the livery during the campaign.
* **[New Game Wizard]** Automatically invert factions when 'Invert Map' is selected.
* **[Flight Plans]** Added "SEAD Sweep" flight plan, which basically reintroduces the legacy "SEAD Escort" flight plan where the flight will engage whatever it can find without actually escorting the primary flight.
## Fixes

View File

@ -19,6 +19,7 @@ from .ocarunway import OcaRunwayFlightPlan
from .packagerefueling import PackageRefuelingFlightPlan
from .planningerror import PlanningError
from .sead import SeadFlightPlan
from .seadsweep import SeadSweepFlightPlan
from .strike import StrikeFlightPlan
from .sweep import SweepFlightPlan
from .tarcap import TarCapFlightPlan
@ -51,6 +52,7 @@ class FlightPlanBuilderTypes:
FlightType.OCA_RUNWAY: OcaRunwayFlightPlan.builder_type(),
FlightType.SEAD: SeadFlightPlan.builder_type(),
FlightType.SEAD_ESCORT: EscortFlightPlan.builder_type(),
FlightType.SEAD_SWEEP: SeadSweepFlightPlan.builder_type(),
FlightType.STRIKE: StrikeFlightPlan.builder_type(),
FlightType.SWEEP: SweepFlightPlan.builder_type(),
FlightType.TARCAP: TarCapFlightPlan.builder_type(),

View File

@ -182,6 +182,8 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
initial = None
if ingress_type == FlightWaypointType.INGRESS_SEAD:
initial = builder.sead_search(self.package.target)
elif ingress_type == FlightWaypointType.INGRESS_SEAD_SWEEP:
initial = builder.sead_sweep(self.package.target)
return FormationAttackLayout(
departure=builder.takeoff(self.flight.departure),
@ -213,7 +215,7 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
return builder.bai_group(target)
elif flight.flight_type == FlightType.DEAD:
return builder.dead_point(target)
elif flight.flight_type == FlightType.SEAD:
elif flight.flight_type in {FlightType.SEAD, FlightType.SEAD_SWEEP}:
return builder.sead_point(target)
else:
return builder.strike_point(target)

View File

@ -0,0 +1,28 @@
from __future__ import annotations
from datetime import timedelta
from typing import Type
from .formationattack import (
FormationAttackBuilder,
FormationAttackFlightPlan,
FormationAttackLayout,
)
from ..flightwaypointtype import FlightWaypointType
class SeadSweepFlightPlan(FormationAttackFlightPlan):
@staticmethod
def builder_type() -> Type[Builder]:
return Builder
def default_tot_offset(self) -> timedelta:
return -timedelta(minutes=2)
class Builder(FormationAttackBuilder[SeadSweepFlightPlan, FormationAttackLayout]):
def layout(self) -> FormationAttackLayout:
return self._build(FlightWaypointType.INGRESS_SEAD_SWEEP)
def build(self) -> SeadSweepFlightPlan:
return SeadSweepFlightPlan(self.flight, self.layout())

View File

@ -423,6 +423,32 @@ class WaypointBuilder:
)
def sead_search(self, target: MissionTarget) -> FlightWaypoint:
hold = self._sead_search_point(target)
return FlightWaypoint(
"SEAD Search",
FlightWaypointType.NAV,
hold,
self.doctrine.ingress_altitude,
alt_type="BARO",
description="Anchor and search from this point",
pretty_name="SEAD Search",
)
def sead_sweep(self, target: MissionTarget) -> FlightWaypoint:
hold = self._sead_search_point(target)
return FlightWaypoint(
"SEAD Sweep",
FlightWaypointType.NAV,
hold,
self.doctrine.ingress_altitude,
alt_type="BARO",
description="Anchor and search from this point",
pretty_name="SEAD Sweep",
)
def _sead_search_point(self, target: MissionTarget) -> Point:
"""Creates custom waypoint for AI SEAD flights
to avoid having them fly all the way to the SAM site.
Args:
@ -437,16 +463,7 @@ class WaypointBuilder:
hold = target.position.point_from_heading(
hdg, min(threat_range, ingress2tgt_dist * 0.95)
)
return FlightWaypoint(
"SEAD Search",
FlightWaypointType.INGRESS_SEAD,
hold,
self.doctrine.ingress_altitude,
alt_type="BARO",
description="Anchor and search from this point",
pretty_name="SEAD Search",
)
return hold
@staticmethod
def escort_hold(start: Point, altitude: Distance) -> FlightWaypoint:

View File

@ -57,6 +57,7 @@ class FlightType(Enum):
REFUELING = "Refueling"
FERRY = "Ferry"
AIR_ASSAULT = "Air Assault"
SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD
def __str__(self) -> str:
return self.value
@ -91,6 +92,7 @@ class FlightType(Enum):
FlightType.OCA_AIRCRAFT,
FlightType.SEAD_ESCORT,
FlightType.AIR_ASSAULT,
FlightType.SEAD_SWEEP,
}
@property
@ -110,6 +112,7 @@ class FlightType(Enum):
FlightType.REFUELING: AirEntity.TANKER,
FlightType.SEAD: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE,
FlightType.SEAD_ESCORT: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE,
FlightType.SEAD_SWEEP: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE,
FlightType.STRIKE: AirEntity.ATTACK_STRIKE,
FlightType.SWEEP: AirEntity.FIGHTER,
FlightType.TARCAP: AirEntity.FIGHTER,

View File

@ -50,3 +50,4 @@ class FlightWaypointType(IntEnum):
CARGO_STOP = 30 # Stopover landing point using the LandingReFuAr waypoint type
INGRESS_AIR_ASSAULT = 31
INGRESS_ANTI_SHIP = 32
INGRESS_SEAD_SWEEP = 33

View File

@ -184,6 +184,9 @@ class Loadout:
# A SEAD escort typically does not need a different loadout than a regular
# SEAD flight, so fall back to SEAD if needed.
loadout_names[FlightType.SEAD_ESCORT].extend(loadout_names[FlightType.SEAD])
loadout_names[FlightType.SEAD_SWEEP].extend(
loadout_names[FlightType.SEAD_ESCORT]
)
# Sweep and escort can fall back to TARCAP.
loadout_names[FlightType.ESCORT].extend(loadout_names[FlightType.TARCAP])
loadout_names[FlightType.SWEEP].extend(loadout_names[FlightType.TARCAP])

View File

@ -459,6 +459,9 @@ class AircraftType(UnitType[Type[FlyingType]]):
for task_name, priority in data.get("tasks", {}).items():
task_priorities[FlightType(task_name)] = priority
if FlightType.SEAD in task_priorities:
task_priorities[FlightType.SEAD_SWEEP] = task_priorities[FlightType.SEAD]
for variant in data.get("variants", [aircraft.id]):
yield AircraftType(
dcs_unit_type=aircraft,

View File

@ -56,7 +56,7 @@ class AircraftBehavior:
self.configure_cas(group, flight)
elif self.task == FlightType.DEAD:
self.configure_dead(group, flight)
elif self.task == FlightType.SEAD:
elif self.task in [FlightType.SEAD, FlightType.SEAD_SWEEP]:
self.configure_sead(group, flight)
elif self.task == FlightType.SEAD_ESCORT:
self.configure_sead_escort(group, flight)

View File

@ -0,0 +1,27 @@
from dcs.point import MovingPoint
from dcs.task import (
OptECMUsing,
ControlledTask,
EngageTargets,
Targets,
)
from game.utils import nautical_miles
from .pydcswaypointbuilder import PydcsWaypointBuilder
class SeadSweepIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
# Preemptively use ECM to better avoid getting swatted.
ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar)
waypoint.tasks.append(ecm_option)
waypoint.add_task(
ControlledTask(
EngageTargets(
# TODO: From doctrine.
max_distance=int(nautical_miles(30).meters),
targets=[Targets.All.GroundUnits.AirDefence.AAA.SAMRelated],
)
)
)

View File

@ -37,6 +37,7 @@ from .racetrack import RaceTrackBuilder
from .racetrackend import RaceTrackEndBuilder
from .refuel import RefuelPointBuilder
from .seadingress import SeadIngressBuilder
from .seadsweepingress import SeadSweepIngressBuilder
from .splitpoint import SplitPointBuilder
from .strikeingress import StrikeIngressBuilder
from .sweepingress import SweepIngressBuilder
@ -126,6 +127,7 @@ class WaypointGenerator:
FlightWaypointType.INGRESS_OCA_AIRCRAFT: OcaAircraftIngressBuilder,
FlightWaypointType.INGRESS_OCA_RUNWAY: OcaRunwayIngressBuilder,
FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder,
FlightWaypointType.INGRESS_SEAD_SWEEP: SeadSweepIngressBuilder,
FlightWaypointType.INGRESS_STRIKE: StrikeIngressBuilder,
FlightWaypointType.INGRESS_SWEEP: SweepIngressBuilder,
FlightWaypointType.JOIN: JoinPointBuilder,

View File

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