diff --git a/qt_ui/windows/mission/flight/generator/QAbstractMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QAbstractMissionGenerator.py deleted file mode 100644 index 4811da1d..00000000 --- a/qt_ui/windows/mission/flight/generator/QAbstractMissionGenerator.py +++ /dev/null @@ -1,47 +0,0 @@ -from PySide2.QtCore import Qt -from PySide2.QtWidgets import QDialog, QPushButton - -from game import Game -from gen.ato import Package -from gen.flights.flight import Flight -from gen.flights.flightplan import FlightPlanBuilder -from qt_ui.uiconstants import EVENT_ICONS -from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox - - -class QAbstractMissionGenerator(QDialog): - - def __init__(self, game: Game, package: Package, flight: Flight, - flight_waypoint_list, title) -> None: - super(QAbstractMissionGenerator, self).__init__() - self.game = game - self.package = package - self.flight = flight - self.setWindowFlags(Qt.WindowStaysOnTopHint) - self.setMinimumSize(400, 250) - self.setModal(True) - self.setWindowTitle(title) - self.setWindowIcon(EVENT_ICONS["strike"]) - self.flight_waypoint_list = flight_waypoint_list - self.planner = FlightPlanBuilder(self.game, package, is_player=True) - - self.selected_waypoints = [] - self.wpt_info = QFlightWaypointInfoBox() - - self.ok_button = QPushButton("Ok") - self.ok_button.clicked.connect(self.apply) - - def on_select_wpt_changed(self): - self.selected_waypoints = self.wpt_selection_box.get_selected_waypoints(False) - if self.selected_waypoints is None or len(self.selected_waypoints) <= 0: - self.ok_button.setDisabled(True) - else: - self.wpt_info.set_flight_waypoint(self.selected_waypoints[0]) - self.ok_button.setDisabled(False) - - def apply(self): - raise NotImplementedError() - - - - diff --git a/qt_ui/windows/mission/flight/generator/QCAPMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QCAPMissionGenerator.py deleted file mode 100644 index b376851d..00000000 --- a/qt_ui/windows/mission/flight/generator/QCAPMissionGenerator.py +++ /dev/null @@ -1,69 +0,0 @@ -import logging - -from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout - -from game import Game -from gen.ato import Package -from gen.flights.flight import Flight, FlightType -from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox -from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator -from theater import ControlPoint, FrontLine - - -class QCAPMissionGenerator(QAbstractMissionGenerator): - - def __init__(self, game: Game, package: Package, flight: Flight, - 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.setMinimumWidth(200) - self.wpt_selection_box.currentTextChanged.connect(self.on_select_wpt_changed) - - self.init_ui() - self.on_select_wpt_changed() - - def init_ui(self): - layout = QVBoxLayout() - - wpt_layout = QHBoxLayout() - wpt_layout.addWidget(QLabel("CAP mission on : ")) - wpt_layout.addWidget(self.wpt_selection_box) - wpt_layout.addStretch() - - layout.addLayout(wpt_layout) - layout.addWidget(self.wpt_info) - layout.addStretch() - layout.addWidget(self.ok_button) - - self.setLayout(layout) - - def apply(self): - location = self.package.target - if isinstance(location, FrontLine): - self.flight.flight_type = FlightType.TARCAP - self.planner.populate_flight_plan(self.flight) - elif isinstance(location, ControlPoint): - if location.is_fleet: - self.flight.flight_type = FlightType.BARCAP - else: - self.flight.flight_type = FlightType.CAP - else: - name = location.__class__.__name__ - logging.error(f"Unexpected objective type for CAP: {name}") - return - - self.planner.generate_barcap(self.flight) - - self.flight_waypoint_list.update_list() - self.close() - - - - diff --git a/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py deleted file mode 100644 index 76177772..00000000 --- a/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py +++ /dev/null @@ -1,73 +0,0 @@ -from PySide2.QtGui import Qt -from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox -from dcs import Point - -from game import Game -from game.utils import meter_to_nm -from gen.ato import Package -from gen.flights.flight import Flight, FlightType -from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox -from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator - - -class QCASMissionGenerator(QAbstractMissionGenerator): - - def __init__(self, game: Game, package: Package, flight: Flight, - 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.setMinimumWidth(200) - self.wpt_selection_box.currentTextChanged.connect(self.on_select_wpt_changed) - - self.distanceToTargetLabel = QLabel("0 nm") - self.init_ui() - self.on_select_wpt_changed() - - def on_select_wpt_changed(self): - super(QCASMissionGenerator, self).on_select_wpt_changed() - wpts = self.wpt_selection_box.get_selected_waypoints() - - if len(wpts) > 0: - self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(Point(wpts[0].x, wpts[0].y)))) + " nm") - else: - self.distanceToTargetLabel.setText("??? nm") - - def init_ui(self): - layout = QVBoxLayout() - - wpt_layout = QHBoxLayout() - wpt_layout.addWidget(QLabel("CAS : ")) - wpt_layout.addWidget(self.wpt_selection_box) - wpt_layout.addStretch() - - distToTargetBox = QGroupBox("Infos :") - distToTarget = QHBoxLayout() - distToTarget.addWidget(QLabel("Distance to target : ")) - distToTarget.addStretch() - distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight) - distToTargetBox.setLayout(distToTarget) - - layout.addLayout(wpt_layout) - layout.addWidget(self.wpt_info) - layout.addWidget(distToTargetBox) - layout.addStretch() - layout.addWidget(self.ok_button) - - self.setLayout(layout) - - def apply(self): - self.flight.flight_type = FlightType.CAS - self.planner.populate_flight_plan(self.flight) - self.flight_waypoint_list.update_list() - self.close() - - - - diff --git a/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py deleted file mode 100644 index dd3a31f8..00000000 --- a/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py +++ /dev/null @@ -1,92 +0,0 @@ -from PySide2.QtGui import Qt -from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox - -from game import Game -from game.utils import meter_to_nm -from gen.ato import Package -from gen.flights.flight import Flight, FlightType -from qt_ui.widgets.combos.QSEADTargetSelectionComboBox import QSEADTargetSelectionComboBox -from qt_ui.widgets.views.QSeadTargetInfoView import QSeadTargetInfoView -from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator - - -class QSEADMissionGenerator(QAbstractMissionGenerator): - - def __init__(self, game: Game, package: Package, flight: Flight, - 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.setMinimumWidth(200) - self.tgt_selection_box.currentTextChanged.connect(self.on_selected_target_changed) - - self.distanceToTargetLabel = QLabel("0 nm") - self.threatRangeLabel = QLabel("0 nm") - self.detectionRangeLabel = QLabel("0 nm") - self.seadTargetInfoView = QSeadTargetInfoView(None) - self.init_ui() - self.on_selected_target_changed() - - def on_selected_target_changed(self): - target = self.tgt_selection_box.get_selected_target() - if target is not None: - self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(target.location.position))) + " nm") - self.threatRangeLabel.setText(str(meter_to_nm(target.threat_range)) + " nm") - self.detectionRangeLabel.setText(str(meter_to_nm(target.detection_range)) + " nm") - self.seadTargetInfoView.setTarget(target) - - def init_ui(self): - layout = QVBoxLayout() - - wpt_layout = QHBoxLayout() - wpt_layout.addWidget(QLabel("SEAD/DEAD target : ")) - wpt_layout.addStretch() - wpt_layout.addWidget(self.tgt_selection_box, alignment=Qt.AlignRight) - - distThreatBox = QGroupBox("Infos :") - threatLayout = QVBoxLayout() - - distToTarget = QHBoxLayout() - distToTarget.addWidget(QLabel("Distance to site : ")) - distToTarget.addStretch() - distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight) - - threatRangeLayout = QHBoxLayout() - threatRangeLayout.addWidget(QLabel("Site threat range : ")) - threatRangeLayout.addStretch() - threatRangeLayout.addWidget(self.threatRangeLabel, alignment=Qt.AlignRight) - - detectionRangeLayout = QHBoxLayout() - detectionRangeLayout.addWidget(QLabel("Site radar detection range: ")) - detectionRangeLayout.addStretch() - detectionRangeLayout.addWidget(self.detectionRangeLabel, alignment=Qt.AlignRight) - - threatLayout.addLayout(distToTarget) - threatLayout.addLayout(threatRangeLayout) - threatLayout.addLayout(detectionRangeLayout) - distThreatBox.setLayout(threatLayout) - - layout.addLayout(wpt_layout) - layout.addWidget(self.seadTargetInfoView) - layout.addWidget(distThreatBox) - layout.addStretch() - layout.addWidget(self.ok_button) - - self.setLayout(layout) - - def apply(self): - target = self.tgt_selection_box.get_selected_target() - self.flight.flight_type = FlightType.SEAD - self.planner.populate_flight_plan(self.flight, target.radars) - self.flight_waypoint_list.update_list() - self.close() - - - - diff --git a/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py deleted file mode 100644 index c210208c..00000000 --- a/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py +++ /dev/null @@ -1,72 +0,0 @@ -from PySide2.QtGui import Qt -from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox - -from game import Game -from game.utils import meter_to_nm -from gen.ato import Package -from gen.flights.flight import Flight, FlightType -from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import QStrikeTargetSelectionComboBox -from qt_ui.widgets.views.QStrikeTargetInfoView import QStrikeTargetInfoView -from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator - - -class QSTRIKEMissionGenerator(QAbstractMissionGenerator): - - def __init__(self, game: Game, package: Package, flight: Flight, - 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.setMinimumWidth(200) - self.tgt_selection_box.currentTextChanged.connect(self.on_selected_target_changed) - - - self.distanceToTargetLabel = QLabel("0 nm") - self.strike_infos = QStrikeTargetInfoView(None) - self.init_ui() - self.on_selected_target_changed() - - def on_selected_target_changed(self): - target = self.tgt_selection_box.get_selected_target() - self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(target.location.position))) + " nm") - self.strike_infos.setTarget(target) - - def init_ui(self): - layout = QVBoxLayout() - - wpt_layout = QHBoxLayout() - wpt_layout.addWidget(QLabel("Target : ")) - wpt_layout.addStretch() - wpt_layout.addWidget(self.tgt_selection_box, alignment=Qt.AlignRight) - - distToTargetBox = QGroupBox("Infos :") - distToTarget = QHBoxLayout() - distToTarget.addWidget(QLabel("Distance to target : ")) - distToTarget.addStretch() - distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight) - distToTargetBox.setLayout(distToTarget) - - layout.addLayout(wpt_layout) - layout.addWidget(self.strike_infos) - layout.addWidget(distToTargetBox) - layout.addStretch() - layout.addWidget(self.ok_button) - - self.setLayout(layout) - - def apply(self): - target = self.tgt_selection_box.get_selected_target() - self.flight.flight_type = FlightType.STRIKE - self.planner.populate_flight_plan(self.flight, target.location) - self.flight_waypoint_list.update_list() - self.close() - - - - diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py index 7561d639..2879c356 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py @@ -1,16 +1,24 @@ +from typing import List, Optional + from PySide2.QtCore import Signal -from PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QPushButton, QVBoxLayout +from PySide2.QtWidgets import ( + QFrame, + QGridLayout, + QLabel, + QMessageBox, + QPushButton, + QVBoxLayout, +) from game import Game from gen.ato import Package -from gen.flights.flight import Flight +from gen.flights.flight import Flight, FlightType from gen.flights.flightplan import FlightPlanBuilder -from qt_ui.windows.mission.flight.generator.QCAPMissionGenerator import QCAPMissionGenerator -from qt_ui.windows.mission.flight.generator.QCASMissionGenerator import QCASMissionGenerator -from qt_ui.windows.mission.flight.generator.QSEADMissionGenerator import QSEADMissionGenerator -from qt_ui.windows.mission.flight.generator.QSTRIKEMissionGenerator import QSTRIKEMissionGenerator -from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import QFlightWaypointList -from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import QPredefinedWaypointSelectionWindow +from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import \ + QFlightWaypointList +from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import \ + QPredefinedWaypointSelectionWindow +from theater import ControlPoint, FrontLine class QFlightWaypointTab(QFrame): @@ -23,56 +31,68 @@ class QFlightWaypointTab(QFrame): self.package = package self.flight = flight self.planner = FlightPlanBuilder(self.game, package, is_player=True) + + self.flight_waypoint_list: Optional[QFlightWaypointList] = None + self.ascend_waypoint: Optional[QPushButton] = None + self.descend_waypoint: Optional[QPushButton] = None + self.rtb_waypoint: Optional[QPushButton] = None + self.delete_selected: Optional[QPushButton] = None + self.open_fast_waypoint_button: Optional[QPushButton] = None + self.recreate_buttons: List[QPushButton] = [] self.init_ui() def init_ui(self): layout = QGridLayout() - rlayout = QVBoxLayout() + self.flight_waypoint_list = QFlightWaypointList(self.flight) - self.open_fast_waypoint_button = QPushButton("Add Waypoint") - self.open_fast_waypoint_button.clicked.connect(self.on_fast_waypoint) - - self.cas_generator = QPushButton("Gen. CAS") - self.cas_generator.clicked.connect(self.on_cas_generator) - - self.cap_generator = QPushButton("Gen. CAP") - self.cap_generator.clicked.connect(self.on_cap_generator) - - self.sead_generator = QPushButton("Gen. SEAD/DEAD") - self.sead_generator.clicked.connect(self.on_sead_generator) - - self.strike_generator = QPushButton("Gen. STRIKE") - self.strike_generator.clicked.connect(self.on_strike_generator) - - self.rtb_waypoint = QPushButton("Add RTB Waypoint") - self.rtb_waypoint.clicked.connect(self.on_rtb_waypoint) - - self.ascend_waypoint = QPushButton("Add Ascend Waypoint") - self.ascend_waypoint.clicked.connect(self.on_ascend_waypoint) - - self.descend_waypoint = QPushButton("Add Descend Waypoint") - self.descend_waypoint.clicked.connect(self.on_descend_waypoint) - - self.delete_selected = QPushButton("Delete Selected") - self.delete_selected.clicked.connect(self.on_delete_waypoint) - layout.addWidget(self.flight_waypoint_list, 0, 0) + rlayout = QVBoxLayout() + layout.addLayout(rlayout, 0, 1) + rlayout.addWidget(QLabel("Generator :")) rlayout.addWidget(QLabel("AI compatible")) - rlayout.addWidget(self.cas_generator) - rlayout.addWidget(self.cap_generator) - rlayout.addWidget(self.sead_generator) - rlayout.addWidget(self.strike_generator) + + self.recreate_buttons.clear() + recreate_types = [ + FlightType.CAS, + FlightType.CAP, + FlightType.SEAD, + FlightType.STRIKE + ] + for task in recreate_types: + def make_closure(arg): + def closure(): + return self.confirm_recreate(arg) + return closure + button = QPushButton(f"Recreate as {task.name}") + button.clicked.connect(make_closure(task)) + rlayout.addWidget(button) + self.recreate_buttons.append(button) + rlayout.addWidget(QLabel("Advanced : ")) rlayout.addWidget(QLabel("Do not use for AI flights")) + + self.ascend_waypoint = QPushButton("Add Ascend Waypoint") + self.ascend_waypoint.clicked.connect(self.on_ascend_waypoint) rlayout.addWidget(self.ascend_waypoint) + + self.descend_waypoint = QPushButton("Add Descend Waypoint") + self.descend_waypoint.clicked.connect(self.on_descend_waypoint) rlayout.addWidget(self.descend_waypoint) + + self.rtb_waypoint = QPushButton("Add RTB Waypoint") + self.rtb_waypoint.clicked.connect(self.on_rtb_waypoint) rlayout.addWidget(self.rtb_waypoint) - rlayout.addWidget(self.open_fast_waypoint_button) + + self.delete_selected = QPushButton("Delete Selected") + self.delete_selected.clicked.connect(self.on_delete_waypoint) rlayout.addWidget(self.delete_selected) + + self.open_fast_waypoint_button = QPushButton("Add Waypoint") + self.open_fast_waypoint_button.clicked.connect(self.on_fast_waypoint) + rlayout.addWidget(self.open_fast_waypoint_button) rlayout.addStretch() - layout.addLayout(rlayout, 0, 1) self.setLayout(layout) def on_delete_waypoint(self): @@ -105,45 +125,27 @@ class QFlightWaypointTab(QFrame): self.flight_waypoint_list.update_list() self.on_change() - def on_cas_generator(self): - self.subwindow = QCASMissionGenerator( - self.game, - self.package, - self.flight, - self.flight_waypoint_list + def confirm_recreate(self, task: FlightType) -> None: + result = QMessageBox.question( + self, + "Regenerate flight?", + ("Changing the flight type will reset its flight plan. Do you want " + "to continue?"), + QMessageBox.No, + QMessageBox.Yes ) - self.subwindow.finished.connect(self.on_change) - self.subwindow.show() - - def on_cap_generator(self): - self.subwindow = QCAPMissionGenerator( - self.game, - self.package, - self.flight, - self.flight_waypoint_list - ) - self.subwindow.finished.connect(self.on_change) - self.subwindow.show() - - def on_sead_generator(self): - self.subwindow = QSEADMissionGenerator( - self.game, - self.package, - self.flight, - self.flight_waypoint_list - ) - self.subwindow.finished.connect(self.on_change) - self.subwindow.show() - - def on_strike_generator(self): - self.subwindow = QSTRIKEMissionGenerator( - self.game, - self.package, - self.flight, - self.flight_waypoint_list - ) - self.subwindow.finished.connect(self.on_change) - self.subwindow.show() + if result == QMessageBox.Yes: + # TODO: These should all be just CAP. + if task == FlightType.CAP: + if isinstance(self.package.target, FrontLine): + task = FlightType.TARCAP + elif isinstance(self.package.target, ControlPoint): + if self.package.target.is_fleet: + task = FlightType.BARCAP + self.flight.flight_type = task + self.planner.populate_flight_plan(self.flight) + self.flight_waypoint_list.update_list() + self.on_change() def on_change(self): self.flight_waypoint_list.update_list()