Add BAI planning against supply routes.

This currently is only supported for player flights. I have no idea how
to create an AI flight plan that won't just get them killed. AI-only BAI
missions against supply routes will warn the player on mission creation.
This commit is contained in:
Dan Albert
2021-04-19 23:09:39 -07:00
parent 2b06d8a096
commit 3f16c0378a
5 changed files with 238 additions and 2 deletions

View File

@@ -30,6 +30,7 @@ from game.theater import (
SamGroundObject,
TheaterGroundObject,
)
from game.theater.supplyroutes import SupplyRouteLink
from game.theater.theatergroundobject import EwrGroundObject
from game.utils import Distance, Speed, feet, meters, nautical_miles
from .closestairfields import ObjectiveDistanceCache
@@ -466,6 +467,25 @@ class CasFlightPlan(PatrollingFlightPlan):
return self.patrol_end
@dataclass(frozen=True)
class ConvoyInterdictionFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint
land: FlightWaypoint
divert: Optional[FlightWaypoint]
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
yield from self.nav_to
yield from [
self.patrol_start,
self.patrol_end,
]
yield from self.nav_from
yield self.land
if self.divert is not None:
yield self.divert
@dataclass(frozen=True)
class TarCapFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint
@@ -1014,7 +1034,7 @@ class FlightPlanBuilder:
hold_duration=timedelta(hours=4),
)
def generate_bai(self, flight: Flight) -> StrikeFlightPlan:
def generate_bai(self, flight: Flight) -> FlightPlan:
"""Generates a BAI flight plan.
Args:
@@ -1022,6 +1042,9 @@ class FlightPlanBuilder:
"""
location = self.package.target
if isinstance(location, SupplyRouteLink):
return self.generate_supply_route_bai(flight, location)
if not isinstance(location, TheaterGroundObject):
raise InvalidObjectiveLocation(flight.flight_type, location)
@@ -1034,6 +1057,60 @@ class FlightPlanBuilder:
flight, location, FlightWaypointType.INGRESS_BAI, targets
)
def generate_supply_route_bai(
self, flight: Flight, location: SupplyRouteLink
) -> ConvoyInterdictionFlightPlan:
"""Generates a BAI flight plan for attacking a supply route.
These flight plans are extremely rough because we do not know where the roads
are. For now they're mostly only usable by players. The flight plan includes a
start and end patrol point matching the end points of the convoy's route and a
30 minute time on station. It is up to the player to find the target.
Args:
flight: The flight to generate the flight plan for.
location: The supply route link to attack.
"""
origin = self.package_airfield()
a_dist = origin.distance_to(location.control_point_a)
b_dist = origin.distance_to(location.control_point_b)
if a_dist < b_dist:
near = location.control_point_a
far = location.control_point_b
else:
near = location.control_point_b
far = location.control_point_a
patrol_alt = meters(
random.randint(
int(self.doctrine.min_patrol_altitude.meters),
int(self.doctrine.max_patrol_altitude.meters),
)
)
builder = WaypointBuilder(flight, self.game, self.is_player)
start, end = builder.convoy_search(near, far, patrol_alt)
return ConvoyInterdictionFlightPlan(
self.package,
flight,
takeoff=builder.takeoff(flight.departure),
nav_to=builder.nav_path(
flight.departure.position, near.position, patrol_alt
),
nav_from=builder.nav_path(
far.position, flight.arrival.position, patrol_alt
),
patrol_start=start,
patrol_end=end,
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
# Not relevant because player only.
engagement_distance=meters(0),
patrol_duration=timedelta(minutes=30),
)
def generate_anti_ship(self, flight: Flight) -> StrikeFlightPlan:
"""Generates an anti-ship flight plan.

View File

@@ -349,6 +349,63 @@ class WaypointBuilder:
self.race_track_end(end, altitude),
)
@staticmethod
def convoy_search_start(
control_point: ControlPoint, altitude: Distance
) -> FlightWaypoint:
"""Creates a convoy search start waypoint.
Args:
control_point: Control point for the beginning of the search.
altitude: Altitude of the racetrack.
"""
waypoint = FlightWaypoint(
FlightWaypointType.INGRESS_BAI,
control_point.position.x,
control_point.position.y,
altitude,
)
waypoint.name = control_point.name
waypoint.description = "Beginning of convoy search area"
waypoint.pretty_name = "Search start"
return waypoint
@staticmethod
def convoy_search_end(
control_point: ControlPoint, altitude: Distance
) -> FlightWaypoint:
"""Creates a convoy search start waypoint.
Args:
control_point: Control point for the beginning of the search.
altitude: Altitude of the racetrack.
"""
waypoint = FlightWaypoint(
FlightWaypointType.EGRESS,
control_point.position.x,
control_point.position.y,
altitude,
)
waypoint.name = control_point.name
waypoint.description = "End of convoy search area"
waypoint.pretty_name = "Search end"
return waypoint
def convoy_search(
self, start: ControlPoint, end: ControlPoint, altitude: Distance
) -> Tuple[FlightWaypoint, FlightWaypoint]:
"""Creates two waypoint for a convoy search path.
Args:
start: The beginning convoy search waypoint.
end: The ending convoy search waypoint.
altitude: The convoy search altitude.
"""
return (
self.convoy_search_start(start, altitude),
self.convoy_search_end(end, altitude),
)
@staticmethod
def orbit(start: Point, altitude: Distance) -> FlightWaypoint:
"""Creates an circular orbit point.