diff --git a/game/ato/flight.py b/game/ato/flight.py index 3c76eb84..c12d0029 100644 --- a/game/ato/flight.py +++ b/game/ato/flight.py @@ -63,6 +63,7 @@ class Flight(SidcDescribable): self.start_type = start_type self.use_custom_loadout = False self.custom_name = custom_name + self.group_id: int = 0 # Only used by transport missions. self.cargo = cargo diff --git a/game/ato/package.py b/game/ato/package.py index 06c4b9cf..68b82daa 100644 --- a/game/ato/package.py +++ b/game/ato/package.py @@ -129,6 +129,16 @@ class Package: if not self.flights: self.waypoints = None + @property + def primary_flight(self) -> Optional[Flight]: + task = self.primary_task + if not task: + return None + primaries = [x for x in self.flights if x.flight_type == task] + if len(primaries) > 0: + return primaries[0] + return None + @property def primary_task(self) -> Optional[FlightType]: if not self.flights: diff --git a/game/missiongenerator/aircraft/aircraftbehavior.py b/game/missiongenerator/aircraft/aircraftbehavior.py index 89d3eeeb..445b1643 100644 --- a/game/missiongenerator/aircraft/aircraftbehavior.py +++ b/game/missiongenerator/aircraft/aircraftbehavior.py @@ -8,6 +8,7 @@ from dcs.task import ( CAP, CAS, EPLRS, + Escort, FighterSweep, GroundAttack, Nothing, @@ -18,7 +19,7 @@ from dcs.task import ( OptRestrictJettison, Refueling, RunwayAttack, - Transport, + Transport, SEAD, ) from dcs.unitgroup import FlyingGroup @@ -265,7 +266,7 @@ class AircraftBehavior: # Escort groups are actually given the CAP task so they can perform the # Search Then Engage task, which we have to use instead of the Escort # task for the reasons explained in JoinPointBuilder. - group.task = CAP.name + group.task = Escort.name self.configure_behavior( flight, group, roe=OptROE.Values.OpenFire, restrict_jettison=True ) @@ -274,7 +275,7 @@ class AircraftBehavior: # CAS is able to perform all the same tasks as SEAD using a superset of the # available aircraft, and F-14s are not able to be SEAD despite having TALDs. # https://forums.eagle.ru/topic/272112-cannot-assign-f-14-to-sead/ - group.task = CAS.name + group.task = SEAD.name self.configure_behavior( flight, group, diff --git a/game/missiongenerator/aircraft/flightgroupspawner.py b/game/missiongenerator/aircraft/flightgroupspawner.py index aa124066..9a21fbf3 100644 --- a/game/missiongenerator/aircraft/flightgroupspawner.py +++ b/game/missiongenerator/aircraft/flightgroupspawner.py @@ -74,8 +74,14 @@ class FlightGroupSpawner: self.flight.state.is_waiting_for_start or self.flight.state.spawn_type is not StartType.IN_FLIGHT ): - return self.generate_flight_at_departure() - return self.generate_mid_mission() + grp = self.generate_flight_at_departure() + # grp.id = id(self.flight) + self.flight.group_id = grp.id + return grp + grp = self.generate_mid_mission() + # grp.id = id(self.flight) + self.flight.group_id = grp.id + return grp def create_idle_aircraft(self) -> FlyingGroup[Any]: airport = self.flight.squadron.location.dcs_airport diff --git a/game/missiongenerator/aircraft/waypoints/joinpoint.py b/game/missiongenerator/aircraft/waypoints/joinpoint.py index a30f426d..0a22993c 100644 --- a/game/missiongenerator/aircraft/waypoints/joinpoint.py +++ b/game/missiongenerator/aircraft/waypoints/joinpoint.py @@ -1,13 +1,15 @@ -from typing import List, Type +import random +from typing import List, Type, Optional from dcs.point import MovingPoint from dcs.task import ( ControlledTask, EngageTargets, + EscortTaskAction, OptECMUsing, OptFormation, TargetType, - Targets, + Targets, GoToWaypoint, SwitchWaypoint, ) from game.ato import FlightType @@ -19,42 +21,69 @@ from .pydcswaypointbuilder import PydcsWaypointBuilder class JoinPointBuilder(PydcsWaypointBuilder): def add_tasks(self, waypoint: MovingPoint) -> None: if self.flight.flight_type == FlightType.ESCORT: - self.configure_escort_tasks( - waypoint, - [ - Targets.All.Air.Planes.Fighters, - Targets.All.Air.Planes.MultiroleFighters, - ], + # self.configure_escort_tasks( + # waypoint, + # [ + # Targets.All.Air.Planes.Fighters, + # Targets.All.Air.Planes.MultiroleFighters, + # ], + # ) + + waypoint.tasks.append(OptFormation.finger_four_open()) + waypoint.tasks.append(SwitchWaypoint(3, 7)) + + rx = (random.random() + 0.1) * 500 * random.choice([-1, 1]) + ry = (random.random() + 0.1) * 500 * random.choice([-1, 1]) + rz = (random.random() + 0.1) * 500 * random.choice([-1, 1]) + pos = {"x": rx, "y": ry, "z": rz} + targets = [ + Targets.All.Air.Planes.Fighters.id, + Targets.All.Air.Planes.MultiroleFighters.id, + ] + waypoint.tasks.append( + EscortTaskAction( + group_id=self.package.primary_flight.group_id, + engagement_max_dist=int(nautical_miles(40).meters), + lastwpt=6, + targets=targets, + position=pos) ) - - if self.flight.count < 4: - waypoint.tasks.append(OptFormation.line_abreast_open()) - else: - waypoint.tasks.append(OptFormation.spread_four_open()) - elif self.flight.flight_type == FlightType.SEAD_ESCORT: - if isinstance(self.flight.package.target, NavalControlPoint): - self.configure_escort_tasks( - waypoint, - [ - Targets.All.Naval, - Targets.All.GroundUnits.AirDefence.AAA.SAMRelated, - ], - ) - else: - self.configure_escort_tasks( - waypoint, [Targets.All.GroundUnits.AirDefence.AAA.SAMRelated] - ) + # if isinstance(self.flight.package.target, NavalControlPoint): + # self.configure_escort_tasks( + # waypoint, + # [ + # Targets.All.Naval, + # Targets.All.GroundUnits.AirDefence.AAA.SAMRelated, + # ], + # ) + # else: + # self.configure_escort_tasks( + # waypoint, [Targets.All.GroundUnits.AirDefence.AAA.SAMRelated] + # ) # Let the AI use ECM to preemptively defend themselves. ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar) waypoint.tasks.append(ecm_option) - if self.flight.count < 4: - waypoint.tasks.append(OptFormation.line_abreast_open()) - else: - waypoint.tasks.append(OptFormation.spread_four_open()) + waypoint.tasks.append(OptFormation.finger_four_open()) + waypoint.tasks.append(SwitchWaypoint(3, 7)) + rx = (random.random() + 0.1) * 500 * random.choice([-1, 1]) + ry = (random.random() + 0.1) * 500 * random.choice([-1, 1]) + rz = (random.random() + 0.1) * 500 * random.choice([-1, 1]) + pos = {"x": rx, "y": ry, "z": rz} + targets = [Targets.All.GroundUnits.AirDefence.AAA.SAMRelated.id] + if isinstance(self.flight.package.target, NavalControlPoint): + targets.append(Targets.All.Naval.id) + waypoint.tasks.append( + EscortTaskAction( + group_id=self.package.primary_flight.group_id, + engagement_max_dist=int(nautical_miles(40).meters), + lastwpt=6, + targets=targets, + position=pos) + ) elif not self.flight.flight_type.is_air_to_air: # Capture any non A/A type to avoid issues with SPJs that use the primary radar such as the F/A-18C. # You can bully them with STT to not be able to fire radar guided missiles at you, @@ -68,7 +97,9 @@ class JoinPointBuilder(PydcsWaypointBuilder): @staticmethod def configure_escort_tasks( - waypoint: MovingPoint, target_types: List[Type[TargetType]] + waypoint: MovingPoint, + target_types: List[Type[TargetType]], + max_dist: Optional[float] = 30.0 ) -> None: # Ideally we would use the escort mission type and escort task to have # the AI automatically but the AI only escorts AI flights while they are @@ -100,7 +131,7 @@ class JoinPointBuilder(PydcsWaypointBuilder): ControlledTask( EngageTargets( # TODO: From doctrine. - max_distance=int(nautical_miles(30).meters), + max_distance=int(nautical_miles(max_dist).meters), targets=target_types, ) )