mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
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:
parent
e2d9a794b8
commit
db038ecdea
@ -47,6 +47,7 @@
|
|||||||
* **[Options]** Renamed Maximum frontline length -> Maximum frontline width.
|
* **[Options]** Renamed Maximum frontline length -> Maximum frontline width.
|
||||||
* **[Squadrons]** Add livery selector in Squadron Dialog, allowing you to change the livery during the campaign.
|
* **[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.
|
* **[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
|
## Fixes
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from .ocarunway import OcaRunwayFlightPlan
|
|||||||
from .packagerefueling import PackageRefuelingFlightPlan
|
from .packagerefueling import PackageRefuelingFlightPlan
|
||||||
from .planningerror import PlanningError
|
from .planningerror import PlanningError
|
||||||
from .sead import SeadFlightPlan
|
from .sead import SeadFlightPlan
|
||||||
|
from .seadsweep import SeadSweepFlightPlan
|
||||||
from .strike import StrikeFlightPlan
|
from .strike import StrikeFlightPlan
|
||||||
from .sweep import SweepFlightPlan
|
from .sweep import SweepFlightPlan
|
||||||
from .tarcap import TarCapFlightPlan
|
from .tarcap import TarCapFlightPlan
|
||||||
@ -51,6 +52,7 @@ class FlightPlanBuilderTypes:
|
|||||||
FlightType.OCA_RUNWAY: OcaRunwayFlightPlan.builder_type(),
|
FlightType.OCA_RUNWAY: OcaRunwayFlightPlan.builder_type(),
|
||||||
FlightType.SEAD: SeadFlightPlan.builder_type(),
|
FlightType.SEAD: SeadFlightPlan.builder_type(),
|
||||||
FlightType.SEAD_ESCORT: EscortFlightPlan.builder_type(),
|
FlightType.SEAD_ESCORT: EscortFlightPlan.builder_type(),
|
||||||
|
FlightType.SEAD_SWEEP: SeadSweepFlightPlan.builder_type(),
|
||||||
FlightType.STRIKE: StrikeFlightPlan.builder_type(),
|
FlightType.STRIKE: StrikeFlightPlan.builder_type(),
|
||||||
FlightType.SWEEP: SweepFlightPlan.builder_type(),
|
FlightType.SWEEP: SweepFlightPlan.builder_type(),
|
||||||
FlightType.TARCAP: TarCapFlightPlan.builder_type(),
|
FlightType.TARCAP: TarCapFlightPlan.builder_type(),
|
||||||
|
|||||||
@ -182,6 +182,8 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
|
|||||||
initial = None
|
initial = None
|
||||||
if ingress_type == FlightWaypointType.INGRESS_SEAD:
|
if ingress_type == FlightWaypointType.INGRESS_SEAD:
|
||||||
initial = builder.sead_search(self.package.target)
|
initial = builder.sead_search(self.package.target)
|
||||||
|
elif ingress_type == FlightWaypointType.INGRESS_SEAD_SWEEP:
|
||||||
|
initial = builder.sead_sweep(self.package.target)
|
||||||
|
|
||||||
return FormationAttackLayout(
|
return FormationAttackLayout(
|
||||||
departure=builder.takeoff(self.flight.departure),
|
departure=builder.takeoff(self.flight.departure),
|
||||||
@ -213,7 +215,7 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
|
|||||||
return builder.bai_group(target)
|
return builder.bai_group(target)
|
||||||
elif flight.flight_type == FlightType.DEAD:
|
elif flight.flight_type == FlightType.DEAD:
|
||||||
return builder.dead_point(target)
|
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)
|
return builder.sead_point(target)
|
||||||
else:
|
else:
|
||||||
return builder.strike_point(target)
|
return builder.strike_point(target)
|
||||||
|
|||||||
28
game/ato/flightplans/seadsweep.py
Normal file
28
game/ato/flightplans/seadsweep.py
Normal 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())
|
||||||
@ -423,6 +423,32 @@ class WaypointBuilder:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def sead_search(self, target: MissionTarget) -> FlightWaypoint:
|
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
|
"""Creates custom waypoint for AI SEAD flights
|
||||||
to avoid having them fly all the way to the SAM site.
|
to avoid having them fly all the way to the SAM site.
|
||||||
Args:
|
Args:
|
||||||
@ -437,16 +463,7 @@ class WaypointBuilder:
|
|||||||
hold = target.position.point_from_heading(
|
hold = target.position.point_from_heading(
|
||||||
hdg, min(threat_range, ingress2tgt_dist * 0.95)
|
hdg, min(threat_range, ingress2tgt_dist * 0.95)
|
||||||
)
|
)
|
||||||
|
return hold
|
||||||
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",
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def escort_hold(start: Point, altitude: Distance) -> FlightWaypoint:
|
def escort_hold(start: Point, altitude: Distance) -> FlightWaypoint:
|
||||||
|
|||||||
@ -57,6 +57,7 @@ class FlightType(Enum):
|
|||||||
REFUELING = "Refueling"
|
REFUELING = "Refueling"
|
||||||
FERRY = "Ferry"
|
FERRY = "Ferry"
|
||||||
AIR_ASSAULT = "Air Assault"
|
AIR_ASSAULT = "Air Assault"
|
||||||
|
SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.value
|
return self.value
|
||||||
@ -91,6 +92,7 @@ class FlightType(Enum):
|
|||||||
FlightType.OCA_AIRCRAFT,
|
FlightType.OCA_AIRCRAFT,
|
||||||
FlightType.SEAD_ESCORT,
|
FlightType.SEAD_ESCORT,
|
||||||
FlightType.AIR_ASSAULT,
|
FlightType.AIR_ASSAULT,
|
||||||
|
FlightType.SEAD_SWEEP,
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -110,6 +112,7 @@ class FlightType(Enum):
|
|||||||
FlightType.REFUELING: AirEntity.TANKER,
|
FlightType.REFUELING: AirEntity.TANKER,
|
||||||
FlightType.SEAD: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE,
|
FlightType.SEAD: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE,
|
||||||
FlightType.SEAD_ESCORT: 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.STRIKE: AirEntity.ATTACK_STRIKE,
|
||||||
FlightType.SWEEP: AirEntity.FIGHTER,
|
FlightType.SWEEP: AirEntity.FIGHTER,
|
||||||
FlightType.TARCAP: AirEntity.FIGHTER,
|
FlightType.TARCAP: AirEntity.FIGHTER,
|
||||||
|
|||||||
@ -50,3 +50,4 @@ class FlightWaypointType(IntEnum):
|
|||||||
CARGO_STOP = 30 # Stopover landing point using the LandingReFuAr waypoint type
|
CARGO_STOP = 30 # Stopover landing point using the LandingReFuAr waypoint type
|
||||||
INGRESS_AIR_ASSAULT = 31
|
INGRESS_AIR_ASSAULT = 31
|
||||||
INGRESS_ANTI_SHIP = 32
|
INGRESS_ANTI_SHIP = 32
|
||||||
|
INGRESS_SEAD_SWEEP = 33
|
||||||
|
|||||||
@ -184,6 +184,9 @@ class Loadout:
|
|||||||
# A SEAD escort typically does not need a different loadout than a regular
|
# A SEAD escort typically does not need a different loadout than a regular
|
||||||
# SEAD flight, so fall back to SEAD if needed.
|
# SEAD flight, so fall back to SEAD if needed.
|
||||||
loadout_names[FlightType.SEAD_ESCORT].extend(loadout_names[FlightType.SEAD])
|
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.
|
# Sweep and escort can fall back to TARCAP.
|
||||||
loadout_names[FlightType.ESCORT].extend(loadout_names[FlightType.TARCAP])
|
loadout_names[FlightType.ESCORT].extend(loadout_names[FlightType.TARCAP])
|
||||||
loadout_names[FlightType.SWEEP].extend(loadout_names[FlightType.TARCAP])
|
loadout_names[FlightType.SWEEP].extend(loadout_names[FlightType.TARCAP])
|
||||||
|
|||||||
@ -459,6 +459,9 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
for task_name, priority in data.get("tasks", {}).items():
|
for task_name, priority in data.get("tasks", {}).items():
|
||||||
task_priorities[FlightType(task_name)] = priority
|
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]):
|
for variant in data.get("variants", [aircraft.id]):
|
||||||
yield AircraftType(
|
yield AircraftType(
|
||||||
dcs_unit_type=aircraft,
|
dcs_unit_type=aircraft,
|
||||||
|
|||||||
@ -56,7 +56,7 @@ class AircraftBehavior:
|
|||||||
self.configure_cas(group, flight)
|
self.configure_cas(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 == FlightType.SEAD:
|
elif self.task in [FlightType.SEAD, FlightType.SEAD_SWEEP]:
|
||||||
self.configure_sead(group, flight)
|
self.configure_sead(group, flight)
|
||||||
elif self.task == FlightType.SEAD_ESCORT:
|
elif self.task == FlightType.SEAD_ESCORT:
|
||||||
self.configure_sead_escort(group, flight)
|
self.configure_sead_escort(group, flight)
|
||||||
|
|||||||
27
game/missiongenerator/aircraft/waypoints/seadsweepingress.py
Normal file
27
game/missiongenerator/aircraft/waypoints/seadsweepingress.py
Normal 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],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
@ -37,6 +37,7 @@ from .racetrack import RaceTrackBuilder
|
|||||||
from .racetrackend import RaceTrackEndBuilder
|
from .racetrackend import RaceTrackEndBuilder
|
||||||
from .refuel import RefuelPointBuilder
|
from .refuel import RefuelPointBuilder
|
||||||
from .seadingress import SeadIngressBuilder
|
from .seadingress import SeadIngressBuilder
|
||||||
|
from .seadsweepingress import SeadSweepIngressBuilder
|
||||||
from .splitpoint import SplitPointBuilder
|
from .splitpoint import SplitPointBuilder
|
||||||
from .strikeingress import StrikeIngressBuilder
|
from .strikeingress import StrikeIngressBuilder
|
||||||
from .sweepingress import SweepIngressBuilder
|
from .sweepingress import SweepIngressBuilder
|
||||||
@ -126,6 +127,7 @@ class WaypointGenerator:
|
|||||||
FlightWaypointType.INGRESS_OCA_AIRCRAFT: OcaAircraftIngressBuilder,
|
FlightWaypointType.INGRESS_OCA_AIRCRAFT: OcaAircraftIngressBuilder,
|
||||||
FlightWaypointType.INGRESS_OCA_RUNWAY: OcaRunwayIngressBuilder,
|
FlightWaypointType.INGRESS_OCA_RUNWAY: OcaRunwayIngressBuilder,
|
||||||
FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder,
|
FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder,
|
||||||
|
FlightWaypointType.INGRESS_SEAD_SWEEP: SeadSweepIngressBuilder,
|
||||||
FlightWaypointType.INGRESS_STRIKE: StrikeIngressBuilder,
|
FlightWaypointType.INGRESS_STRIKE: StrikeIngressBuilder,
|
||||||
FlightWaypointType.INGRESS_SWEEP: SweepIngressBuilder,
|
FlightWaypointType.INGRESS_SWEEP: SweepIngressBuilder,
|
||||||
FlightWaypointType.JOIN: JoinPointBuilder,
|
FlightWaypointType.JOIN: JoinPointBuilder,
|
||||||
|
|||||||
@ -38,6 +38,7 @@ class MissionTarget:
|
|||||||
FlightType.ESCORT,
|
FlightType.ESCORT,
|
||||||
FlightType.TARCAP,
|
FlightType.TARCAP,
|
||||||
FlightType.SEAD_ESCORT,
|
FlightType.SEAD_ESCORT,
|
||||||
|
FlightType.SEAD_SWEEP,
|
||||||
FlightType.SWEEP,
|
FlightType.SWEEP,
|
||||||
# TODO: FlightType.ELINT,
|
# TODO: FlightType.ELINT,
|
||||||
# TODO: FlightType.EWAR,
|
# TODO: FlightType.EWAR,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user