def add_runway_waypoint( self, airport: Airport, runway: Optional[Runway] = None, distance=random.randrange(6000, 8000, 100) ) -> MovingPoint: """Adds a waypoint parallel to the given runway heading, for start or approach. :param airport: start airport object :param runway: runway for heading direction, if None first(default) airport runway will be used. :param distance: distance of the waypoint from the airport :return: MovePoint object describing the waypoint """ runway = runway if runway else airport.runways[0] mp = MovingPoint() mp.type = "Turning Point" mp.action = PointAction.TurningPoint mp.alt_type = "RADIO" mp.position = airport.position.point_from_heading( runway.heading, distance) mp.alt = 300 mp.speed = 200 / 3.6 mp.ETA_locked = False mp.properties = PointProperties() self.add_point(mp) return mp
def configure_escort_tasks(waypoint: MovingPoint, target_types: List[Type[TargetType]]) -> 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 # traveling between waypoints. When an AI flight performs an attack # (such as attacking the mission target), AI escorts wander aimlessly # until the escorted group resumes its flight plan. # # As such, we instead use the Search Then Engage task, which is an # enroute task that causes the AI to follow their flight plan and engage # enemies of the set type within a certain distance. The downside to # this approach is that AI escorts are no longer related to the group # they are escorting, aside from the fact that they fly a similar flight # plan at the same time. With Escort, the escorts will follow the # escorted group out of the area. The strike element may or may not fly # directly over the target, and they may or may not require multiple # attack runs. For the escort flight we must just assume a flight plan # for the escort to fly. If the strike flight doesn't need to overfly # the target, the escorts are needlessly going in harms way. If the # strike flight needs multiple passes, the escorts may leave before the # escorted aircraft do. # # Another possible option would be to use Search Then Engage for join -> # ingress and egress -> split, but use a Search Then Engage in Zone task # for the target area that is set to end on a flag flip that occurs when # the strike aircraft finish their attack task. # # https://forums.eagle.ru/topic/251798-options-for-alternate-ai-escort-behavior waypoint.add_task( ControlledTask( EngageTargets( # TODO: From doctrine. max_distance=int(nautical_miles(30).meters), targets=target_types, )))
def set_waypoint_tot(self, waypoint: MovingPoint, tot: timedelta) -> None: self.waypoint.tot = tot if not self._viggen_client_tot(): waypoint.ETA = int( (tot - self.elapsed_mission_time).total_seconds()) waypoint.ETA_locked = True waypoint.speed_locked = False
def add_tasks(self, waypoint: MovingPoint) -> None: if isinstance(self.flight.flight_plan, CasFlightPlan): waypoint.add_task( EngageTargetsInZone( position=self.flight.flight_plan.layout.target.position, radius=int( self.flight.flight_plan.engagement_distance.meters), targets=[ Targets.All.GroundUnits.GroundVehicles, Targets.All.GroundUnits.AirDefence.AAA, Targets.All.GroundUnits.Infantry, ], )) else: logging.error( "No CAS waypoint found. Falling back to search and engage") waypoint.add_task( EngageTargets( max_distance=int(nautical_miles(10).meters), targets=[ Targets.All.GroundUnits.GroundVehicles, Targets.All.GroundUnits.AirDefence.AAA, Targets.All.GroundUnits.Infantry, ], ))
def _import_moving_point(mission, group: unitgroup.Group, imp_group) -> unitgroup.Group: for imp_point_idx in imp_group["route"]["points"]: imp_point = imp_group["route"]["points"][imp_point_idx] point = MovingPoint() point.load_from_dict(imp_point, mission.translation) group.add_point(point) return group
def _import_moving_point(mission, group: unitgroup.Group, imp_group) -> unitgroup.Group: keys = Coalition._sort_keys(imp_group["route"]["points"]) for imp_point_idx in keys: imp_point = imp_group["route"]["points"][imp_point_idx] point = MovingPoint(Point(0, 0, mission.terrain)) point.load_from_dict(imp_point, mission.translation) group.add_point(point) return group
def land_at(self, airport: Airport) -> MovingPoint: mp = MovingPoint() mp.type = "Land" mp.action = PointAction.Landing mp.position = copy.copy(airport.position) mp.airdrome_id = airport.id mp.alt = 0 mp.speed = 0 mp.ETA_locked = False mp.properties = PointProperties() self.add_point(mp) return mp
def add_waypoint(self, pos: mapping.Point, altitude, speed=600, name: String=None) -> MovingPoint: mp = MovingPoint() mp.type = "Turning Point" mp.action = PointAction.TurningPoint mp.name = name if name else String() mp.position = mapping.Point(pos.x, pos.y) mp.alt = altitude mp.speed = speed / 3.6 mp.ETA_locked = False mp.properties = PointProperties() self.add_point(mp) return mp
def add_tasks(self, waypoint: MovingPoint) -> None: loiter = ControlledTask( OrbitAction(altitude=waypoint.alt, pattern=OrbitAction.OrbitPattern.Circle) ) if not isinstance(self.flight.flight_plan, LoiterFlightPlan): flight_plan_type = self.flight.flight_plan.__class__.__name__ logging.error( f"Cannot configure hold for for {self.flight} because " f"{flight_plan_type} does not define a push time. AI will push " "immediately and may flight unsuitable speeds." ) return push_time = self.flight.flight_plan.push_time self.waypoint.departure_time = push_time loiter.stop_after_time( int((push_time - self.elapsed_mission_time).total_seconds()) ) waypoint.add_task(loiter) waypoint.add_task(OptFormation.finger_four_close())
def configure_refueling_actions(self, waypoint: MovingPoint) -> None: waypoint.add_task(Tanker()) if self.flight.unit_type.dcs_unit_type.tacan: tanker_info = self.air_support.tankers[-1] tacan = tanker_info.tacan tacan_callsign = { "Texaco": "TEX", "Arco": "ARC", "Shell": "SHL", }.get(tanker_info.callsign) waypoint.add_task( ActivateBeaconCommand( tacan.number, tacan.band.value, tacan_callsign, bearing=True, unit_id=self.group.units[0].id, aa=True, ))
def add_waypoint(self, position: mapping.Point, speed=20) -> MovingPoint: mp = MovingPoint() mp.type = "Turning Point" mp.action = PointAction.TurningPoint mp.position = copy.copy(position) mp.speed = speed / 3.6 mp.ETA_locked = False self.add_point(mp) return mp
def add_tasks(self, waypoint: MovingPoint) -> None: flight_plan = self.flight.flight_plan if not isinstance(flight_plan, PatrollingFlightPlan): flight_plan_type = flight_plan.__class__.__name__ logging.error( f"Cannot create race track for {self.flight} because " f"{flight_plan_type} does not define a patrol.") return # NB: It's important that the engage task comes before the orbit task. # Though they're on the same waypoint, if the orbit task comes first it # is their first priority and they will not engage any targets because # they're fully focused on orbiting. If the STE task is first, they will # engage targets if available and orbit if they find nothing to shoot. if self.flight.flight_type is FlightType.REFUELING: self.configure_refueling_actions(waypoint) # TODO: Move the properties of this task into the flight plan? # CAP is the only current user of this so it's not a big deal, but might # be good to make this usable for things like BAI when we add that # later. cap_types = {FlightType.BARCAP, FlightType.TARCAP} if self.flight.flight_type in cap_types: engagement_distance = int(flight_plan.engagement_distance.meters) waypoint.tasks.append( EngageTargets(max_distance=engagement_distance, targets=[Targets.All.Air])) orbit = OrbitAction( altitude=waypoint.alt, pattern=OrbitAction.OrbitPattern.RaceTrack, speed=int(flight_plan.patrol_speed.kph), ) racetrack = ControlledTask(orbit) self.set_waypoint_tot(waypoint, flight_plan.patrol_start_time) loiter_duration = flight_plan.patrol_end_time - self.elapsed_mission_time racetrack.stop_after_time(int(loiter_duration.total_seconds())) waypoint.add_task(racetrack)
def add_waypoint(self, position: mapping.Point, move_formation: PointAction=PointAction.OffRoad, speed=32) -> MovingPoint: mp = MovingPoint() mp.type = "Turning Point" mp.action = move_formation mp.position = copy.copy(position) mp.speed = speed / 3.6 mp.ETA_locked = False self.add_point(mp) return mp
def add_tasks(self, waypoint: MovingPoint) -> None: waypoint.add_task(RefuelingTaskAction()) return super().add_tasks(waypoint)