from __future__ import annotations from collections.abc import Iterator from dataclasses import dataclass from datetime import timedelta from typing import TYPE_CHECKING, Type from game.utils import feet from .ibuilder import IBuilder from .planningerror import PlanningError from .standard import StandardFlightPlan, StandardLayout from .waypointbuilder import WaypointBuilder if TYPE_CHECKING: from ..flight import Flight from ..flightwaypoint import FlightWaypoint class Builder(IBuilder): def build(self) -> AirliftLayout: cargo = self.flight.cargo if cargo is None: raise PlanningError( "Cannot plan transport mission for flight with no cargo." ) altitude = feet(1500) altitude_is_agl = True builder = WaypointBuilder(self.flight, self.coalition) pickup = None nav_to_pickup = [] if cargo.origin != self.flight.departure: pickup = builder.pickup(cargo.origin) nav_to_pickup = builder.nav_path( self.flight.departure.position, cargo.origin.position, altitude, altitude_is_agl, ) return AirliftLayout( departure=builder.takeoff(self.flight.departure), nav_to_pickup=nav_to_pickup, pickup=pickup, nav_to_drop_off=builder.nav_path( cargo.origin.position, cargo.next_stop.position, altitude, altitude_is_agl, ), drop_off=builder.drop_off(cargo.next_stop), nav_to_home=builder.nav_path( cargo.origin.position, self.flight.arrival.position, altitude, altitude_is_agl, ), arrival=builder.land(self.flight.arrival), divert=builder.divert(self.flight.divert), bullseye=builder.bullseye(), ) @dataclass(frozen=True) class AirliftLayout(StandardLayout): nav_to_pickup: list[FlightWaypoint] pickup: FlightWaypoint | None nav_to_drop_off: list[FlightWaypoint] drop_off: FlightWaypoint nav_to_home: list[FlightWaypoint] def iter_waypoints(self) -> Iterator[FlightWaypoint]: yield self.departure yield from self.nav_to_pickup if self.pickup: yield self.pickup yield from self.nav_to_drop_off yield self.drop_off yield from self.nav_to_home yield self.arrival if self.divert is not None: yield self.divert yield self.bullseye class AirliftFlightPlan(StandardFlightPlan[AirliftLayout]): def __init__(self, flight: Flight, layout: AirliftLayout) -> None: super().__init__(flight, layout) @staticmethod def builder_type() -> Type[Builder]: return Builder @property def tot_waypoint(self) -> FlightWaypoint | None: return self.layout.drop_off def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: # TOT planning isn't really useful for transports. They're behind the front # lines so no need to wait for escorts or for other missions to complete. return None def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: return None @property def mission_departure_time(self) -> timedelta: return self.package.time_over_target