Add runway bombing missions.

This allows planning the missions and the missions are functional, but
they will have no effect on future turns yet.
This commit is contained in:
Dan Albert
2020-11-23 01:38:27 -08:00
parent 63bdbebcaa
commit 9394ed663a
10 changed files with 237 additions and 87 deletions

View File

@@ -41,6 +41,7 @@ from dcs.task import (
AntishipStrike,
AttackGroup,
Bombing,
BombingRunway,
CAP,
CAS,
ControlledTask,
@@ -55,6 +56,7 @@ from dcs.task import (
OptReactOnThreat,
OptRestrictJettison,
OrbitAction,
RunwayAttack,
SEAD,
StartCommand,
Targets,
@@ -1169,6 +1171,18 @@ class AircraftConflictGenerator:
roe=OptROE.Values.OpenFire,
restrict_jettison=True)
def configure_runway_attack(
self, group: FlyingGroup, package: Package, flight: Flight,
dynamic_runways: Dict[str, RunwayData]) -> None:
group.task = RunwayAttack.name
self._setup_group(group, RunwayAttack, package, flight,
dynamic_runways)
self.configure_behavior(
group,
react_on_threat=OptReactOnThreat.Values.EvadeFire,
roe=OptROE.Values.OpenFire,
restrict_jettison=True)
def configure_escort(self, group: FlyingGroup, package: Package,
flight: Flight,
dynamic_runways: Dict[str, RunwayData]) -> None:
@@ -1206,6 +1220,9 @@ class AircraftConflictGenerator:
self.configure_anti_ship(group, package, flight, dynamic_runways)
elif flight_type == FlightType.ESCORT:
self.configure_escort(group, package, flight, dynamic_runways)
elif flight_type == FlightType.RUNWAY_ATTACK:
self.configure_runway_attack(group, package, flight,
dynamic_runways)
else:
self.configure_unknown_task(group, flight)
@@ -1345,6 +1362,7 @@ class PydcsWaypointBuilder:
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,
FlightWaypointType.INGRESS_RUNWAY_BOMBING: RunwayBombingIngressBuilder,
FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder,
FlightWaypointType.INGRESS_STRIKE: StrikeIngressBuilder,
FlightWaypointType.JOIN: JoinPointBuilder,
@@ -1480,6 +1498,22 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
return waypoint
class RunwayBombingIngressBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint:
waypoint = super().build()
target = self.package.target
if not isinstance(target, Airfield):
logging.error(
"Unexpected target type for runway bombing mission: %s",
target.__class__.__name__)
return waypoint
waypoint.tasks.append(
BombingRunway(airport_id=target.airport.id, group_attack=True))
return waypoint
class SeadIngressBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint:
waypoint = super().build()

View File

@@ -44,6 +44,8 @@ from gen.flights.ai_flight_planner_db import (
CAP_PREFERRED,
CAS_CAPABLE,
CAS_PREFERRED,
RUNWAY_ATTACK_CAPABLE,
RUNWAY_ATTACK_PREFERRED,
SEAD_CAPABLE,
SEAD_PREFERRED,
STRIKE_CAPABLE,
@@ -160,6 +162,8 @@ class AircraftAllocator:
return CAS_PREFERRED
elif task in (FlightType.DEAD, FlightType.SEAD):
return SEAD_PREFERRED
elif task == FlightType.RUNWAY_ATTACK:
return RUNWAY_ATTACK_PREFERRED
elif task == FlightType.STRIKE:
return STRIKE_PREFERRED
elif task == FlightType.ESCORT:
@@ -180,6 +184,8 @@ class AircraftAllocator:
return CAS_CAPABLE
elif task in (FlightType.DEAD, FlightType.SEAD):
return SEAD_CAPABLE
elif task == FlightType.RUNWAY_ATTACK:
return RUNWAY_ATTACK_CAPABLE
elif task == FlightType.STRIKE:
return STRIKE_CAPABLE
elif task == FlightType.ESCORT:

View File

@@ -484,6 +484,13 @@ ANTISHIP_PREFERRED = [
Su_24M,
]
RUNWAY_ATTACK_PREFERRED = [
JF_17,
M_2000C,
]
RUNWAY_ATTACK_CAPABLE = STRIKE_CAPABLE
DRONES = [
MQ_9_Reaper,
RQ_1A_Predator,

View File

@@ -39,6 +39,7 @@ class FlightType(Enum):
EWAR = 16
SWEEP = 17
RUNWAY_ATTACK = 18
class FlightWaypointType(Enum):
@@ -66,6 +67,7 @@ class FlightWaypointType(Enum):
INGRESS_SWEEP = 21
INGRESS_BAI = 22
DIVERT = 23
INGRESS_RUNWAY_BOMBING = 24
class FlightWaypoint:

View File

@@ -20,6 +20,7 @@ from dcs.unit import Unit
from game.data.doctrine import Doctrine
from game.theater import (
Airfield,
ControlPoint,
FrontLine,
MissionTarget,
@@ -650,6 +651,8 @@ class FlightPlanBuilder:
return self.generate_dead(flight, custom_targets)
elif task == FlightType.ESCORT:
return self.generate_escort(flight)
elif task == FlightType.RUNWAY_ATTACK:
return self.generate_runway_attack(flight)
elif task == FlightType.SEAD:
return self.generate_sead(flight, custom_targets)
elif task == FlightType.STRIKE:
@@ -713,7 +716,9 @@ class FlightPlanBuilder:
targets.append(StrikeTarget(building.category, building))
return self.strike_flightplan(flight, location, targets)
return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_STRIKE,
targets)
def generate_bai(self, flight: Flight) -> StrikeFlightPlan:
"""Generates a BAI flight plan.
@@ -731,7 +736,8 @@ class FlightPlanBuilder:
targets.append(
StrikeTarget(f"{group.name} at {location.name}", group))
return self.strike_flightplan(flight, location, targets)
return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_BAI, targets)
def generate_anti_ship(self, flight: Flight) -> StrikeFlightPlan:
"""Generates an anti-ship flight plan.
@@ -756,7 +762,8 @@ class FlightPlanBuilder:
targets.append(
StrikeTarget(f"{group.name} at {location.name}", group))
return self.strike_flightplan(flight, location, targets)
return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_BAI, targets)
def generate_barcap(self, flight: Flight) -> BarCapFlightPlan:
"""Generate a BARCAP flight at a given location.
@@ -942,7 +949,25 @@ class FlightPlanBuilder:
for target in custom_targets:
targets.append(StrikeTarget(location.name, target))
return self.strike_flightplan(flight, location, targets)
return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_DEAD, targets)
def generate_runway_attack(self, flight: Flight) -> StrikeFlightPlan:
"""Generate a runway attack flight plan at a given location.
Args:
flight: The flight to generate the flight plan for.
"""
location = self.package.target
if not isinstance(location, Airfield):
logging.exception(
f"Invalid Objective Location for runway bombing flight "
f"{flight=} at {location=}.")
raise InvalidObjectiveLocation(flight.flight_type, location)
return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_RUNWAY_BOMBING)
def generate_sead(self, flight: Flight,
custom_targets: Optional[List[Unit]]) -> StrikeFlightPlan:
@@ -963,7 +988,8 @@ class FlightPlanBuilder:
for target in custom_targets:
targets.append(StrikeTarget(location.name, target))
return self.strike_flightplan(flight, location, targets)
return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_SEAD, targets)
def generate_escort(self, flight: Flight) -> StrikeFlightPlan:
assert self.package.waypoints is not None
@@ -1012,7 +1038,8 @@ class FlightPlanBuilder:
flight=flight,
patrol_duration=self.doctrine.cas_duration,
takeoff=builder.takeoff(flight.departure),
patrol_start=builder.ingress_cas(ingress, location),
patrol_start=builder.ingress(FlightWaypointType.INGRESS_CAS,
ingress, location),
target=builder.cas(center),
patrol_end=builder.egress(egress, location),
land=builder.land(flight.arrival),
@@ -1101,23 +1128,11 @@ class FlightPlanBuilder:
def strike_flightplan(
self, flight: Flight, location: MissionTarget,
ingress_type: FlightWaypointType,
targets: Optional[List[StrikeTarget]] = None) -> StrikeFlightPlan:
assert self.package.waypoints is not None
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine,
targets)
if flight.flight_type is FlightType.SEAD:
ingress = builder.ingress_sead(self.package.waypoints.ingress,
location)
elif flight.flight_type is FlightType.DEAD:
ingress = builder.ingress_dead(self.package.waypoints.ingress,
location)
elif flight.flight_type in {FlightType.ANTISHIP, FlightType.BAI}:
ingress = builder.ingress_bai(self.package.waypoints.ingress,
location)
else:
ingress = builder.ingress_strike(self.package.waypoints.ingress,
location)
target_waypoints: List[FlightWaypoint] = []
if targets is not None:
@@ -1134,7 +1149,8 @@ class FlightPlanBuilder:
takeoff=builder.takeoff(flight.departure),
hold=builder.hold(self._hold_point(flight)),
join=builder.join(self.package.waypoints.join),
ingress=ingress,
ingress=builder.ingress(ingress_type,
self.package.waypoints.ingress, location),
targets=target_waypoints,
egress=builder.egress(self.package.waypoints.egress, location),
split=builder.split(self.package.waypoints.split),

View File

@@ -173,38 +173,8 @@ class WaypointBuilder:
waypoint.name = "SPLIT"
return waypoint
def ingress_cas(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_CAS, position,
objective)
def ingress_escort(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_ESCORT, position,
objective)
def ingress_bai(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_BAI, position,
objective)
def ingress_dead(self, position:Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_DEAD, position,
objective)
def ingress_sead(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_SEAD, position,
objective)
def ingress_strike(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_STRIKE, position,
objective)
def _ingress(self, ingress_type: FlightWaypointType, position: Point,
objective: MissionTarget) -> FlightWaypoint:
def ingress(self, ingress_type: FlightWaypointType, position: Point,
objective: MissionTarget) -> FlightWaypoint:
waypoint = FlightWaypoint(
ingress_type,
position.x,
@@ -415,8 +385,8 @@ class WaypointBuilder:
# description in gen.aircraft.JoinPointBuilder), so instead we give
# the escort flights a flight plan including the ingress point, target
# area, and egress point.
ingress = self._ingress(FlightWaypointType.INGRESS_ESCORT, ingress,
target)
ingress = self.ingress(FlightWaypointType.INGRESS_ESCORT, ingress,
target)
waypoint = FlightWaypoint(
FlightWaypointType.TARGET_GROUP_LOC,