From eedb5c26a9cb7480e6f22c3c7a7bf49b9912f8ec Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Sun, 23 May 2021 12:58:16 -0700 Subject: [PATCH] Ignore non-escorted regions when planning escorts. We shouldn't consider the non-escorted parts of the flight path when checking for threats to determine if escorts should be used or not, since escorts can't help in those areas anyway. This was causing escorts to be overly requested since the bullseye is now a part of the "flight plan", but could have also triggered for divert waypoints, or for aircraft taking off in a retreat from a threatened location. --- game/threatzones.py | 18 ++++++++++++++++-- gen/flights/ai_flight_planner.py | 8 ++++++-- gen/flights/flightplan.py | 18 ++++++++++++++---- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/game/threatzones.py b/game/threatzones.py index 571e7082..475517d6 100644 --- a/game/threatzones.py +++ b/game/threatzones.py @@ -1,7 +1,7 @@ from __future__ import annotations from functools import singledispatchmethod -from typing import Optional, TYPE_CHECKING, Union +from typing import Optional, TYPE_CHECKING, Union, Iterable from dcs.mapping import Point as DcsPoint from shapely.geometry import ( @@ -16,7 +16,7 @@ from shapely.ops import nearest_points, unary_union from game.theater import ControlPoint from game.utils import Distance, meters, nautical_miles from gen.flights.closestairfields import ObjectiveDistanceCache -from gen.flights.flight import Flight +from gen.flights.flight import Flight, FlightWaypoint if TYPE_CHECKING: from game import Game @@ -71,6 +71,13 @@ class ThreatZones: LineString((self.dcs_to_shapely_point(p.position) for p in flight.points)) ) + def waypoints_threatened_by_aircraft( + self, waypoints: Iterable[FlightWaypoint] + ) -> bool: + return self.threatened_by_aircraft( + LineString((self.dcs_to_shapely_point(p.position) for p in waypoints)) + ) + @singledispatchmethod def threatened_by_air_defense(self, target) -> bool: raise NotImplementedError @@ -99,6 +106,13 @@ class ThreatZones: LineString((self.dcs_to_shapely_point(p.position) for p in flight.points)) ) + def waypoints_threatened_by_radar_sam( + self, waypoints: Iterable[FlightWaypoint] + ) -> bool: + return self.threatened_by_radar_sam( + LineString((self.dcs_to_shapely_point(p.position) for p in waypoints)) + ) + @classmethod def closest_enemy_airbase( cls, location: ControlPoint, max_distance: Distance diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 3b06481c..027230ed 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -831,9 +831,13 @@ class CoalitionMissionPlanner: def check_needed_escorts(self, builder: PackageBuilder) -> Dict[EscortType, bool]: threats = defaultdict(bool) for flight in builder.package.flights: - if self.threat_zones.threatened_by_aircraft(flight): + if self.threat_zones.waypoints_threatened_by_aircraft( + flight.flight_plan.escorted_waypoints() + ): threats[EscortType.AirToAir] = True - if self.threat_zones.threatened_by_radar_sam(flight): + if self.threat_zones.waypoints_threatened_by_radar_sam( + list(flight.flight_plan.escorted_waypoints()) + ): threats[EscortType.Sead] = True return threats diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index 5e6bc886..c2779e3f 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -198,6 +198,20 @@ class FlightPlan: def dismiss_escort_at(self) -> Optional[FlightWaypoint]: return None + def escorted_waypoints(self) -> Iterator[FlightWaypoint]: + begin = self.request_escort_at() + end = self.dismiss_escort_at() + if begin is None or end is None: + return + escorting = False + for waypoint in self.waypoints: + if waypoint == begin: + escorting = True + if escorting: + yield waypoint + if waypoint == end: + return + def takeoff_time(self) -> Optional[timedelta]: tot_waypoint = self.tot_waypoint if tot_waypoint is None: @@ -600,10 +614,6 @@ class StrikeFlightPlan(FormationFlightPlan): ) return total - @property - def mission_speed(self) -> Speed: - return GroundSpeed.for_flight(self.flight, self.ingress.alt) - @property def join_time(self) -> timedelta: travel_time = self.travel_time_between_waypoints(self.join, self.ingress)