mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Convert escort request to a waypoint property.
Another step in reducing the rigidity of FlightPlan and making it testable. There is one intentional behavior change here: escort flights no longer request escorts. That actually has a very minimal effect because these properties are only used for two things: determining if a package needs escorts or not, and determining when the TARCAP should show up and leave. Since escorts won't have been in the package when the first part happens anyway, that has no effect. The only change is that TARCAP won't show up earlier or stay later just because of a TOT offset for an escort flight.
This commit is contained in:
parent
502d37058c
commit
3862ec1b2e
@ -63,12 +63,6 @@ class CasFlightPlan(PatrollingFlightPlan[CasLayout], UiZoneDisplay):
|
||||
def combat_speed_waypoints(self) -> set[FlightWaypoint]:
|
||||
return {self.layout.ingress, self.layout.patrol_start, self.layout.patrol_end}
|
||||
|
||||
def request_escort_at(self) -> FlightWaypoint | None:
|
||||
return self.layout.patrol_start
|
||||
|
||||
def dismiss_escort_at(self) -> FlightWaypoint | None:
|
||||
return self.layout.patrol_end
|
||||
|
||||
def ui_zone(self) -> UiZone:
|
||||
midpoint = (
|
||||
self.layout.patrol_start.position + self.layout.patrol_end.position
|
||||
@ -128,6 +122,7 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
|
||||
patrol_start_waypoint.name = "FLOT START"
|
||||
patrol_start_waypoint.pretty_name = "FLOT start"
|
||||
patrol_start_waypoint.description = "FLOT boundary"
|
||||
patrol_start_waypoint.wants_escort = True
|
||||
|
||||
patrol_end_waypoint = builder.nav(
|
||||
patrol_end, patrol_altitude, use_agl_patrol_altitude
|
||||
@ -135,6 +130,7 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
|
||||
patrol_end_waypoint.name = "FLOT END"
|
||||
patrol_end_waypoint.pretty_name = "FLOT end"
|
||||
patrol_end_waypoint.description = "FLOT boundary"
|
||||
patrol_end_waypoint.wants_escort = True
|
||||
|
||||
ingress = builder.ingress(
|
||||
FlightWaypointType.INGRESS_CAS, ingress_point, location
|
||||
|
||||
@ -205,24 +205,21 @@ class FlightPlan(ABC, Generic[LayoutT]):
|
||||
raise NotImplementedError
|
||||
|
||||
def request_escort_at(self) -> FlightWaypoint | None:
|
||||
return None
|
||||
try:
|
||||
return next(self.escorted_waypoints())
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def dismiss_escort_at(self) -> FlightWaypoint | None:
|
||||
return None
|
||||
try:
|
||||
return list(self.escorted_waypoints())[-1]
|
||||
except IndexError:
|
||||
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:
|
||||
for waypoint in self.iter_waypoints():
|
||||
if waypoint.wants_escort:
|
||||
yield waypoint
|
||||
if waypoint == end:
|
||||
return
|
||||
|
||||
def takeoff_time(self) -> datetime:
|
||||
return self.tot - self._travel_time_to_waypoint(self.tot_waypoint)
|
||||
|
||||
@ -37,12 +37,6 @@ class FormationFlightPlan(LoiterFlightPlan[LayoutT], ABC):
|
||||
def combat_speed_waypoints(self) -> set[FlightWaypoint]:
|
||||
return self.package_speed_waypoints
|
||||
|
||||
def request_escort_at(self) -> FlightWaypoint | None:
|
||||
return self.layout.join
|
||||
|
||||
def dismiss_escort_at(self) -> FlightWaypoint | None:
|
||||
return self.layout.split
|
||||
|
||||
@cached_property
|
||||
def best_flight_formation_speed(self) -> Speed:
|
||||
"""The best speed this flight is capable at all formation waypoints.
|
||||
|
||||
@ -145,7 +145,18 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
|
||||
|
||||
hold = builder.hold(self._hold_point())
|
||||
join = builder.join(self.package.waypoints.join)
|
||||
join.wants_escort = True
|
||||
|
||||
ingress = builder.ingress(
|
||||
ingress_type, self.package.waypoints.ingress, self.package.target
|
||||
)
|
||||
ingress.wants_escort = True
|
||||
|
||||
for target_waypoint in target_waypoints:
|
||||
target_waypoint.wants_escort = True
|
||||
|
||||
split = builder.split(self.package.waypoints.split)
|
||||
split.wants_escort = True
|
||||
refuel = builder.refuel(self.package.waypoints.refuel)
|
||||
|
||||
return FormationAttackLayout(
|
||||
@ -155,9 +166,7 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
|
||||
hold.position, join.position, self.doctrine.ingress_altitude
|
||||
),
|
||||
join=join,
|
||||
ingress=builder.ingress(
|
||||
ingress_type, self.package.waypoints.ingress, self.package.target
|
||||
),
|
||||
ingress=ingress,
|
||||
targets=target_waypoints,
|
||||
split=split,
|
||||
refuel=refuel,
|
||||
|
||||
@ -41,6 +41,8 @@ class FlightWaypoint:
|
||||
# The minimum amount of fuel remaining at this waypoint in pounds.
|
||||
min_fuel: float | None = None
|
||||
|
||||
wants_escort: bool = False
|
||||
|
||||
actions: list[WaypointAction] = field(default_factory=list)
|
||||
options: dict[str, WaypointOption] = field(default_factory=dict)
|
||||
|
||||
|
||||
85
tests/ato/test_flightplan.py
Normal file
85
tests/ato/test_flightplan.py
Normal file
@ -0,0 +1,85 @@
|
||||
from typing import cast, Any
|
||||
|
||||
import pytest
|
||||
from dcs import Point
|
||||
from dcs.terrain import Caucasus
|
||||
|
||||
from game.ato import Flight, FlightWaypoint
|
||||
from game.ato.flightplans.custom import CustomFlightPlan, CustomLayout
|
||||
from game.ato.flightplans.flightplan import FlightPlan
|
||||
from game.ato.flightwaypointtype import FlightWaypointType
|
||||
|
||||
|
||||
@pytest.fixture(name="unescorted_flight_plan")
|
||||
def unescorted_flight_plan_fixture() -> FlightPlan[Any]:
|
||||
point = Point(0, 0, Caucasus())
|
||||
departure = FlightWaypoint("", FlightWaypointType.TAKEOFF, point)
|
||||
|
||||
waypoints = [
|
||||
FlightWaypoint(f"{i}", FlightWaypointType.NAV, point) for i in range(10)
|
||||
]
|
||||
return CustomFlightPlan(cast(Flight, object()), CustomLayout(departure, waypoints))
|
||||
|
||||
|
||||
@pytest.fixture(name="escorted_flight_plan")
|
||||
def escorted_flight_plan_fixture() -> FlightPlan[Any]:
|
||||
point = Point(0, 0, Caucasus())
|
||||
departure = FlightWaypoint("", FlightWaypointType.TAKEOFF, point)
|
||||
|
||||
waypoints = [
|
||||
FlightWaypoint(f"{i}", FlightWaypointType.NAV, point) for i in range(10)
|
||||
]
|
||||
waypoints[1].wants_escort = True
|
||||
waypoints[2].wants_escort = True
|
||||
waypoints[3].wants_escort = True
|
||||
waypoints[5].wants_escort = True
|
||||
waypoints[7].wants_escort = True
|
||||
waypoints[8].wants_escort = True
|
||||
return CustomFlightPlan(cast(Flight, object()), CustomLayout(departure, waypoints))
|
||||
|
||||
|
||||
def test_escorted_flight_plan_escorted_waypoints(
|
||||
escorted_flight_plan: FlightPlan[Any],
|
||||
) -> None:
|
||||
assert [w.name for w in escorted_flight_plan.escorted_waypoints()] == [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
]
|
||||
|
||||
|
||||
def test_escorted_flight_plan_request_escort_at(
|
||||
escorted_flight_plan: FlightPlan[Any],
|
||||
) -> None:
|
||||
wp = escorted_flight_plan.request_escort_at()
|
||||
assert wp is not None
|
||||
assert wp.name == "1"
|
||||
|
||||
|
||||
def test_escorted_flight_plan_dismiss_escort_at(
|
||||
escorted_flight_plan: FlightPlan[Any],
|
||||
) -> None:
|
||||
wp = escorted_flight_plan.dismiss_escort_at()
|
||||
assert wp is not None
|
||||
assert wp.name == "8"
|
||||
|
||||
|
||||
def test_unescorted_flight_plan_escorted_waypoints(
|
||||
unescorted_flight_plan: FlightPlan[Any],
|
||||
) -> None:
|
||||
assert not list(unescorted_flight_plan.escorted_waypoints())
|
||||
|
||||
|
||||
def test_unescorted_flight_plan_request_escort_at(
|
||||
unescorted_flight_plan: FlightPlan[Any],
|
||||
) -> None:
|
||||
assert unescorted_flight_plan.request_escort_at() is None
|
||||
|
||||
|
||||
def test_unescorted_flight_plan_dismiss_escort_at(
|
||||
unescorted_flight_plan: FlightPlan[Any],
|
||||
) -> None:
|
||||
assert unescorted_flight_plan.dismiss_escort_at() is None
|
||||
Loading…
x
Reference in New Issue
Block a user