From 9fb33526a7d6bd8d007a850b5a0e1ddf9ecc1dba Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 16 Nov 2020 21:05:53 -0800 Subject: [PATCH] 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 --- changelog.md | 1 + gen/aircraft.py | 44 +++++++++++++++++++++++++++++----- gen/flights/flight.py | 1 + gen/flights/flightplan.py | 29 +++++++++++++++++++--- gen/flights/waypointbuilder.py | 11 ++++++++- theater/theatergroundobject.py | 5 +++- 6 files changed, 80 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 03821d5a..8c55e2c0 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,7 @@ # Features/Improvements * **[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. * **[Modding]** Possible to setup liveries overrides for factions diff --git a/gen/aircraft.py b/gen/aircraft.py index 03a9e619..0872f4d6 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -1279,6 +1279,7 @@ class PydcsWaypointBuilder: package: Package, flight: Flight, mission: Mission) -> PydcsWaypointBuilder: builders = { + FlightWaypointType.INGRESS_BAI: BaiIngressBuilder, FlightWaypointType.INGRESS_CAS: CasIngressBuilder, FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder, FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder, @@ -1339,6 +1340,32 @@ class HoldPointBuilder(PydcsWaypointBuilder): 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): def build(self) -> MovingPoint: waypoint = super().build() @@ -1372,14 +1399,16 @@ class DeadIngressBuilder(PydcsWaypointBuilder): target_group = self.package.target 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 - if tgroup is not None: # the Mission group name because of SkyNet prefixes. - task = AttackGroup(tgroup.id) + # 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.Guided) task.params["expend"] = "All" task.params["attackQtyLimit"] = False task.params["directionEnabled"] = False task.params["altitudeEnabled"] = False - task.params["weaponType"] = 268402702 # Guided Weapons task.params["groupAttack"] = True waypoint.tasks.append(task) else: @@ -1394,8 +1423,11 @@ class SeadIngressBuilder(PydcsWaypointBuilder): target_group = self.package.target 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 - if tgroup is not None: # the Mission group name because of SkyNet prefixes. + # 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: waypoint.add_task(EngageTargetsInZone( position=tgroup.position, radius=nm_to_meter(30), diff --git a/gen/flights/flight.py b/gen/flights/flight.py index a19d362c..4ddc4003 100644 --- a/gen/flights/flight.py +++ b/gen/flights/flight.py @@ -64,6 +64,7 @@ class FlightWaypointType(Enum): INGRESS_ESCORT = 19 INGRESS_DEAD = 20 INGRESS_SWEEP = 21 + INGRESS_BAI = 22 class FlightWaypoint: diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index c905d993..89e23115 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -629,7 +629,9 @@ class FlightPlanBuilder: custom_targets: Optional[List[Unit]]) -> FlightPlan: # TODO: Flesh out mission types. 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) elif task == FlightType.CAS: return self.generate_cas(flight) @@ -702,6 +704,23 @@ class FlightPlanBuilder: 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: """Generate a BARCAP flight at a given location. @@ -965,7 +984,9 @@ class FlightPlanBuilder: @staticmethod def target_waypoint(flight: Flight, builder: WaypointBuilder, 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) elif flight.flight_type == FlightType.SEAD: return builder.sead_point(target) @@ -1068,7 +1089,6 @@ class FlightPlanBuilder: assert self.package.waypoints is not None builder = WaypointBuilder(self.game.conditions, flight, self.doctrine, targets) - # sead_types = {FlightType.DEAD, FlightType.SEAD} if flight.flight_type is FlightType.SEAD: ingress = builder.ingress_sead(self.package.waypoints.ingress, location) @@ -1076,6 +1096,9 @@ class FlightPlanBuilder: elif flight.flight_type is FlightType.DEAD: ingress = builder.ingress_dead(self.package.waypoints.ingress, location) + elif flight.flight_type is FlightType.BAI: + ingress = builder.ingress_bai(self.package.waypoints.ingress, + location) else: ingress = builder.ingress_strike(self.package.waypoints.ingress, location) diff --git a/gen/flights/waypointbuilder.py b/gen/flights/waypointbuilder.py index dd82f1aa..d22d4324 100644 --- a/gen/flights/waypointbuilder.py +++ b/gen/flights/waypointbuilder.py @@ -5,6 +5,7 @@ from typing import List, Optional, Tuple, Union from dcs.mapping import Point from dcs.unit import Unit +from dcs.unitgroup import VehicleGroup from game.data.doctrine import Doctrine from game.utils import nm_to_meter @@ -17,7 +18,7 @@ from ..runways import RunwayAssigner @dataclass(frozen=True) class StrikeTarget: name: str - target: Union[TheaterGroundObject, Unit] + target: Union[VehicleGroup, TheaterGroundObject, Unit] class WaypointBuilder: @@ -168,6 +169,11 @@ class WaypointBuilder: 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: @@ -211,6 +217,9 @@ class WaypointBuilder: waypoint.name = "EGRESS" 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: return self._target_point(target, f"STRIKE {target.name}") diff --git a/theater/theatergroundobject.py b/theater/theatergroundobject.py index ff3840cf..5d44d198 100644 --- a/theater/theatergroundobject.py +++ b/theater/theatergroundobject.py @@ -126,7 +126,10 @@ class TheaterGroundObject(MissionTarget): # TODO: FlightType.TROOP_TRANSPORT ] else: - yield FlightType.STRIKE + yield from [ + FlightType.STRIKE, + FlightType.BAI, + ] yield from super().mission_types(for_player)