Set up split/join points.

This commit is contained in:
Dan Albert 2020-09-29 18:22:20 -07:00
parent 56a5864600
commit 6ce82be46b
23 changed files with 282 additions and 121 deletions

View File

@ -14,9 +14,13 @@ class Doctrine:
strike_max_range: int strike_max_range: int
sead_max_range: int sead_max_range: int
rendezvous_altitude: int
join_distance: int
split_distance: int
ingress_egress_distance: int ingress_egress_distance: int
ingress_altitude: int ingress_altitude: int
egress_altitude: int egress_altitude: int
min_patrol_altitude: int min_patrol_altitude: int
max_patrol_altitude: int max_patrol_altitude: int
pattern_altitude: int pattern_altitude: int
@ -35,6 +39,9 @@ MODERN_DOCTRINE = Doctrine(
antiship=True, antiship=True,
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(25000),
join_distance=nm_to_meter(20),
split_distance=nm_to_meter(20),
ingress_egress_distance=nm_to_meter(45), ingress_egress_distance=nm_to_meter(45),
ingress_altitude=feet_to_meter(20000), ingress_altitude=feet_to_meter(20000),
egress_altitude=feet_to_meter(20000), egress_altitude=feet_to_meter(20000),
@ -55,6 +62,9 @@ COLDWAR_DOCTRINE = Doctrine(
antiship=True, antiship=True,
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(22000),
join_distance=nm_to_meter(10),
split_distance=nm_to_meter(10),
ingress_egress_distance=nm_to_meter(30), ingress_egress_distance=nm_to_meter(30),
ingress_altitude=feet_to_meter(18000), ingress_altitude=feet_to_meter(18000),
egress_altitude=feet_to_meter(18000), egress_altitude=feet_to_meter(18000),
@ -75,6 +85,9 @@ WWII_DOCTRINE = Doctrine(
antiship=True, antiship=True,
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
join_distance=nm_to_meter(5),
split_distance=nm_to_meter(5),
rendezvous_altitude=feet_to_meter(10000),
ingress_egress_distance=nm_to_meter(7), ingress_egress_distance=nm_to_meter(7),
ingress_altitude=feet_to_meter(8000), ingress_altitude=feet_to_meter(8000),
egress_altitude=feet_to_meter(8000), egress_altitude=feet_to_meter(8000),

View File

@ -89,6 +89,7 @@ class Game:
) )
self.sanitize_sides() self.sanitize_sides()
self.on_load()
def sanitize_sides(self): def sanitize_sides(self):
@ -204,9 +205,10 @@ class Game:
else: else:
return event and event.name and event.name == self.player_name return event and event.name and event.name == self.player_name
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None): def on_load(self) -> None:
ObjectiveDistanceCache.set_theater(self.theater) ObjectiveDistanceCache.set_theater(self.theater)
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
logging.info("Pass turn") logging.info("Pass turn")
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0)) self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
self.turn = self.turn + 1 self.turn = self.turn + 1

View File

@ -13,6 +13,7 @@ from dataclasses import dataclass, field
import logging import logging
from typing import Dict, Iterator, List, Optional from typing import Dict, Iterator, List, Optional
from dcs.mapping import Point
from .flights.flight import Flight, FlightType from .flights.flight import Flight, FlightType
from theater.missiontarget import MissionTarget from theater.missiontarget import MissionTarget
@ -39,6 +40,11 @@ class Package:
#: The set of flights in the package. #: The set of flights in the package.
flights: List[Flight] = field(default_factory=list) flights: List[Flight] = field(default_factory=list)
join_point: Optional[Point] = field(default=None, init=False, hash=False)
split_point: Optional[Point] = field(default=None, init=False, hash=False)
ingress_point: Optional[Point] = field(default=None, init=False, hash=False)
egress_point: Optional[Point] = field(default=None, init=False, hash=False)
def add_flight(self, flight: Flight) -> None: def add_flight(self, flight: Flight) -> None:
"""Adds a flight to the package.""" """Adds a flight to the package."""
self.flights.append(flight) self.flights.append(flight)
@ -46,6 +52,9 @@ class Package:
def remove_flight(self, flight: Flight) -> None: def remove_flight(self, flight: Flight) -> None:
"""Removes a flight from the package.""" """Removes a flight from the package."""
self.flights.remove(flight) self.flights.remove(flight)
if not self.flights:
self.ingress_point = None
self.egress_point = None
@property @property
def primary_task(self) -> Optional[FlightType]: def primary_task(self) -> Optional[FlightType]:

View File

@ -414,9 +414,9 @@ class CoalitionMissionPlanner:
return return
package = builder.build() package = builder.build()
builder = FlightPlanBuilder(self.game, self.is_player, package) builder = FlightPlanBuilder(self.game, package, self.is_player)
for flight in package.flights: for flight in package.flights:
builder.populate_flight_plan(flight, package.target) builder.populate_flight_plan(flight)
self.ato.add_package(package) self.ato.add_package(package)
def message(self, title, text) -> None: def message(self, title, text) -> None:

View File

@ -47,6 +47,8 @@ class FlightWaypointType(Enum):
TARGET_GROUP_LOC = 13 # A target group approximate location TARGET_GROUP_LOC = 13 # A target group approximate location
TARGET_SHIP = 14 # A target ship known location TARGET_SHIP = 14 # A target ship known location
CUSTOM = 15 # User waypoint (no specific behaviour) CUSTOM = 15 # User waypoint (no specific behaviour)
JOIN = 16
SPLIT = 17
class PredefinedWaypointCategory(Enum): class PredefinedWaypointCategory(Enum):

View File

