diff --git a/game/ato/flight.py b/game/ato/flight.py index 243b4a6d..981eb6c5 100644 --- a/game/ato/flight.py +++ b/game/ato/flight.py @@ -8,6 +8,7 @@ from dcs import Point from dcs.planes import C_101CC, C_101EB, Su_33 from .flightplans.planningerror import PlanningError +from .flightplans.waypointbuilder import WaypointBuilder from .flightroster import FlightRoster from .flightstate import FlightState, Navigating, Uninitialized from .flightstate.killed import Killed @@ -89,7 +90,11 @@ class Flight(SidcDescribable): from .flightplans.custom import CustomFlightPlan, CustomLayout self.flight_plan: FlightPlan[Any] = CustomFlightPlan( - self, CustomLayout(custom_waypoints=[]) + self, + CustomLayout( + departure=WaypointBuilder(self, self.coalition).takeoff(self.departure), + custom_waypoints=[], + ), ) def __getstate__(self) -> dict[str, Any]: diff --git a/game/ato/flightplans/airassault.py b/game/ato/flightplans/airassault.py index 8d4fa0f2..d2db440b 100644 --- a/game/ato/flightplans/airassault.py +++ b/game/ato/flightplans/airassault.py @@ -41,7 +41,7 @@ class AirAssaultFlightPlan(StandardFlightPlan[AirAssaultLayout]): return Builder @property - def tot_waypoint(self) -> FlightWaypoint | None: + def tot_waypoint(self) -> FlightWaypoint: return self.layout.drop_off def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: diff --git a/game/ato/flightplans/airlift.py b/game/ato/flightplans/airlift.py index 41ee1254..4ff69d05 100644 --- a/game/ato/flightplans/airlift.py +++ b/game/ato/flightplans/airlift.py @@ -47,7 +47,7 @@ class AirliftFlightPlan(StandardFlightPlan[AirliftLayout]): return Builder @property - def tot_waypoint(self) -> FlightWaypoint | None: + def tot_waypoint(self) -> FlightWaypoint: return self.layout.drop_off def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: diff --git a/game/ato/flightplans/custom.py b/game/ato/flightplans/custom.py index ec18f2eb..970ad4b8 100644 --- a/game/ato/flightplans/custom.py +++ b/game/ato/flightplans/custom.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Type from .flightplan import FlightPlan, Layout from .ibuilder import IBuilder +from .waypointbuilder import WaypointBuilder from ..flightwaypointtype import FlightWaypointType if TYPE_CHECKING: @@ -18,6 +19,7 @@ class CustomLayout(Layout): custom_waypoints: list[FlightWaypoint] def iter_waypoints(self) -> Iterator[FlightWaypoint]: + yield self.departure yield from self.custom_waypoints @@ -27,7 +29,7 @@ class CustomFlightPlan(FlightPlan[CustomLayout]): return Builder @property - def tot_waypoint(self) -> FlightWaypoint | None: + def tot_waypoint(self) -> FlightWaypoint: target_types = ( FlightWaypointType.PATROL_TRACK, FlightWaypointType.TARGET_GROUP_LOC, @@ -37,7 +39,7 @@ class CustomFlightPlan(FlightPlan[CustomLayout]): for waypoint in self.waypoints: if waypoint in target_types: return waypoint - return None + return self.layout.departure def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: if waypoint == self.tot_waypoint: @@ -54,7 +56,8 @@ class CustomFlightPlan(FlightPlan[CustomLayout]): class Builder(IBuilder[CustomFlightPlan, CustomLayout]): def layout(self) -> CustomLayout: - return CustomLayout([]) + builder = WaypointBuilder(self.flight, self.coalition) + return CustomLayout(builder.takeoff(self.flight.departure), []) def build(self) -> CustomFlightPlan: return CustomFlightPlan(self.flight, self.layout()) diff --git a/game/ato/flightplans/ferry.py b/game/ato/flightplans/ferry.py index c6d6e7cc..0ddd82ac 100644 --- a/game/ato/flightplans/ferry.py +++ b/game/ato/flightplans/ferry.py @@ -34,7 +34,7 @@ class FerryFlightPlan(StandardFlightPlan[FerryLayout]): return Builder @property - def tot_waypoint(self) -> FlightWaypoint | None: + def tot_waypoint(self) -> FlightWaypoint: return self.layout.arrival def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: diff --git a/game/ato/flightplans/flightplan.py b/game/ato/flightplans/flightplan.py index 1496da10..c02a8848 100644 --- a/game/ato/flightplans/flightplan.py +++ b/game/ato/flightplans/flightplan.py @@ -10,6 +10,7 @@ from __future__ import annotations import math from abc import ABC from collections.abc import Iterator +from dataclasses import dataclass from datetime import timedelta from functools import cached_property from typing import Any, Generic, TYPE_CHECKING, TypeGuard, TypeVar @@ -40,7 +41,10 @@ INGRESS_TYPES = { } +@dataclass(frozen=True) class Layout(ABC): + departure: FlightWaypoint + @property def waypoints(self) -> list[FlightWaypoint]: """A list of all waypoints in the flight plan, in order.""" @@ -135,7 +139,7 @@ class FlightPlan(ABC, Generic[LayoutT]): return self.flight.unit_type.fuel_consumption.cruise @property - def tot_waypoint(self) -> FlightWaypoint | None: + def tot_waypoint(self) -> FlightWaypoint: """The waypoint that is associated with the package TOT, or None. Note that the only flight plans that should have no target waypoints are @@ -246,19 +250,12 @@ class FlightPlan(ABC, Generic[LayoutT]): if waypoint == end: return - def takeoff_time(self) -> timedelta | None: - tot_waypoint = self.tot_waypoint - if tot_waypoint is None: - return None - return self.tot - self._travel_time_to_waypoint(tot_waypoint) + def takeoff_time(self) -> timedelta: + return self.tot - self._travel_time_to_waypoint(self.tot_waypoint) def startup_time(self) -> timedelta | None: - takeoff_time = self.takeoff_time() - if takeoff_time is None: - return None - start_time: timedelta = ( - takeoff_time - self.estimate_startup() - self.estimate_ground_ops() + self.takeoff_time() - self.estimate_startup() - self.estimate_ground_ops() ) # In case FP math has given us some barely below zero time, round to diff --git a/game/ato/flightplans/patrolling.py b/game/ato/flightplans/patrolling.py index 43645f24..c531fdfb 100644 --- a/game/ato/flightplans/patrolling.py +++ b/game/ato/flightplans/patrolling.py @@ -85,7 +85,7 @@ class PatrollingFlightPlan(StandardFlightPlan[LayoutT], ABC): return {self.layout.patrol_start, self.layout.patrol_end} @property - def tot_waypoint(self) -> FlightWaypoint | None: + def tot_waypoint(self) -> FlightWaypoint: return self.layout.patrol_start @property diff --git a/game/ato/flightplans/rtb.py b/game/ato/flightplans/rtb.py index 7555179f..8f1cbfa8 100644 --- a/game/ato/flightplans/rtb.py +++ b/game/ato/flightplans/rtb.py @@ -40,8 +40,8 @@ class RtbFlightPlan(StandardFlightPlan[RtbLayout]): return 1 @property - def tot_waypoint(self) -> FlightWaypoint | None: - return None + def tot_waypoint(self) -> FlightWaypoint: + return self.layout.abort_location def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: return None diff --git a/game/ato/flightplans/standard.py b/game/ato/flightplans/standard.py index 6fe1fa1a..5cc45f95 100644 --- a/game/ato/flightplans/standard.py +++ b/game/ato/flightplans/standard.py @@ -12,7 +12,6 @@ if TYPE_CHECKING: @dataclass(frozen=True) class StandardLayout(Layout, ABC): - departure: FlightWaypoint arrival: FlightWaypoint divert: FlightWaypoint | None bullseye: FlightWaypoint diff --git a/game/ato/flightplans/sweep.py b/game/ato/flightplans/sweep.py index 5c4e8761..57f40c64 100644 --- a/game/ato/flightplans/sweep.py +++ b/game/ato/flightplans/sweep.py @@ -54,7 +54,7 @@ class SweepFlightPlan(LoiterFlightPlan): return {self.layout.sweep_end} @property - def tot_waypoint(self) -> FlightWaypoint | None: + def tot_waypoint(self) -> FlightWaypoint: return self.layout.sweep_end @property diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py index 8650c9ee..16329e8d 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py @@ -148,7 +148,12 @@ class QFlightWaypointTab(QFrame): if not isinstance(self.flight.flight_plan, CustomFlightPlan): self.flight.flight_plan = CustomFlightPlan( self.flight, - CustomLayout(custom_waypoints=self.flight.flight_plan.waypoints), + CustomLayout( + departure=WaypointBuilder(self.flight, self.coalition).takeoff( + self.flight.departure + ), + custom_waypoints=self.flight.flight_plan.waypoints[1:], + ), ) def confirm_recreate(self, task: FlightType) -> None: