mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
The mechanism for how this bug arises is that the *WaypointGenerator* uses the *FlightWaypoint.waypoint_type* to decide whether to generate the waypoint in the .miz file using a *DeadIngressBuilder* or a *SeadIngressBuilder*. This *waypoint_type* is set by *ato.flightplans.<sead|dead>.Builder*, which is set when *ato.flight* is initialised in the *Flight._flight_plan_builder* member variable based on *Flight.flight_type*. When *Flight.flight_type* is updated when the flight is changed from SEAD->DEAD, *Flight._flight_plan_builder* is not updated in the development build, resulting in it continuing to generate SEAD waypoints. This PR adds *set_flight_type()* which sets the *flight_type* property and updates *Flight._flight_plan_builder* and uses this function when converting flight types. Ideally, *flight_type* should be made private and only accessed through getter/setter functions that encapsulate this behavior, but that would mess up any existing liberation save files. This PR was tested by: 1. Opening the save file from Issue 2779 in the development build 2. Clicking "Take Off" and confirming that the Weapon Release Type is "Guided" at the Ingress Waypoint as described in the issue. 3. Opening the save file from Issue 2779 in this PR 4. Converting the SEAD2DEAD flight from DEAD back to SEAD, and then from SEAD to DEAD 5. Clicking "Take Off" and confirming in the mission editor that the SEAD2DEAD flight has Weapon Release Type set to "Auto" at the Ingress Waypoint. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2779.
182 lines
7.0 KiB
Python
182 lines
7.0 KiB
Python
import logging
|
|
from typing import Iterable, List, Optional
|
|
|
|
from PySide6.QtCore import Signal
|
|
from PySide6.QtWidgets import (
|
|
QFrame,
|
|
QGridLayout,
|
|
QLabel,
|
|
QMessageBox,
|
|
QPushButton,
|
|
QVBoxLayout,
|
|
)
|
|
|
|
from game import Game
|
|
from game.ato.flight import Flight
|
|
from game.ato.flightplans.custom import CustomFlightPlan
|
|
from game.ato.flightplans.formationattack import FormationAttackFlightPlan
|
|
from game.ato.flightplans.planningerror import PlanningError
|
|
from game.ato.flightplans.waypointbuilder import WaypointBuilder
|
|
from game.ato.flighttype import FlightType
|
|
from game.ato.flightwaypoint import FlightWaypoint
|
|
from game.ato.loadouts import Loadout
|
|
from game.ato.package import Package
|
|
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import (
|
|
QFlightWaypointList,
|
|
)
|
|
from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import (
|
|
QPredefinedWaypointSelectionWindow,
|
|
)
|
|
|
|
|
|
class QFlightWaypointTab(QFrame):
|
|
loadout_changed = Signal()
|
|
|
|
def __init__(self, game: Game, package: Package, flight: Flight):
|
|
super(QFlightWaypointTab, self).__init__()
|
|
self.game = game
|
|
self.coalition = game.coalition_for(player=True)
|
|
self.package = package
|
|
self.flight = flight
|
|
|
|
self.flight_waypoint_list: Optional[QFlightWaypointList] = 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()
|
|
|
|
self.flight_waypoint_list = QFlightWaypointList(self.package, self.flight)
|
|
layout.addWidget(self.flight_waypoint_list, 0, 0)
|
|
|
|
rlayout = QVBoxLayout()
|
|
layout.addLayout(rlayout, 0, 1)
|
|
|
|
rlayout.addWidget(QLabel("<strong>Generator :</strong>"))
|
|
rlayout.addWidget(QLabel("<small>AI compatible</small>"))
|
|
|
|
self.recreate_buttons.clear()
|
|
for task in self.package.target.mission_types(for_player=True):
|
|
|
|
if task == FlightType.AIR_ASSAULT and not self.game.settings.plugin_option(
|
|
"ctld"
|
|
):
|
|
# Only add Air Assault if ctld plugin is enabled
|
|
continue
|
|
|
|
def make_closure(arg):
|
|
def closure():
|
|
return self.confirm_recreate(arg)
|
|
|
|
return closure
|
|
|
|
button = QPushButton(f"Recreate as {task}")
|
|
button.clicked.connect(make_closure(task))
|
|
rlayout.addWidget(button)
|
|
self.recreate_buttons.append(button)
|
|
|
|
rlayout.addWidget(QLabel("<strong>Advanced : </strong>"))
|
|
rlayout.addWidget(QLabel("<small>Do not use for AI flights</small>"))
|
|
|
|
self.rtb_waypoint = QPushButton("Add RTB Waypoint")
|
|
self.rtb_waypoint.clicked.connect(self.on_rtb_waypoint)
|
|
rlayout.addWidget(self.rtb_waypoint)
|
|
|
|
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()
|
|
self.setLayout(layout)
|
|
|
|
def on_delete_waypoint(self):
|
|
waypoints = []
|
|
selection = self.flight_waypoint_list.selectionModel()
|
|
for selected_row in selection.selectedIndexes():
|
|
if selected_row.row() > 0:
|
|
waypoints.append(self.flight.flight_plan.waypoints[selected_row.row()])
|
|
for waypoint in waypoints:
|
|
self.delete_waypoint(waypoint)
|
|
self.flight_waypoint_list.update_list()
|
|
self.on_change()
|
|
|
|
def delete_waypoint(self, waypoint: FlightWaypoint) -> None:
|
|
# Need to degrade to a custom flight plan and remove the waypoint.
|
|
# If the waypoint is a target waypoint and is not the last target
|
|
# waypoint, we don't need to degrade.
|
|
if isinstance(self.flight.flight_plan, FormationAttackFlightPlan):
|
|
is_target = waypoint in self.flight.flight_plan.target_area_waypoint.targets
|
|
count = len(self.flight.flight_plan.target_area_waypoint.targets)
|
|
if is_target and count > 1:
|
|
self.flight.flight_plan.target_area_waypoint.targets.remove(waypoint)
|
|
return
|
|
|
|
self.degrade_to_custom_flight_plan()
|
|
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
|
|
self.flight.flight_plan.layout.custom_waypoints.remove(waypoint)
|
|
|
|
def on_fast_waypoint(self):
|
|
self.subwindow = QPredefinedWaypointSelectionWindow(
|
|
self.game, self.flight, self.flight_waypoint_list
|
|
)
|
|
self.subwindow.waypoints_added.connect(self.on_waypoints_added)
|
|
self.subwindow.show()
|
|
|
|
def on_waypoints_added(self, waypoints: Iterable[FlightWaypoint]) -> None:
|
|
if not waypoints:
|
|
return
|
|
self.degrade_to_custom_flight_plan()
|
|
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
|
|
self.flight.flight_plan.layout.custom_waypoints.extend(waypoints)
|
|
self.flight_waypoint_list.update_list()
|
|
self.on_change()
|
|
|
|
def on_rtb_waypoint(self):
|
|
rtb = WaypointBuilder(self.flight, self.coalition).land(self.flight.arrival)
|
|
self.degrade_to_custom_flight_plan()
|
|
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
|
|
self.flight.flight_plan.layout.custom_waypoints.append(rtb)
|
|
self.flight_waypoint_list.update_list()
|
|
self.on_change()
|
|
|
|
def degrade_to_custom_flight_plan(self) -> None:
|
|
if not isinstance(self.flight.flight_plan, CustomFlightPlan):
|
|
self.flight.degrade_to_custom_flight_plan()
|
|
|
|
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,
|
|
)
|
|
original_task = self.flight.flight_type
|
|
if result == QMessageBox.Yes:
|
|
self.flight.set_flight_type(task)
|
|
try:
|
|
self.flight.recreate_flight_plan()
|
|
except PlanningError as ex:
|
|
self.flight.set_flight_type(original_task)
|
|
logging.exception("Could not recreate flight")
|
|
QMessageBox.critical(
|
|
self, "Could not recreate flight", str(ex), QMessageBox.Ok
|
|
)
|
|
if not self.flight.loadout.is_custom:
|
|
self.flight.loadout = Loadout.default_for(self.flight)
|
|
self.loadout_changed.emit()
|
|
self.flight_waypoint_list.update_list()
|
|
self.on_change()
|
|
|
|
def on_change(self):
|
|
self.flight_waypoint_list.update_list()
|