@ -38,8 +38,7 @@ class InvalidObjectiveLocation(RuntimeError):
class FlightPlanBuilder: class FlightPlanBuilder:
"""Generates flight plans for flights.""" """Generates flight plans for flights."""
def __init__(self, game: Game, is_player: bool, def __init__(self, game: Game, package: Package, is_player: bool) -> None:
package: Optional[Package] = None) -> None:
self.game = game self.game = game
self.package = package self.package = package
self.is_player = is_player self.is_player = is_player
@ -49,9 +48,15 @@ class FlightPlanBuilder:
faction = self.game.enemy_faction faction = self.game.enemy_faction
self.doctrine: Doctrine = faction.get("doctrine", MODERN_DOCTRINE) self.doctrine: Doctrine = faction.get("doctrine", MODERN_DOCTRINE)
def populate_flight_plan(self, flight: Flight, def populate_flight_plan(
objective_location: MissionTarget) -> None: self, flight: Flight,
# TODO: Custom targets should be an attribute of the flight.
custom_targets: Optional[List[Unit]] = None) -> None:
"""Creates a default flight plan for the given mission.""" """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")
self.generate_missing_package_waypoints()
# TODO: Flesh out mission types. # TODO: Flesh out mission types.
try: try:
task = flight.flight_type task = flight.flight_type
@ -62,17 +67,17 @@ class FlightPlanBuilder:
elif task == FlightType.BAI: elif task == FlightType.BAI:
logging.error("BAI flight plan generation not implemented") logging.error("BAI flight plan generation not implemented")
elif task == FlightType.BARCAP: elif task == FlightType.BARCAP:
self.generate_barcap(flight, objective_location) self.generate_barcap(flight)
elif task == FlightType.CAP: elif task == FlightType.CAP:
self.generate_barcap(flight, objective_location) self.generate_barcap(flight)
elif task == FlightType.CAS: elif task == FlightType.CAS:
self.generate_cas(flight, objective_location) self.generate_cas(flight)
elif task == FlightType.DEAD: elif task == FlightType.DEAD:
self.generate_sead(flight, objective_location) self.generate_sead(flight, custom_targets)
elif task == FlightType.ELINT: elif task == FlightType.ELINT:
logging.error("ELINT flight plan generation not implemented") logging.error("ELINT flight plan generation not implemented")
elif task == FlightType.ESCORT: elif task == FlightType.ESCORT:
self.generate_escort(flight, objective_location) self.generate_escort(flight)
elif task == FlightType.EVAC: elif task == FlightType.EVAC:
logging.error("Evac flight plan generation not implemented") logging.error("Evac flight plan generation not implemented")
elif task == FlightType.EWAR: elif task == FlightType.EWAR:
@ -88,11 +93,11 @@ class FlightPlanBuilder:
elif task == FlightType.RECON: elif task == FlightType.RECON:
logging.error("Recon flight plan generation not implemented") logging.error("Recon flight plan generation not implemented")
elif task == FlightType.SEAD: elif task == FlightType.SEAD:
self.generate_sead(flight, objective_location) self.generate_sead(flight, custom_targets)
elif task == FlightType.STRIKE: elif task == FlightType.STRIKE:
self.generate_strike(flight, objective_location) self.generate_strike(flight)
elif task == FlightType.TARCAP: elif task == FlightType.TARCAP:
self.generate_frontline_cap(flight, objective_location) self.generate_frontline_cap(flight)
elif task == FlightType.TROOP_TRANSPORT: elif task == FlightType.TROOP_TRANSPORT:
logging.error( logging.error(
"Troop transport flight plan generation not implemented" "Troop transport flight plan generation not implemented"
@ -100,23 +105,32 @@ class FlightPlanBuilder:
except InvalidObjectiveLocation as ex: except InvalidObjectiveLocation as ex:
logging.error(f"Could not create flight plan: {ex}") logging.error(f"Could not create flight plan: {ex}")
def generate_strike(self, flight: Flight, location: MissionTarget) -> None: def generate_missing_package_waypoints(self) -> None:
if self.package.ingress_point is None:
self.package.ingress_point = self._ingress_point()
if self.package.egress_point is None:
self.package.egress_point = self._egress_point()
if self.package.join_point is None:
self.package.join_point = self._join_point()
if self.package.split_point is None:
self.package.split_point = self._split_point()
def generate_strike(self, flight: Flight) -> None:
"""Generates a strike flight plan. """Generates a strike flight plan.
Args: Args:
flight: The flight to generate the flight plan for. flight: The flight to generate the flight plan for.
location: The strike target location.
""" """
location = self.package.target
# TODO: Support airfield strikes. # TODO: Support airfield strikes.
if not isinstance(location, TheaterGroundObject): if not isinstance(location, TheaterGroundObject):
raise InvalidObjectiveLocation(flight.flight_type, location) raise InvalidObjectiveLocation(flight.flight_type, location)
# TODO: Stop clobbering flight type.
flight.flight_type = FlightType.STRIKE
builder = WaypointBuilder(self.doctrine) builder = WaypointBuilder(self.doctrine)
builder.ascent(flight.from_cp) builder.ascent(flight.from_cp)
builder.ingress_strike(self.ingress_point(flight, location), location) builder.join(self.package.join_point)
builder.ingress_strike(self.package.ingress_point, location)
if len(location.groups) > 0 and location.dcs_identifier == "AA": if len(location.groups) > 0 and location.dcs_identifier == "AA":
# TODO: Replace with DEAD? # TODO: Replace with DEAD?
@ -143,26 +157,23 @@ class FlightPlanBuilder:
location location
) )
builder.egress(self.egress_point(flight, location), location) builder.egress(self.package.egress_point, location)
builder.split(self.package.split_point)
builder.rtb(flight.from_cp) builder.rtb(flight.from_cp)
flight.points = builder.build() flight.points = builder.build()
def generate_barcap(self, flight: Flight, location: MissionTarget) -> None: def generate_barcap(self, flight: Flight) -> None:
"""Generate a BARCAP flight at a given location. """Generate a BARCAP flight at a given location.
Args: Args:
flight: The flight to generate the flight plan for. flight: The flight to generate the flight plan for.
location: The control point to protect.
""" """
location = self.package.target
if isinstance(location, FrontLine): if isinstance(location, FrontLine):
raise InvalidObjectiveLocation(flight.flight_type, location) raise InvalidObjectiveLocation(flight.flight_type, location)
if isinstance(location, ControlPoint) and location.is_carrier:
flight.flight_type = FlightType.BARCAP
else:
flight.flight_type = FlightType.CAP
patrol_alt = random.randint( patrol_alt = random.randint(
self.doctrine.min_patrol_altitude, self.doctrine.min_patrol_altitude,
self.doctrine.max_patrol_altitude self.doctrine.max_patrol_altitude
@ -198,19 +209,18 @@ class FlightPlanBuilder:
builder.rtb(flight.from_cp) builder.rtb(flight.from_cp)
flight.points = builder.build() flight.points = builder.build()
def generate_frontline_cap(self, flight: Flight, def generate_frontline_cap(self, flight: Flight) -> None:
location: MissionTarget) -> None:
"""Generate a CAP flight plan for the given front line. """Generate a CAP flight plan for the given front line.
Args: Args:
flight: The flight to generate the flight plan for. flight: The flight to generate the flight plan for.
location: Front line to protect.
""" """
location = self.package.target
if not isinstance(location, FrontLine): if not isinstance(location, FrontLine):
raise InvalidObjectiveLocation(flight.flight_type, location) raise InvalidObjectiveLocation(flight.flight_type, location)
ally_cp, enemy_cp = location.control_points ally_cp, enemy_cp = location.control_points
flight.flight_type = FlightType.CAP
patrol_alt = random.randint(self.doctrine.min_patrol_altitude, patrol_alt = random.randint(self.doctrine.min_patrol_altitude,
self.doctrine.max_patrol_altitude) self.doctrine.max_patrol_altitude)
@ -240,26 +250,26 @@ class FlightPlanBuilder:
builder.rtb(flight.from_cp) builder.rtb(flight.from_cp)
flight.points = builder.build() flight.points = builder.build()
def generate_sead(self, flight: Flight, location: MissionTarget, def generate_sead(self, flight: Flight,
custom_targets: Optional[List[Unit]] = None) -> None: custom_targets: Optional[List[Unit]]) -> None:
"""Generate a SEAD/DEAD flight at a given location. """Generate a SEAD/DEAD flight at a given location.
Args: Args:
flight: The flight to generate the flight plan for. flight: The flight to generate the flight plan for.
location: Location of the SAM site.
custom_targets: Specific radar equipped units selected by the user. custom_targets: Specific radar equipped units selected by the user.
""" """
location = self.package.target
if not isinstance(location, TheaterGroundObject): if not isinstance(location, TheaterGroundObject):
raise InvalidObjectiveLocation(flight.flight_type, location) raise InvalidObjectiveLocation(flight.flight_type, location)
if custom_targets is None: if custom_targets is None:
custom_targets = [] custom_targets = []
flight.flight_type = random.choice([FlightType.SEAD, FlightType.DEAD])
builder = WaypointBuilder(self.doctrine) builder = WaypointBuilder(self.doctrine)
builder.ascent(flight.from_cp) builder.ascent(flight.from_cp)
builder.ingress_sead(self.ingress_point(flight, location), location) builder.join(self.package.join_point)
builder.ingress_sead(self.package.ingress_point, location)
# TODO: Unify these. # TODO: Unify these.
# There doesn't seem to be any reason to treat the UI fragged missions # There doesn't seem to be any reason to treat the UI fragged missions
@ -283,14 +293,13 @@ class FlightPlanBuilder:
else: else:
builder.sead_area(location) builder.sead_area(location)
builder.egress(self.egress_point(flight, location), location) builder.egress(self.package.egress_point, location)
builder.split(self.package.split_point)
builder.rtb(flight.from_cp) builder.rtb(flight.from_cp)
flight.points = builder.build() flight.points = builder.build()
def generate_escort(self, flight: Flight, location: MissionTarget) -> None: def generate_escort(self, flight: Flight) -> None:
flight.flight_type = FlightType.ESCORT
# TODO: Decide common waypoints for the package ahead of time. # TODO: Decide common waypoints for the package ahead of time.
# Packages should determine some common points like push, ingress, # Packages should determine some common points like push, ingress,
# egress, and split points ahead of time so they can be shared by all # egress, and split points ahead of time so they can be shared by all
@ -303,25 +312,30 @@ class FlightPlanBuilder:
builder = WaypointBuilder(self.doctrine) builder = WaypointBuilder(self.doctrine)
builder.ascent(flight.from_cp) builder.ascent(flight.from_cp)
builder.race_track(self.ingress_point(flight, location), builder.join(self.package.join_point)
self.egress_point(flight, location), patrol_alt) builder.race_track(
self.package.ingress_point,
self.package.egress_point,
patrol_alt
)
builder.split(self.package.split_point)
builder.rtb(flight.from_cp) builder.rtb(flight.from_cp)
flight.points = builder.build() flight.points = builder.build()
def generate_cas(self, flight: Flight, location: MissionTarget) -> None: def generate_cas(self, flight: Flight) -> None:
"""Generate a CAS flight plan for the given target. """Generate a CAS flight plan for the given target.
Args: Args:
flight: The flight to generate the flight plan for. flight: The flight to generate the flight plan for.
location: Front line with CAS targets.
""" """
location = self.package.target
if not isinstance(location, FrontLine): if not isinstance(location, FrontLine):
raise InvalidObjectiveLocation(flight.flight_type, location) raise InvalidObjectiveLocation(flight.flight_type, location)
is_helo = getattr(flight.unit_type, "helicopter", False) is_helo = getattr(flight.unit_type, "helicopter", False)
cap_alt = 500 if is_helo else 1000 cap_alt = 500 if is_helo else 1000
flight.flight_type = FlightType.CAS
ingress, heading, distance = Conflict.frontline_vector( ingress, heading, distance = Conflict.frontline_vector(
location.control_points[0], location.control_points[1], location.control_points[0], location.control_points[1],
@ -332,9 +346,11 @@ class FlightPlanBuilder:
builder = WaypointBuilder(self.doctrine) builder = WaypointBuilder(self.doctrine)
builder.ascent(flight.from_cp, is_helo) builder.ascent(flight.from_cp, is_helo)
builder.join(self.package.join_point)
builder.ingress_cas(ingress, location) builder.ingress_cas(ingress, location)
builder.cas(center, cap_alt) builder.cas(center, cap_alt)
builder.egress(egress, location) builder.egress(egress, location)
builder.split(self.package.split_point)
builder.rtb(flight.from_cp, is_helo) builder.rtb(flight.from_cp, is_helo)
flight.points = builder.build() flight.points = builder.build()
@ -370,33 +386,51 @@ class FlightPlanBuilder:
builder.land(arrival) builder.land(arrival)
return builder.build()[0] return builder.build()[0]
def ingress_point(self, flight: Flight, target: MissionTarget) -> Point: def _join_point(self) -> Point:
heading = self._heading_to_package_airfield(flight, target) ingress_point = self.package.ingress_point
return target.position.point_from_heading( heading = self._heading_to_package_airfield(ingress_point)
return ingress_point.point_from_heading(heading,
-self.doctrine.join_distance)
def _split_point(self) -> Point:
egress_point = self.package.egress_point
heading = self._heading_to_package_airfield(egress_point)
return egress_point.point_from_heading(heading,
-self.doctrine.split_distance)
def _ingress_point(self) -> Point:
heading = self._target_heading_to_package_airfield()
return self.package.target.position.point_from_heading(
heading - 180 + 25, self.doctrine.ingress_egress_distance heading - 180 + 25, self.doctrine.ingress_egress_distance
) )
def egress_point(self, flight: Flight, target: MissionTarget) -> Point: def _egress_point(self) -> Point:
heading = self._heading_to_package_airfield(flight, target) heading = self._target_heading_to_package_airfield()
return target.position.point_from_heading( return self.package.target.position.point_from_heading(
heading - 180 - 25, self.doctrine.ingress_egress_distance heading - 180 - 25, self.doctrine.ingress_egress_distance
) )
def _heading_to_package_airfield(self, flight: Flight, def _target_heading_to_package_airfield(self) -> int:
target: MissionTarget) -> int: return self._heading_to_package_airfield(self.package.target.position)
airfield = self.package_airfield(flight, target)
return airfield.position.heading_between_point(target.position) def _heading_to_package_airfield(self, point: Point) -> int:
return self.package_airfield().position.heading_between_point(point)
# TODO: Set ingress/egress/join/split points in the Package. # TODO: Set ingress/egress/join/split points in the Package.
def package_airfield(self, flight: Flight, def package_airfield(self) -> ControlPoint:
target: MissionTarget) -> ControlPoint: # We'll always have a package, but if this is being planned via the UI
# it could be the first flight in the package.
if not self.package.flights:
raise RuntimeError(
"Cannot determine source airfield for package with no flights"
)
# The package airfield is either the flight's airfield (when there is no # The package airfield is either the flight's airfield (when there is no
# package) or the closest airfield to the objective that is the # package) or the closest airfield to the objective that is the
# departure airfield for some flight in the package. # departure airfield for some flight in the package.
if self.package is None: cache = ObjectiveDistanceCache.get_closest_airfields(
return flight.from_cp self.package.target
)
cache = ObjectiveDistanceCache.get_closest_airfields(target)
for airfield in cache.closest_airfields: for airfield in cache.closest_airfields:
for flight in self.package.flights: for flight in self.package.flights:
if flight.from_cp == airfield: if flight.from_cp == airfield:

View File

@ -86,6 +86,30 @@ class WaypointBuilder:
waypoint.pretty_name = "Land" waypoint.pretty_name = "Land"
self.waypoints.append(waypoint) self.waypoints.append(waypoint)
def join(self, position: Point) -> None:
waypoint = FlightWaypoint(
FlightWaypointType.JOIN,
position.x,
position.y,
self.doctrine.ingress_altitude
)
waypoint.pretty_name = "Join"
waypoint.description = "Rendezvous with package"
waypoint.name = "JOIN"
self.waypoints.append(waypoint)
def split(self, position: Point) -> None:
waypoint = FlightWaypoint(
FlightWaypointType.SPLIT,
position.x,
position.y,
self.doctrine.ingress_altitude
)
waypoint.pretty_name = "Split"
waypoint.description = "Depart from package"
waypoint.name = "SPLIT"
self.waypoints.append(waypoint)
def ingress_cas(self, position: Point, objective: MissionTarget) -> None: def ingress_cas(self, position: Point, objective: MissionTarget) -> None:
self._ingress(FlightWaypointType.INGRESS_CAS, position, objective) self._ingress(FlightWaypointType.INGRESS_CAS, position, objective)

View File

@ -54,7 +54,12 @@ class Dialog:
cls.edit_package_dialog.show() cls.edit_package_dialog.show()
@classmethod @classmethod
def open_edit_flight_dialog(cls, flight: Flight): def open_edit_flight_dialog(cls, package_model: PackageModel,
flight: Flight) -> None:
"""Opens the dialog to edit the given flight.""" """Opens the dialog to edit the given flight."""
cls.edit_flight_dialog = QEditFlightDialog(cls.game_model.game, flight) cls.edit_flight_dialog = QEditFlightDialog(
cls.game_model.game,
package_model.package,
flight
)
cls.edit_flight_dialog.show() cls.edit_flight_dialog.show()

View File

@ -1,3 +1,5 @@
from typing import Optional
from PySide2.QtWidgets import QFrame, QGroupBox, QHBoxLayout, QPushButton from PySide2.QtWidgets import QFrame, QGroupBox, QHBoxLayout, QPushButton
import qt_ui.uiconstants as CONST import qt_ui.uiconstants as CONST
@ -74,7 +76,7 @@ class QTopPanel(QFrame):
self.layout.setContentsMargins(0,0,0,0) self.layout.setContentsMargins(0,0,0,0)
self.setLayout(self.layout) self.setLayout(self.layout)
def setGame(self, game:Game): def setGame(self, game: Optional[Game]):
self.game = game self.game = game
if game is not None: if game is not None:
self.turnCounter.setCurrentTurn(self.game.turn, self.game.current_day) self.turnCounter.setCurrentTurn(self.game.turn, self.game.current_day)

View File

@ -123,7 +123,7 @@ class QFlightPanel(QGroupBox):
return return
from qt_ui.dialogs import Dialog from qt_ui.dialogs import Dialog
Dialog.open_edit_flight_dialog( Dialog.open_edit_flight_dialog(
self.package_model.flight_at_index(index) self.package_model, self.package_model.flight_at_index(index)
) )
def on_delete(self) -> None: def on_delete(self) -> None:

View File

@ -1,4 +1,4 @@
from typing import Dict, List, Tuple from typing import Dict, List, Optional, Tuple
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtGui import QBrush, QColor, QPen, QPixmap, QWheelEvent from PySide2.QtGui import QBrush, QColor, QPen, QPixmap, QWheelEvent
@ -43,6 +43,7 @@ class QLiberationMap(QGraphicsView):
super(QLiberationMap, self).__init__() super(QLiberationMap, self).__init__()
QLiberationMap.instance = self QLiberationMap.instance = self
self.game_model = game_model self.game_model = game_model
self.game: Optional[Game] = game_model.game
self.flight_path_items: List[QGraphicsItem] = [] self.flight_path_items: List[QGraphicsItem] = []
@ -71,7 +72,7 @@ class QLiberationMap(QGraphicsView):
def connectSignals(self): def connectSignals(self):
GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) GameUpdateSignal.get_instance().gameupdated.connect(self.setGame)
def setGame(self, game: Game): def setGame(self, game: Optional[Game]):
self.game = game self.game = game
print("Reloading Map Canvas") print("Reloading Map Canvas")
if self.game is not None: if self.game is not None:

View File

@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
from typing import Optional
from PySide2.QtCore import QObject, Signal from PySide2.QtCore import QObject, Signal
from game import Game from game import Game
@ -31,7 +33,7 @@ class GameUpdateSignal(QObject):
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.flight_paths_changed.emit() self.flight_paths_changed.emit()
def updateGame(self, game: Game): def updateGame(self, game: Optional[Game]):
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.gameupdated.emit(game) self.gameupdated.emit(game)

View File

@ -232,6 +232,8 @@ class QLiberationWindow(QMainWindow):
sys.exit(0) sys.exit(0)
def setGame(self, game: Optional[Game]): def setGame(self, game: Optional[Game]):
if game is not None:
game.on_load()
self.game = game self.game = game
if self.info_panel: if self.info_panel:
self.info_panel.setGame(game) self.info_panel.setGame(game)

View File

@ -5,6 +5,7 @@ from PySide2.QtWidgets import (
) )
from game import Game from game import Game
from gen.ato import Package
from gen.flights.flight import Flight from gen.flights.flight import Flight
from qt_ui.uiconstants import EVENT_ICONS from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
@ -14,7 +15,7 @@ from qt_ui.windows.mission.flight.QFlightPlanner import QFlightPlanner
class QEditFlightDialog(QDialog): class QEditFlightDialog(QDialog):
"""Dialog window for editing flight plans and loadouts.""" """Dialog window for editing flight plans and loadouts."""
def __init__(self, game: Game, flight: Flight) -> None: def __init__(self, game: Game, package: Package, flight: Flight) -> None:
super().__init__() super().__init__()
self.game = game self.game = game
@ -24,7 +25,7 @@ class QEditFlightDialog(QDialog):
layout = QVBoxLayout() layout = QVBoxLayout()
self.flight_planner = QFlightPlanner(flight, game) self.flight_planner = QFlightPlanner(package, flight, game)
layout.addWidget(self.flight_planner) layout.addWidget(self.flight_planner)
self.setLayout(layout) self.setLayout(layout)

View File

@ -14,6 +14,7 @@ from PySide2.QtWidgets import (
from game.game import Game from game.game import Game
from gen.ato import Package from gen.ato import Package
from gen.flights.flight import Flight from gen.flights.flight import Flight
from gen.flights.flightplan import FlightPlanBuilder
from qt_ui.models import AtoModel, PackageModel from qt_ui.models import AtoModel, PackageModel
from qt_ui.uiconstants import EVENT_ICONS from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.widgets.ato import QFlightList from qt_ui.widgets.ato import QFlightList
@ -100,15 +101,17 @@ class QPackageDialog(QDialog):
def on_add_flight(self) -> None: def on_add_flight(self) -> None:
"""Opens the new flight dialog.""" """Opens the new flight dialog."""
self.add_flight_dialog = QFlightCreator( self.add_flight_dialog = QFlightCreator(self.game,
self.game, self.package_model.package self.package_model.package)
)
self.add_flight_dialog.created.connect(self.add_flight) self.add_flight_dialog.created.connect(self.add_flight)
self.add_flight_dialog.show() self.add_flight_dialog.show()
def add_flight(self, flight: Flight) -> None: def add_flight(self, flight: Flight) -> None:
"""Adds the new flight to the package.""" """Adds the new flight to the package."""
self.package_model.add_flight(flight) self.package_model.add_flight(flight)
planner = FlightPlanBuilder(self.game, self.package_model.package,
is_player=True)
planner.populate_flight_plan(flight)
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.package_changed.emit() self.package_changed.emit()
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences

View File

@ -1,4 +1,3 @@
import logging
from typing import Optional from typing import Optional
from PySide2.QtCore import Qt, Signal from PySide2.QtCore import Qt, Signal
@ -11,15 +10,14 @@ from dcs.planes import PlaneType
from game import Game from game import Game
from gen.ato import Package from gen.ato import Package
from gen.flights.flightplan import FlightPlanBuilder from gen.flights.flight import Flight
from gen.flights.flight import Flight, FlightType
from qt_ui.uiconstants import EVENT_ICONS from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.widgets.QFlightSizeSpinner import QFlightSizeSpinner from qt_ui.widgets.QFlightSizeSpinner import QFlightSizeSpinner
from qt_ui.widgets.QLabeledWidget import QLabeledWidget from qt_ui.widgets.QLabeledWidget import QLabeledWidget
from qt_ui.widgets.combos.QAircraftTypeSelector import QAircraftTypeSelector from qt_ui.widgets.combos.QAircraftTypeSelector import QAircraftTypeSelector
from qt_ui.widgets.combos.QFlightTypeComboBox import QFlightTypeComboBox from qt_ui.widgets.combos.QFlightTypeComboBox import QFlightTypeComboBox
from qt_ui.widgets.combos.QOriginAirfieldSelector import QOriginAirfieldSelector from qt_ui.widgets.combos.QOriginAirfieldSelector import QOriginAirfieldSelector
from theater import ControlPoint, FrontLine, TheaterGroundObject from theater import ControlPoint
class QFlightCreator(QDialog): class QFlightCreator(QDialog):
@ -29,9 +27,6 @@ class QFlightCreator(QDialog):
super().__init__() super().__init__()
self.game = game self.game = game
self.package = package
self.planner = FlightPlanBuilder(self.game, is_player=True)
self.setWindowTitle("Create flight") self.setWindowTitle("Create flight")
self.setWindowIcon(EVENT_ICONS["strike"]) self.setWindowIcon(EVENT_ICONS["strike"])
@ -39,7 +34,7 @@ class QFlightCreator(QDialog):
layout = QVBoxLayout() layout = QVBoxLayout()
self.task_selector = QFlightTypeComboBox( self.task_selector = QFlightTypeComboBox(
self.game.theater, self.package.target self.game.theater, package.target
) )
self.task_selector.setCurrentIndex(0) self.task_selector.setCurrentIndex(0)
layout.addLayout(QLabeledWidget("Task:", self.task_selector)) layout.addLayout(QLabeledWidget("Task:", self.task_selector))
@ -95,7 +90,6 @@ class QFlightCreator(QDialog):
size = self.flight_size_spinner.value() size = self.flight_size_spinner.value()
flight = Flight(aircraft, size, origin, task) flight = Flight(aircraft, size, origin, task)
self.planner.populate_flight_plan(flight, self.package.target)
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.created.emit(flight) self.created.emit(flight)

View File

@ -2,6 +2,7 @@ from PySide2.QtCore import Signal
from PySide2.QtWidgets import QTabWidget from PySide2.QtWidgets import QTabWidget
from game import Game from game import Game
from gen.ato import Package
from gen.flights.flight import Flight from gen.flights.flight import Flight
from qt_ui.windows.mission.flight.payload.QFlightPayloadTab import \ from qt_ui.windows.mission.flight.payload.QFlightPayloadTab import \
QFlightPayloadTab QFlightPayloadTab
@ -15,14 +16,14 @@ class QFlightPlanner(QTabWidget):
on_planned_flight_changed = Signal() on_planned_flight_changed = Signal()
def __init__(self, flight: Flight, game: Game): def __init__(self, package: Package, flight: Flight, game: Game):
super().__init__() super().__init__()
self.general_settings_tab = QGeneralFlightSettingsTab(game, flight) self.general_settings_tab = QGeneralFlightSettingsTab(game, flight)
self.general_settings_tab.on_flight_settings_changed.connect( self.general_settings_tab.on_flight_settings_changed.connect(
lambda: self.on_planned_flight_changed.emit()) lambda: self.on_planned_flight_changed.emit())
self.payload_tab = QFlightPayloadTab(flight, game) self.payload_tab = QFlightPayloadTab(flight, game)
self.waypoint_tab = QFlightWaypointTab(game, flight) self.waypoint_tab = QFlightWaypointTab(game, package, flight)
self.waypoint_tab.on_flight_changed.connect( self.waypoint_tab.on_flight_changed.connect(
lambda: self.on_planned_flight_changed.emit()) lambda: self.on_planned_flight_changed.emit())
self.addTab(self.general_settings_tab, "General Flight settings") self.addTab(self.general_settings_tab, "General Flight settings")

View File

@ -2,6 +2,7 @@ from PySide2.QtCore import Qt
from PySide2.QtWidgets import QDialog, QPushButton from PySide2.QtWidgets import QDialog, QPushButton
from game import Game from game import Game
from gen.ato import Package
from gen.flights.flight import Flight from gen.flights.flight import Flight
from gen.flights.flightplan import FlightPlanBuilder from gen.flights.flightplan import FlightPlanBuilder
from qt_ui.uiconstants import EVENT_ICONS from qt_ui.uiconstants import EVENT_ICONS
@ -10,9 +11,11 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFligh
class QAbstractMissionGenerator(QDialog): class QAbstractMissionGenerator(QDialog):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list, title): def __init__(self, game: Game, package: Package, flight: Flight,
flight_waypoint_list, title) -> None:
super(QAbstractMissionGenerator, self).__init__() super(QAbstractMissionGenerator, self).__init__()
self.game = game self.game = game
self.package = package
self.flight = flight self.flight = flight
self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMinimumSize(400, 250) self.setMinimumSize(400, 250)
@ -20,7 +23,7 @@ class QAbstractMissionGenerator(QDialog):
self.setWindowTitle(title) self.setWindowTitle(title)
self.setWindowIcon(EVENT_ICONS["strike"]) self.setWindowIcon(EVENT_ICONS["strike"])
self.flight_waypoint_list = flight_waypoint_list self.flight_waypoint_list = flight_waypoint_list
self.planner = FlightPlanBuilder(self.game, is_player=True) self.planner = FlightPlanBuilder(self.game, package, is_player=True)
self.selected_waypoints = [] self.selected_waypoints = []
self.wpt_info = QFlightWaypointInfoBox() self.wpt_info = QFlightWaypointInfoBox()

View File

@ -1,15 +1,26 @@
import logging
from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout
from game import Game from game import Game
from gen.flights.flight import Flight, PredefinedWaypointCategory from gen.ato import Package
from gen.flights.flight import Flight, FlightType
from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
from theater import ControlPoint, FrontLine
class QCAPMissionGenerator(QAbstractMissionGenerator): class QCAPMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list): def __init__(self, game: Game, package: Package, flight: Flight,
super(QCAPMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "CAP Generator") flight_waypoint_list) -> None:
super(QCAPMissionGenerator, self).__init__(
game,
package,
flight,
flight_waypoint_list,
"CAP Generator"
)
self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, True, True, False, False, True) self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, True, True, False, False, True)
self.wpt_selection_box.setMinimumWidth(200) self.wpt_selection_box.setMinimumWidth(200)
@ -34,16 +45,22 @@ class QCAPMissionGenerator(QAbstractMissionGenerator):
self.setLayout(layout) self.setLayout(layout)
def apply(self): def apply(self):
self.flight.points = [] location = self.package.target
if isinstance(location, FrontLine):
wpt = self.selected_waypoints[0] self.flight.flight_type = FlightType.TARCAP
if wpt.category == PredefinedWaypointCategory.FRONTLINE: self.planner.populate_flight_plan(self.flight)
self.planner.generate_frontline_cap(self.flight, wpt.data[0], wpt.data[1]) elif isinstance(location, ControlPoint):
elif wpt.category == PredefinedWaypointCategory.ALLY_CP: if location.is_fleet:
self.planner.generate_barcap(self.flight, wpt.data) self.flight.flight_type = FlightType.BARCAP
else: else:
self.flight.flight_type = FlightType.CAP
else:
name = location.__class__.__name__
logging.error(f"Unexpected objective type for CAP: {name}")
return return
self.planner.generate_barcap(self.flight)
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.close() self.close()

View File

@ -4,15 +4,23 @@ from dcs import Point
from game import Game from game import Game
from game.utils import meter_to_nm from game.utils import meter_to_nm
from gen.flights.flight import Flight from gen.ato import Package
from gen.flights.flight import Flight, FlightType
from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
class QCASMissionGenerator(QAbstractMissionGenerator): class QCASMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list): def __init__(self, game: Game, package: Package, flight: Flight,
super(QCASMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "CAS Generator") flight_waypoint_list) -> None:
super(QCASMissionGenerator, self).__init__(
game,
package,
flight,
flight_waypoint_list,
"CAS Generator"
)
self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, False, True, False, False) self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, False, True, False, False)
self.wpt_selection_box.setMinimumWidth(200) self.wpt_selection_box.setMinimumWidth(200)
@ -55,8 +63,8 @@ class QCASMissionGenerator(QAbstractMissionGenerator):
self.setLayout(layout) self.setLayout(layout)
def apply(self): def apply(self):
self.flight.points = [] self.flight.flight_type = FlightType.CAS
self.planner.generate_cas(self.flight, self.selected_waypoints[0].data[0], self.selected_waypoints[0].data[1]) self.planner.populate_flight_plan(self.flight)
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.close() self.close()

View File

@ -3,7 +3,8 @@ from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox
from game import Game from game import Game
from game.utils import meter_to_nm from game.utils import meter_to_nm
from gen.flights.flight import Flight from gen.ato import Package
from gen.flights.flight import Flight, FlightType
from qt_ui.widgets.combos.QSEADTargetSelectionComboBox import QSEADTargetSelectionComboBox from qt_ui.widgets.combos.QSEADTargetSelectionComboBox import QSEADTargetSelectionComboBox
from qt_ui.widgets.views.QSeadTargetInfoView import QSeadTargetInfoView from qt_ui.widgets.views.QSeadTargetInfoView import QSeadTargetInfoView
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
@ -11,8 +12,15 @@ from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAb
class QSEADMissionGenerator(QAbstractMissionGenerator): class QSEADMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list): def __init__(self, game: Game, package: Package, flight: Flight,
super(QSEADMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "SEAD/DEAD Generator") flight_waypoint_list) -> None:
super(QSEADMissionGenerator, self).__init__(
game,
package,
flight,
flight_waypoint_list,
"SEAD/DEAD Generator"
)
self.tgt_selection_box = QSEADTargetSelectionComboBox(self.game) self.tgt_selection_box = QSEADTargetSelectionComboBox(self.game)
self.tgt_selection_box.setMinimumWidth(200) self.tgt_selection_box.setMinimumWidth(200)
@ -73,9 +81,9 @@ class QSEADMissionGenerator(QAbstractMissionGenerator):
self.setLayout(layout) self.setLayout(layout)
def apply(self): def apply(self):
self.flight.points = []
target = self.tgt_selection_box.get_selected_target() target = self.tgt_selection_box.get_selected_target()
self.planner.generate_sead(self.flight, target.location, target.radars) self.flight.flight_type = FlightType.SEAD
self.planner.populate_flight_plan(self.flight, target.radars)
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.close() self.close()

View File

@ -3,7 +3,8 @@ from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox
from game import Game from game import Game
from game.utils import meter_to_nm from game.utils import meter_to_nm
from gen.flights.flight import Flight from gen.ato import Package
from gen.flights.flight import Flight, FlightType
from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import QStrikeTargetSelectionComboBox from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import QStrikeTargetSelectionComboBox
from qt_ui.widgets.views.QStrikeTargetInfoView import QStrikeTargetInfoView from qt_ui.widgets.views.QStrikeTargetInfoView import QStrikeTargetInfoView
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
@ -11,8 +12,15 @@ from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAb
class QSTRIKEMissionGenerator(QAbstractMissionGenerator): class QSTRIKEMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list): def __init__(self, game: Game, package: Package, flight: Flight,
super(QSTRIKEMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "Strike Generator") flight_waypoint_list) -> None:
super(QSTRIKEMissionGenerator, self).__init__(
game,
package,
flight,
flight_waypoint_list,
"Strike Generator"
)
self.tgt_selection_box = QStrikeTargetSelectionComboBox(self.game) self.tgt_selection_box = QStrikeTargetSelectionComboBox(self.game)
self.tgt_selection_box.setMinimumWidth(200) self.tgt_selection_box.setMinimumWidth(200)
@ -53,9 +61,9 @@ class QSTRIKEMissionGenerator(QAbstractMissionGenerator):
self.setLayout(layout) self.setLayout(layout)
def apply(self): def apply(self):
self.flight.points = []
target = self.tgt_selection_box.get_selected_target() target = self.tgt_selection_box.get_selected_target()
self.planner.generate_strike(self.flight, target.location) self.flight.flight_type = FlightType.STRIKE
self.planner.populate_flight_plan(self.flight, target.location)
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.close() self.close()

View File

@ -2,6 +2,7 @@ from PySide2.QtCore import Signal
from PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QPushButton, QVBoxLayout from PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QPushButton, QVBoxLayout
from game import Game from game import Game
from gen.ato import Package
from gen.flights.flight import Flight from gen.flights.flight import Flight
from gen.flights.flightplan import FlightPlanBuilder from gen.flights.flightplan import FlightPlanBuilder
from qt_ui.windows.mission.flight.generator.QCAPMissionGenerator import QCAPMissionGenerator from qt_ui.windows.mission.flight.generator.QCAPMissionGenerator import QCAPMissionGenerator
@ -16,11 +17,12 @@ class QFlightWaypointTab(QFrame):
on_flight_changed = Signal() on_flight_changed = Signal()
def __init__(self, game: Game, flight: Flight): def __init__(self, game: Game, package: Package, flight: Flight):
super(QFlightWaypointTab, self).__init__() super(QFlightWaypointTab, self).__init__()
self.flight = flight
self.game = game self.game = game
self.planner = FlightPlanBuilder(self.game, is_player=True) self.package = package
self.flight = flight
self.planner = FlightPlanBuilder(self.game, package, is_player=True)
self.init_ui() self.init_ui()
def init_ui(self): def init_ui(self):
@ -104,22 +106,42 @@ class QFlightWaypointTab(QFrame):
self.on_change() self.on_change()
def on_cas_generator(self): def on_cas_generator(self):
self.subwindow = QCASMissionGenerator(self.game, self.flight, self.flight_waypoint_list) self.subwindow = QCASMissionGenerator(
self.game,
self.package,
self.flight,
self.flight_waypoint_list
)
self.subwindow.finished.connect(self.on_change) self.subwindow.finished.connect(self.on_change)
self.subwindow.show() self.subwindow.show()
def on_cap_generator(self): def on_cap_generator(self):
self.subwindow = QCAPMissionGenerator(self.game, self.flight, self.flight_waypoint_list) self.subwindow = QCAPMissionGenerator(
self.game,
self.package,
self.flight,
self.flight_waypoint_list
)
self.subwindow.finished.connect(self.on_change) self.subwindow.finished.connect(self.on_change)
self.subwindow.show() self.subwindow.show()
def on_sead_generator(self): def on_sead_generator(self):
self.subwindow = QSEADMissionGenerator(self.game, self.flight, self.flight_waypoint_list) self.subwindow = QSEADMissionGenerator(
self.game,
self.package,
self.flight,
self.flight_waypoint_list
)
self.subwindow.finished.connect(self.on_change) self.subwindow.finished.connect(self.on_change)
self.subwindow.show() self.subwindow.show()
def on_strike_generator(self): def on_strike_generator(self):
self.subwindow = QSTRIKEMissionGenerator(self.game, self.flight, self.flight_waypoint_list) self.subwindow = QSTRIKEMissionGenerator(
self.game,
self.package,
self.flight,
self.flight_waypoint_list
)
self.subwindow.finished.connect(self.on_change) self.subwindow.finished.connect(self.on_change)
self.subwindow.show() self.subwindow.show()