Dan Albert cb3bf56d84 Add a real CAS ingress point.
Putting the ingress point directly on one end of the FLOT means that AI
flights won't start searching and engaging targets until they reach that
point. If the front line has advanced toward the flight's departure
airfield, it might overfly targets on its way to the IP.

Instead, place an IP for CAS the same way we place any other IP. The AI
will fly to that and start searching from there.

This also:

* Removes the midpoint waypoint, since it didn't serve any real purpose
* Names the FLOT boundary waypoints for what they actually are

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2231.
2023-08-10 00:47:13 -07:00

84 lines
2.6 KiB
Python

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any, Generic, TYPE_CHECKING, TypeVar
from game.navmesh import NavMeshError
from .flightplan import FlightPlan, Layout
from .planningerror import PlanningError
from ..packagewaypoints import PackageWaypoints
if TYPE_CHECKING:
from game.coalition import Coalition
from game.data.doctrine import Doctrine
from game.theater import ConflictTheater
from game.threatzones import ThreatZones
from ..flight import Flight
from ..package import Package
FlightPlanT = TypeVar("FlightPlanT", bound=FlightPlan[Any])
LayoutT = TypeVar("LayoutT", bound=Layout)
class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
def __init__(self, flight: Flight) -> None:
self.flight = flight
self._flight_plan: FlightPlanT | None = None
def get_or_build(self) -> FlightPlanT:
if self._flight_plan is None:
self.regenerate()
assert self._flight_plan is not None
return self._flight_plan
def regenerate(self, dump_debug_info: bool = False) -> None:
try:
self._generate_package_waypoints_if_needed(dump_debug_info)
self._flight_plan = self.build(dump_debug_info)
except NavMeshError as ex:
color = "blue" if self.flight.squadron.player else "red"
raise PlanningError(
f"Could not plan {color} {self.flight.flight_type.value} from "
f"{self.flight.departure} to {self.package.target}"
) from ex
def _generate_package_waypoints_if_needed(self, dump_debug_info: bool) -> None:
# Package waypoints are only valid for offensive missions. Skip this if the
# target is friendly.
if self.package.target.is_friendly(self.is_player):
return
if self.package.waypoints is None or dump_debug_info:
self.package.waypoints = PackageWaypoints.create(
self.package, self.coalition, dump_debug_info
)
@property
def theater(self) -> ConflictTheater:
return self.flight.departure.theater
@abstractmethod
def build(self, dump_debug_info: bool = False) -> FlightPlanT:
...
@property
def package(self) -> Package:
return self.flight.package
@property
def coalition(self) -> Coalition:
return self.flight.coalition
@property
def is_player(self) -> bool:
return self.coalition.player
@property
def doctrine(self) -> Doctrine:
return self.coalition.doctrine
@property
def threat_zones(self) -> ThreatZones:
return self.coalition.opponent.threat_zone