Remove isinstance use in flight support data.

mypy struggles to prove this cast correct when there are two or'd
isinstance checks where both types coincidentally have properties of the
same name (but no defined protocol making that explicit). I'm not really
sure why mypy is happy with this in its current state, but it isn't
after a change I'm making.

All our isinstance use is a bit of an anti-pattern anyway, so extract a
method that exposes the data we care about.

The start/end times for tankers aren't actually used, so this could be
simplified even more, but that data _should_ be used.
This commit is contained in:
Dan Albert 2022-12-28 13:48:01 -08:00
parent 24a04fb8c6
commit 4aa42e6573
11 changed files with 52 additions and 19 deletions

View File

@ -67,6 +67,10 @@ class AirAssaultFlightPlan(StandardFlightPlan[AirAssaultLayout], UiZoneDisplay):
def ctld_target_zone_radius(self) -> Distance:
return meters(2500)
@property
def mission_begin_on_station_time(self) -> timedelta | None:
return None
@property
def mission_departure_time(self) -> timedelta:
return self.package.time_over_target

View File

@ -75,6 +75,10 @@ class AirliftFlightPlan(StandardFlightPlan[AirliftLayout]):
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
return None
@property
def mission_begin_on_station_time(self) -> timedelta | None:
return None
@property
def mission_departure_time(self) -> timedelta:
return self.package.time_over_target

View File

@ -50,6 +50,10 @@ class CustomFlightPlan(FlightPlan[CustomLayout]):
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
return None
@property
def mission_begin_on_station_time(self) -> timedelta | None:
return None
@property
def mission_departure_time(self) -> timedelta:
return self.package.time_over_target

View File

@ -45,6 +45,10 @@ class FerryFlightPlan(StandardFlightPlan[FerryLayout]):
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
return None
@property
def mission_begin_on_station_time(self) -> timedelta | None:
return None
@property
def mission_departure_time(self) -> timedelta:
return self.package.time_over_target

View File

@ -8,7 +8,7 @@ generating the waypoints for the mission.
from __future__ import annotations
import math
from abc import ABC
from abc import ABC, abstractmethod
from collections.abc import Iterator
from dataclasses import dataclass
from datetime import timedelta
@ -296,6 +296,16 @@ class FlightPlan(ABC, Generic[LayoutT]):
else:
return timedelta(minutes=8)
@property
@abstractmethod
def mission_begin_on_station_time(self) -> timedelta | None:
"""The time that the mission is first on-station.
Not all mission types will have a time when they can be considered on-station.
Missions that patrol or loiter (CAPs, CAS, refueling, AEW&C, etc) will have this
defined, but strike-like missions will not.
"""
@property
def mission_departure_time(self) -> timedelta:
"""The time that the mission is complete and the flight RTBs."""

View File

@ -96,6 +96,10 @@ class FormationFlightPlan(LoiterFlightPlan, ABC):
GroundSpeed.for_flight(self.flight, self.layout.hold.alt),
)
@property
def mission_begin_on_station_time(self) -> timedelta | None:
return None
@property
def mission_departure_time(self) -> timedelta:
return self.split_time

View File

@ -89,6 +89,10 @@ class PatrollingFlightPlan(StandardFlightPlan[LayoutT], UiZoneDisplay, ABC):
def tot_waypoint(self) -> FlightWaypoint:
return self.layout.patrol_start
@property
def mission_begin_on_station_time(self) -> timedelta:
return self.patrol_start_time
@property
def mission_departure_time(self) -> timedelta:
return self.patrol_end_time

View File

@ -49,6 +49,10 @@ class RtbFlightPlan(StandardFlightPlan[RtbLayout]):
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
return None
@property
def mission_begin_on_station_time(self) -> timedelta | None:
return None
@property
def mission_departure_time(self) -> timedelta:
return timedelta()

View File

@ -3,9 +3,9 @@ from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
from typing import Iterator, Type
from game.ato.flightplans.standard import StandardFlightPlan, StandardLayout
from game.ato.flightplans.ibuilder import IBuilder
from game.ato.flightplans.standard import StandardLayout
from game.ato.flightplans.standard import StandardFlightPlan, StandardLayout
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.ato.flightwaypoint import FlightWaypoint
@ -37,15 +37,11 @@ class RecoveryTankerFlightPlan(StandardFlightPlan[RecoveryTankerLayout]):
return self.layout.recovery_ship
@property
def mission_departure_time(self) -> timedelta:
return self.patrol_end_time
@property
def patrol_start_time(self) -> timedelta:
def mission_begin_on_station_time(self) -> timedelta:
return self.package.time_over_target
@property
def patrol_end_time(self) -> timedelta:
def mission_departure_time(self) -> timedelta:
return self.tot + timedelta(hours=2)
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:

View File

@ -89,6 +89,10 @@ class SweepFlightPlan(LoiterFlightPlan):
GroundSpeed.for_flight(self.flight, self.layout.hold.alt),
)
@property
def mission_begin_on_station_time(self) -> timedelta | None:
return None
def mission_departure_time(self) -> timedelta:
return self.sweep_end_time

View File

@ -10,12 +10,11 @@ from dcs.unit import Skill
from dcs.unitgroup import FlyingGroup
from game.ato import Flight, FlightType
from game.ato.flightplans.shiprecoverytanker import RecoveryTankerFlightPlan
from game.callsigns import callsign_for_support_unit
from game.data.weapons import Pylon, WeaponType as WeaponTypeEnum
from game.missiongenerator.missiondata import MissionData, AwacsInfo, TankerInfo
from game.missiongenerator.lasercoderegistry import LaserCodeRegistry
from game.missiongenerator.logisticsgenerator import LogisticsGenerator
from game.missiongenerator.missiondata import AwacsInfo, MissionData, TankerInfo
from game.radio.radios import RadioFrequency, RadioRegistry
from game.radio.tacan import TacanBand, TacanRegistry, TacanUsage
from game.runways import RunwayData
@ -25,8 +24,6 @@ from .aircraftbehavior import AircraftBehavior
from .aircraftpainter import AircraftPainter
from .flightdata import FlightData
from .waypoints import WaypointGenerator
from ...ato.flightplans.aewc import AewcFlightPlan
from ...ato.flightplans.theaterrefueling import TheaterRefuelingFlightPlan
if TYPE_CHECKING:
from game import Game
@ -149,7 +146,7 @@ class FlightGroupConfigurator:
def register_air_support(self, channel: RadioFrequency) -> None:
callsign = callsign_for_support_unit(self.group)
if isinstance(self.flight.flight_plan, AewcFlightPlan):
if self.flight.flight_type is FlightType.AEWC:
self.mission_data.awacs.append(
AwacsInfo(
group_name=str(self.group.name),
@ -161,9 +158,7 @@ class FlightGroupConfigurator:
blue=self.flight.departure.captured,
)
)
elif isinstance(
self.flight.flight_plan, TheaterRefuelingFlightPlan
) or isinstance(self.flight.flight_plan, RecoveryTankerFlightPlan):
elif self.flight.flight_type is FlightType.REFUELING:
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y, TacanUsage.AirToAir)
self.mission_data.tankers.append(
TankerInfo(
@ -172,8 +167,8 @@ class FlightGroupConfigurator:
variant=self.flight.unit_type.name,
freq=channel,
tacan=tacan,
start_time=self.flight.flight_plan.patrol_start_time,
end_time=self.flight.flight_plan.patrol_end_time,
start_time=self.flight.flight_plan.mission_begin_on_station_time,
end_time=self.flight.flight_plan.mission_departure_time,
blue=self.flight.departure.captured,
)
)