mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Move FlightPlan creation into Flight.
For now this is just a callsite cleanup. Later, this will make it easier to separate unscheduled and scheduled flights into different classes without complicating the layout/scheduling.
This commit is contained in:
parent
7a45391c22
commit
a101527906
@ -7,10 +7,12 @@ from typing import Any, List, Optional, TYPE_CHECKING
|
||||
from dcs import Point
|
||||
from dcs.planes import C_101CC, C_101EB, Su_33
|
||||
|
||||
from .flightplans.planningerror import PlanningError
|
||||
from .flightroster import FlightRoster
|
||||
from .flightstate import FlightState, Navigating, Uninitialized
|
||||
from .flightstate.killed import Killed
|
||||
from .loadouts import Loadout
|
||||
from .packagewaypoints import PackageWaypoints
|
||||
from ..sidc import (
|
||||
Entity,
|
||||
SidcDescribable,
|
||||
@ -81,9 +83,9 @@ class Flight(SidcDescribable):
|
||||
# Used for simulating the travel to first contact.
|
||||
self.state: FlightState = Uninitialized(self, squadron.settings)
|
||||
|
||||
# Will be replaced with a more appropriate FlightPlan by
|
||||
# FlightPlanBuilder, but an empty flight plan the flight begins with an
|
||||
# empty flight plan.
|
||||
# Will be replaced with a more appropriate FlightPlan later, but start with a
|
||||
# cheaply constructed one since adding more flights to the package may affect
|
||||
# the optimal layout.
|
||||
from .flightplans.custom import CustomFlightPlan, CustomLayout
|
||||
|
||||
self.flight_plan: FlightPlan[Any] = CustomFlightPlan(
|
||||
@ -243,3 +245,24 @@ class Flight(SidcDescribable):
|
||||
for pilot in self.roster.pilots:
|
||||
if pilot is not None:
|
||||
results.kill_pilot(self, pilot)
|
||||
|
||||
def recreate_flight_plan(self) -> None:
|
||||
self.flight_plan = self._make_flight_plan()
|
||||
|
||||
def _make_flight_plan(self) -> FlightPlan[Any]:
|
||||
from game.navmesh import NavMeshError
|
||||
from .flightplans.flightplanbuildertypes import FlightPlanBuilderTypes
|
||||
|
||||
try:
|
||||
if self.package.waypoints is None:
|
||||
self.package.waypoints = PackageWaypoints.create(
|
||||
self.package, self.coalition
|
||||
)
|
||||
builder = FlightPlanBuilderTypes.for_flight(self)(self)
|
||||
return builder.build()
|
||||
except NavMeshError as ex:
|
||||
color = "blue" if self.squadron.player else "red"
|
||||
raise PlanningError(
|
||||
f"Could not plan {color} {self.flight_type.value} from "
|
||||
f"{self.departure} to {self.package.target}"
|
||||
) from ex
|
||||
|
||||
@ -13,7 +13,6 @@ from .cas import CasFlightPlan
|
||||
from .dead import DeadFlightPlan
|
||||
from .escort import EscortFlightPlan
|
||||
from .ferry import FerryFlightPlan
|
||||
from .flightplan import FlightPlan
|
||||
from .ibuilder import IBuilder
|
||||
from .ocaaircraft import OcaAircraftFlightPlan
|
||||
from .ocarunway import OcaRunwayFlightPlan
|
||||
@ -24,59 +23,18 @@ from .strike import StrikeFlightPlan
|
||||
from .sweep import SweepFlightPlan
|
||||
from .tarcap import TarCapFlightPlan
|
||||
from .theaterrefueling import TheaterRefuelingFlightPlan
|
||||
from ..packagewaypoints import PackageWaypoints
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato import Flight, Package
|
||||
from game.coalition import Coalition
|
||||
from game.theater import ConflictTheater, FrontLine
|
||||
from game.ato import Flight
|
||||
from game.theater import FrontLine
|
||||
|
||||
|
||||
class FlightPlanBuilder:
|
||||
"""Generates flight plans for flights."""
|
||||
|
||||
def __init__(
|
||||
self, package: Package, coalition: Coalition, theater: ConflictTheater
|
||||
) -> None:
|
||||
# TODO: Plan similar altitudes for the in-country leg of the mission.
|
||||
# Waypoint altitudes for a given flight *shouldn't* differ too much
|
||||
# between the join and split points, so we don't need speeds for each
|
||||
# leg individually since they should all be fairly similar. This doesn't
|
||||
# hold too well right now since nothing is stopping each waypoint from
|
||||
# jumping 20k feet each time, but that's a huge waste of energy we
|
||||
# should be avoiding anyway.
|
||||
self.package = package
|
||||
self.coalition = coalition
|
||||
self.theater = theater
|
||||
|
||||
@property
|
||||
def is_player(self) -> bool:
|
||||
return self.coalition.player
|
||||
|
||||
def populate_flight_plan(self, flight: Flight) -> None:
|
||||
"""Creates a default flight plan for the given mission."""
|
||||
if flight not in self.package.flights:
|
||||
raise RuntimeError("Flight must be a part of the package")
|
||||
|
||||
from game.navmesh import NavMeshError
|
||||
|
||||
try:
|
||||
if self.package.waypoints is None:
|
||||
self.package.waypoints = PackageWaypoints.create(
|
||||
self.package, self.coalition
|
||||
)
|
||||
flight.flight_plan = self.generate_flight_plan(flight)
|
||||
except NavMeshError as ex:
|
||||
color = "blue" if self.is_player else "red"
|
||||
raise PlanningError(
|
||||
f"Could not plan {color} {flight.flight_type.value} from "
|
||||
f"{flight.departure} to {flight.package.target}"
|
||||
) from ex
|
||||
|
||||
def builder_type(self, flight: Flight) -> Type[IBuilder[Any, Any]]:
|
||||
class FlightPlanBuilderTypes:
|
||||
@staticmethod
|
||||
def for_flight(flight: Flight) -> Type[IBuilder[Any, Any]]:
|
||||
if flight.flight_type is FlightType.REFUELING:
|
||||
if self.package.target.is_friendly(self.is_player) or isinstance(
|
||||
self.package.target, FrontLine
|
||||
if flight.package.target.is_friendly(flight.squadron.player) or isinstance(
|
||||
flight.package.target, FrontLine
|
||||
):
|
||||
return TheaterRefuelingFlightPlan.builder_type()
|
||||
return PackageRefuelingFlightPlan.builder_type()
|
||||
@ -106,6 +64,3 @@ class FlightPlanBuilder:
|
||||
raise PlanningError(
|
||||
f"{flight.flight_type} flight plan generation not implemented"
|
||||
) from ex
|
||||
|
||||
def generate_flight_plan(self, flight: Flight) -> FlightPlan[Any]:
|
||||
return self.builder_type(flight)(flight).build()
|
||||
@ -6,7 +6,6 @@ from typing import Dict, Iterable, Optional, Set, TYPE_CHECKING
|
||||
|
||||
from game.ato.airtaaskingorder import AirTaskingOrder
|
||||
from game.ato.closestairfields import ObjectiveDistanceCache
|
||||
from game.ato.flightplans.flightplanbuilder import FlightPlanBuilder
|
||||
from game.ato.flighttype import FlightType
|
||||
from game.ato.package import Package
|
||||
from game.commander.missionproposals import EscortType, ProposedFlight, ProposedMission
|
||||
@ -190,12 +189,9 @@ class PackageFulfiller:
|
||||
# flights that will rendezvous with their package will be affected by
|
||||
# the other flights in the package. Escorts will not be able to
|
||||
# contribute to this.
|
||||
flight_plan_builder = FlightPlanBuilder(
|
||||
builder.package, self.coalition, self.theater
|
||||
)
|
||||
for flight in builder.package.flights:
|
||||
with tracer.trace("Flight plan population"):
|
||||
flight_plan_builder.populate_flight_plan(flight)
|
||||
flight.recreate_flight_plan()
|
||||
|
||||
needed_escorts = self.check_needed_escorts(builder)
|
||||
for escort in escorts:
|
||||
@ -221,7 +217,7 @@ class PackageFulfiller:
|
||||
for flight in package.flights:
|
||||
if not flight.flight_plan.waypoints:
|
||||
with tracer.trace("Flight plan population"):
|
||||
flight_plan_builder.populate_flight_plan(flight)
|
||||
flight.recreate_flight_plan()
|
||||
|
||||
if package.has_players and self.player_missions_asap:
|
||||
package.auto_asap = True
|
||||
|
||||
@ -11,7 +11,6 @@ from faker import Faker
|
||||
from game.ato import Flight, FlightType, Package
|
||||
from game.settings import AutoAtoBehavior, Settings
|
||||
from .pilot import Pilot, PilotStatus
|
||||
from ..ato.flightplans.flightplanbuilder import FlightPlanBuilder
|
||||
from ..db.database import Database
|
||||
from ..utils import meters
|
||||
|
||||
@ -19,7 +18,7 @@ if TYPE_CHECKING:
|
||||
from game import Game
|
||||
from game.coalition import Coalition
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.theater import ControlPoint, ConflictTheater, MissionTarget
|
||||
from game.theater import ControlPoint, MissionTarget
|
||||
from .operatingbases import OperatingBases
|
||||
from .squadrondef import SquadronDef
|
||||
|
||||
@ -335,9 +334,7 @@ class Squadron:
|
||||
def arrival(self) -> ControlPoint:
|
||||
return self.location if self.destination is None else self.destination
|
||||
|
||||
def plan_relocation(
|
||||
self, destination: ControlPoint, theater: ConflictTheater
|
||||
) -> None:
|
||||
def plan_relocation(self, destination: ControlPoint) -> None:
|
||||
if destination == self.location:
|
||||
logging.warning(
|
||||
f"Attempted to plan relocation of {self} to current location "
|
||||
@ -356,7 +353,7 @@ class Squadron:
|
||||
if not destination.can_operate(self.aircraft):
|
||||
raise RuntimeError(f"{self} cannot operate at {destination}.")
|
||||
self.destination = destination
|
||||
self.replan_ferry_flights(theater)
|
||||
self.replan_ferry_flights()
|
||||
|
||||
def cancel_relocation(self) -> None:
|
||||
if self.destination is None:
|
||||
@ -371,9 +368,9 @@ class Squadron:
|
||||
self.destination = None
|
||||
self.cancel_ferry_flights()
|
||||
|
||||
def replan_ferry_flights(self, theater: ConflictTheater) -> None:
|
||||
def replan_ferry_flights(self) -> None:
|
||||
self.cancel_ferry_flights()
|
||||
self.plan_ferry_flights(theater)
|
||||
self.plan_ferry_flights()
|
||||
|
||||
def cancel_ferry_flights(self) -> None:
|
||||
for package in self.coalition.ato.packages:
|
||||
@ -384,7 +381,7 @@ class Squadron:
|
||||
if not package.flights:
|
||||
self.coalition.ato.remove_package(package)
|
||||
|
||||
def plan_ferry_flights(self, theater: ConflictTheater) -> None:
|
||||
def plan_ferry_flights(self) -> None:
|
||||
if self.destination is None:
|
||||
raise RuntimeError(
|
||||
f"Cannot plan ferry flights for {self} because there is no destination."
|
||||
@ -394,17 +391,14 @@ class Squadron:
|
||||
return
|
||||
|
||||
package = Package(self.destination, self.flight_db)
|
||||
builder = FlightPlanBuilder(package, self.coalition, theater)
|
||||
while remaining:
|
||||
size = min(remaining, self.aircraft.max_group_size)
|
||||
self.plan_ferry_flight(builder, package, size)
|
||||
self.plan_ferry_flight(package, size)
|
||||
remaining -= size
|
||||
package.set_tot_asap()
|
||||
self.coalition.ato.add_package(package)
|
||||
|
||||
def plan_ferry_flight(
|
||||
self, builder: FlightPlanBuilder, package: Package, size: int
|
||||
) -> None:
|
||||
def plan_ferry_flight(self, package: Package, size: int) -> None:
|
||||
start_type = self.location.required_aircraft_start_type
|
||||
if start_type is None:
|
||||
start_type = self.settings.default_start_type
|
||||
@ -419,7 +413,7 @@ class Squadron:
|
||||
divert=None,
|
||||
)
|
||||
package.add_flight(flight)
|
||||
builder.populate_flight_plan(flight)
|
||||
flight.recreate_flight_plan()
|
||||
|
||||
@classmethod
|
||||
def create_from(
|
||||
|
||||
@ -43,7 +43,6 @@ from dcs.mapping import Point
|
||||
from game.ato.ai_flight_planner_db import aircraft_for_task
|
||||
from game.ato.closestairfields import ObjectiveDistanceCache
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightplans.flightplanbuilder import FlightPlanBuilder
|
||||
from game.ato.flighttype import FlightType
|
||||
from game.ato.package import Package
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
@ -364,10 +363,7 @@ class AirliftPlanner:
|
||||
transfer.transport = transport
|
||||
|
||||
self.package.add_flight(flight)
|
||||
planner = FlightPlanBuilder(
|
||||
self.package, self.game.coalition_for(self.for_player), self.game.theater
|
||||
)
|
||||
planner.populate_flight_plan(flight)
|
||||
flight.recreate_flight_plan()
|
||||
return flight_size
|
||||
|
||||
|
||||
|
||||
@ -1,30 +1,25 @@
|
||||
import logging
|
||||
from typing import Callable, Iterator, Optional
|
||||
|
||||
from PySide2.QtCore import (
|
||||
QItemSelectionModel,
|
||||
QModelIndex,
|
||||
Qt,
|
||||
QItemSelection,
|
||||
)
|
||||
from PySide2.QtCore import QItemSelection, QItemSelectionModel, QModelIndex, Qt
|
||||
from PySide2.QtWidgets import (
|
||||
QAbstractItemView,
|
||||
QDialog,
|
||||
QListView,
|
||||
QVBoxLayout,
|
||||
QPushButton,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QDialog,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QListView,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
)
|
||||
|
||||
from game.squadrons import Pilot, Squadron
|
||||
from game.theater import ControlPoint, ConflictTheater
|
||||
from game.ato.flighttype import FlightType
|
||||
from game.squadrons import Pilot, Squadron
|
||||
from game.theater import ConflictTheater, ControlPoint
|
||||
from qt_ui.delegates import TwoColumnRowDelegate
|
||||
from qt_ui.errorreporter import report_errors
|
||||
from qt_ui.models import SquadronModel, AtoModel
|
||||
from qt_ui.models import AtoModel, SquadronModel
|
||||
|
||||
|
||||
class PilotDelegate(TwoColumnRowDelegate):
|
||||
@ -144,7 +139,6 @@ class SquadronDialog(QDialog):
|
||||
super().__init__(parent)
|
||||
self.ato_model = ato_model
|
||||
self.squadron_model = squadron_model
|
||||
self.theater = theater
|
||||
|
||||
self.setMinimumSize(1000, 440)
|
||||
self.setWindowTitle(str(squadron_model.squadron))
|
||||
@ -200,7 +194,7 @@ class SquadronDialog(QDialog):
|
||||
if destination is None:
|
||||
self.squadron.cancel_relocation()
|
||||
else:
|
||||
self.squadron.plan_relocation(destination, self.theater)
|
||||
self.squadron.plan_relocation(destination)
|
||||
self.ato_model.replace_from_game(player=True)
|
||||
|
||||
def check_disabled_button_states(
|
||||
|
||||
@ -16,7 +16,6 @@ from PySide2.QtWidgets import (
|
||||
)
|
||||
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightplans.flightplanbuilder import FlightPlanBuilder
|
||||
from game.ato.flightplans.planningerror import PlanningError
|
||||
from game.ato.package import Package
|
||||
from game.game import Game
|
||||
@ -181,11 +180,8 @@ class QPackageDialog(QDialog):
|
||||
def add_flight(self, flight: Flight) -> None:
|
||||
"""Adds the new flight to the package."""
|
||||
self.package_model.add_flight(flight)
|
||||
planner = FlightPlanBuilder(
|
||||
self.package_model.package, self.game.blue, self.game.theater
|
||||
)
|
||||
try:
|
||||
planner.populate_flight_plan(flight)
|
||||
flight.recreate_flight_plan()
|
||||
self.package_model.update_tot()
|
||||
EventStream.put_nowait(GameUpdateEvents().new_flight(flight))
|
||||
except PlanningError as ex:
|
||||
|
||||
@ -4,7 +4,6 @@ from PySide2.QtWidgets import QGroupBox, QLabel, QMessageBox, QVBoxLayout
|
||||
|
||||
from game import Game
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightplans.flightplanbuilder import FlightPlanBuilder
|
||||
from game.ato.flightplans.planningerror import PlanningError
|
||||
from game.ato.traveltime import TotEstimator
|
||||
from qt_ui.models import PackageModel
|
||||
@ -71,16 +70,10 @@ class FlightAirfieldDisplay(QGroupBox):
|
||||
|
||||
self.flight.divert = divert
|
||||
try:
|
||||
self.update_flight_plan()
|
||||
self.flight.recreate_flight_plan()
|
||||
except PlanningError as ex:
|
||||
self.flight.divert = old_divert
|
||||
logging.exception("Could not change divert airfield")
|
||||
QMessageBox.critical(
|
||||
self, "Could not update flight plan", str(ex), QMessageBox.Ok
|
||||
)
|
||||
|
||||
def update_flight_plan(self) -> None:
|
||||
planner = FlightPlanBuilder(
|
||||
self.package_model.package, self.game.blue, self.game.theater
|
||||
)
|
||||
planner.populate_flight_plan(self.flight)
|
||||
|
||||
@ -14,7 +14,6 @@ from PySide2.QtWidgets import (
|
||||
from game import Game
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightplans.custom import CustomFlightPlan, CustomLayout
|
||||
from game.ato.flightplans.flightplanbuilder import FlightPlanBuilder
|
||||
from game.ato.flightplans.formationattack import FormationAttackFlightPlan
|
||||
from game.ato.flightplans.planningerror import PlanningError
|
||||
from game.ato.flightplans.waypointbuilder import WaypointBuilder
|
||||
@ -38,7 +37,6 @@ class QFlightWaypointTab(QFrame):
|
||||
self.game = game
|
||||
self.package = package
|
||||
self.flight = flight
|
||||
self.planner = FlightPlanBuilder(package, game.blue, game.theater)
|
||||
|
||||
self.flight_waypoint_list: Optional[QFlightWaypointList] = None
|
||||
self.rtb_waypoint: Optional[QPushButton] = None
|
||||
@ -168,7 +166,7 @@ class QFlightWaypointTab(QFrame):
|
||||
if result == QMessageBox.Yes:
|
||||
self.flight.flight_type = task
|
||||
try:
|
||||
self.planner.populate_flight_plan(self.flight)
|
||||
self.flight.recreate_flight_plan()
|
||||
except PlanningError as ex:
|
||||
self.flight.flight_type = original_task
|
||||
logging.exception("Could not recreate flight")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user