Add BAI missions.

BAI is used for attacking ground vehicles as opposed to buildings like
strike does, and not air defenses like DEAD does. Unlike strike, BAI is
tolerant of moving targets.

Fixes https://github.com/Khopa/dcs_liberation/issues/216
This commit is contained in:
Dan Albert 2020-11-16 21:05:53 -08:00
parent 8bd00bf450
commit 9fb33526a7
6 changed files with 80 additions and 11 deletions

View File

@ -2,6 +2,7 @@
# Features/Improvements # Features/Improvements
* **[Flight Planner]** Added fighter sweep missions. * **[Flight Planner]** Added fighter sweep missions.
* **[Flight Planner]** Added BAI missions.
* **[Flight Planner]** Differentiated BARCAP and TARCAP. TARCAP is now for hostile areas and will arrive before the package. * **[Flight Planner]** Differentiated BARCAP and TARCAP. TARCAP is now for hostile areas and will arrive before the package.
* **[Modding]** Possible to setup liveries overrides for factions * **[Modding]** Possible to setup liveries overrides for factions

View File

@ -1279,6 +1279,7 @@ class PydcsWaypointBuilder:
package: Package, flight: Flight, package: Package, flight: Flight,
mission: Mission) -> PydcsWaypointBuilder: mission: Mission) -> PydcsWaypointBuilder:
builders = { builders = {
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
FlightWaypointType.INGRESS_CAS: CasIngressBuilder, FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder, FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,
FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder, FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder,
@ -1339,6 +1340,32 @@ class HoldPointBuilder(PydcsWaypointBuilder):
return waypoint return waypoint
class BaiIngressBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint:
waypoint = super().build()
target_group = self.package.target
if isinstance(target_group, TheaterGroundObject):
# Match search is used due to TheaterGroundObject.name not matching
# the Mission group name because of SkyNet prefixes.
tgroup = self.mission.find_group(target_group.group_name,
search="match")
if tgroup is not None:
task = AttackGroup(tgroup.id, weapon_type=WeaponType.Auto)
task.params["attackQtyLimit"] = False
task.params["directionEnabled"] = False
task.params["altitudeEnabled"] = False
task.params["groupAttack"] = True
waypoint.tasks.append(task)
else:
logging.error("Could not find group for BAI mission %s",
target_group.group_name)
else:
logging.error("Unexpected target type for BAI mission: %s",
target_group.__class__.__name__)
return waypoint
class CasIngressBuilder(PydcsWaypointBuilder): class CasIngressBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint: def build(self) -> MovingPoint:
waypoint = super().build() waypoint = super().build()
@ -1372,14 +1399,16 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
target_group = self.package.target target_group = self.package.target
if isinstance(target_group, TheaterGroundObject): if isinstance(target_group, TheaterGroundObject):
tgroup = self.mission.find_group(target_group.group_name, search="match") # Match search is used due to TheaterGroundObject.name not matching # Match search is used due to TheaterGroundObject.name not matching
if tgroup is not None: # the Mission group name because of SkyNet prefixes. # the Mission group name because of SkyNet prefixes.
task = AttackGroup(tgroup.id) tgroup = self.mission.find_group(target_group.group_name,
search="match")
if tgroup is not None:
task = AttackGroup(tgroup.id, weapon_type=WeaponType.Guided)
task.params["expend"] = "All" task.params["expend"] = "All"
task.params["attackQtyLimit"] = False task.params["attackQtyLimit"] = False
task.params["directionEnabled"] = False task.params["directionEnabled"] = False
task.params["altitudeEnabled"] = False task.params["altitudeEnabled"] = False
task.params["weaponType"] = 268402702 # Guided Weapons
task.params["groupAttack"] = True task.params["groupAttack"] = True
waypoint.tasks.append(task) waypoint.tasks.append(task)
else: else:
@ -1394,8 +1423,11 @@ class SeadIngressBuilder(PydcsWaypointBuilder):
target_group = self.package.target target_group = self.package.target
if isinstance(target_group, TheaterGroundObject): if isinstance(target_group, TheaterGroundObject):
tgroup = self.mission.find_group(target_group.group_name, search="match") # Match search is used due to TheaterGroundObject.name not matching # Match search is used due to TheaterGroundObject.name not matching
if tgroup is not None: # the Mission group name because of SkyNet prefixes. # the Mission group name because of SkyNet prefixes.
tgroup = self.mission.find_group(target_group.group_name,
search="match")
if tgroup is not None:
waypoint.add_task(EngageTargetsInZone( waypoint.add_task(EngageTargetsInZone(
position=tgroup.position, position=tgroup.position,
radius=nm_to_meter(30), radius=nm_to_meter(30),

View File

@ -64,6 +64,7 @@ class FlightWaypointType(Enum):
INGRESS_ESCORT = 19 INGRESS_ESCORT = 19
INGRESS_DEAD = 20 INGRESS_DEAD = 20
INGRESS_SWEEP = 21 INGRESS_SWEEP = 21
INGRESS_BAI = 22
class FlightWaypoint: class FlightWaypoint:

View File

@ -629,7 +629,9 @@ class FlightPlanBuilder:
custom_targets: Optional[List[Unit]]) -> FlightPlan: custom_targets: Optional[List[Unit]]) -> FlightPlan:
# TODO: Flesh out mission types. # TODO: Flesh out mission types.
task = flight.flight_type task = flight.flight_type
if task == FlightType.BARCAP: if task == FlightType.BAI:
return self.generate_bai(flight)
elif task == FlightType.BARCAP:
return self.generate_barcap(flight) return self.generate_barcap(flight)
elif task == FlightType.CAS: elif task == FlightType.CAS:
return self.generate_cas(flight) return self.generate_cas(flight)
@ -702,6 +704,23 @@ class FlightPlanBuilder:
return self.strike_flightplan(flight, location, targets) return self.strike_flightplan(flight, location, targets)
def generate_bai(self, flight: Flight) -> StrikeFlightPlan:
"""Generates a BAI flight plan.
Args:
flight: The flight to generate the flight plan for.
"""
location = self.package.target
if not isinstance(location, TheaterGroundObject):
raise InvalidObjectiveLocation(flight.flight_type, location)
targets: List[StrikeTarget] = []
for group in location.groups:
targets.append(StrikeTarget(f"{group.id}", group))
return self.strike_flightplan(flight, location, targets)
def generate_barcap(self, flight: Flight) -> BarCapFlightPlan: def generate_barcap(self, flight: Flight) -> BarCapFlightPlan:
"""Generate a BARCAP flight at a given location. """Generate a BARCAP flight at a given location.
@ -965,7 +984,9 @@ class FlightPlanBuilder:
@staticmethod @staticmethod
def target_waypoint(flight: Flight, builder: WaypointBuilder, def target_waypoint(flight: Flight, builder: WaypointBuilder,
target: StrikeTarget) -> FlightWaypoint: target: StrikeTarget) -> FlightWaypoint:
if flight.flight_type == FlightType.DEAD: if flight.flight_type == FlightType.BAI:
return builder.bai_group(target)
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 == FlightType.SEAD:
return builder.sead_point(target) return builder.sead_point(target)
@ -1068,7 +1089,6 @@ class FlightPlanBuilder:
assert self.package.waypoints is not None assert self.package.waypoints is not None
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine, builder = WaypointBuilder(self.game.conditions, flight, self.doctrine,
targets) targets)
# sead_types = {FlightType.DEAD, FlightType.SEAD}
if flight.flight_type is FlightType.SEAD: if flight.flight_type is FlightType.SEAD:
ingress = builder.ingress_sead(self.package.waypoints.ingress, ingress = builder.ingress_sead(self.package.waypoints.ingress,
location) location)
@ -1076,6 +1096,9 @@ class FlightPlanBuilder:
elif flight.flight_type is FlightType.DEAD: elif flight.flight_type is FlightType.DEAD:
ingress = builder.ingress_dead(self.package.waypoints.ingress, ingress = builder.ingress_dead(self.package.waypoints.ingress,
location) location)
elif flight.flight_type is FlightType.BAI:
ingress = builder.ingress_bai(self.package.waypoints.ingress,
location)
else: else:
ingress = builder.ingress_strike(self.package.waypoints.ingress, ingress = builder.ingress_strike(self.package.waypoints.ingress,
location) location)

View File

@ -5,6 +5,7 @@ from typing import List, Optional, Tuple, Union
from dcs.mapping import Point from dcs.mapping import Point
from dcs.unit import Unit from dcs.unit import Unit
from dcs.unitgroup import VehicleGroup
from game.data.doctrine import Doctrine from game.data.doctrine import Doctrine
from game.utils import nm_to_meter from game.utils import nm_to_meter
@ -17,7 +18,7 @@ from ..runways import RunwayAssigner
@dataclass(frozen=True) @dataclass(frozen=True)
class StrikeTarget: class StrikeTarget:
name: str name: str
target: Union[TheaterGroundObject, Unit] target: Union[VehicleGroup, TheaterGroundObject, Unit]
class WaypointBuilder: class WaypointBuilder:
@ -168,6 +169,11 @@ class WaypointBuilder:
objective: MissionTarget) -> FlightWaypoint: objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_ESCORT, position, return self._ingress(FlightWaypointType.INGRESS_ESCORT, position,
objective) objective)
def ingress_bai(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_BAI, position,
objective)
def ingress_dead(self, position:Point, def ingress_dead(self, position:Point,
objective: MissionTarget) -> FlightWaypoint: objective: MissionTarget) -> FlightWaypoint:
@ -211,6 +217,9 @@ class WaypointBuilder:
waypoint.name = "EGRESS" waypoint.name = "EGRESS"
return waypoint return waypoint
def bai_group(self, target: StrikeTarget) -> FlightWaypoint:
return self._target_point(target, f"ATTACK {target.name}")
def dead_point(self, target: StrikeTarget) -> FlightWaypoint: def dead_point(self, target: StrikeTarget) -> FlightWaypoint:
return self._target_point(target, f"STRIKE {target.name}") return self._target_point(target, f"STRIKE {target.name}")

View File

@ -126,7 +126,10 @@ class TheaterGroundObject(MissionTarget):
# TODO: FlightType.TROOP_TRANSPORT # TODO: FlightType.TROOP_TRANSPORT
] ]
else: else:
yield FlightType.STRIKE yield from [
FlightType.STRIKE,
FlightType.BAI,
]
yield from super().mission_types(for_player) yield from super().mission_types(for_player)