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)