Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator

This commit is contained in:
MetalStormGhost
2024-06-09 20:25:55 +03:00
91 changed files with 1369 additions and 187 deletions

View File

@@ -1,5 +1,6 @@
"""Widgets for displaying air tasking orders."""
import logging
from copy import deepcopy
from typing import Optional
from PySide6.QtCore import (
@@ -143,6 +144,7 @@ class QFlightList(QListView):
)
return
self.package_model.add_flight(clone)
clone.flight_plan.layout = deepcopy(flight.flight_plan.layout)
EventStream.put_nowait(GameUpdateEvents().new_flight(clone))
def cancel_or_abort_flight(self, index: QModelIndex) -> None:

View File

@@ -121,6 +121,8 @@ class PendingTransfersDialog(QDialog):
) -> None:
"""Updates the state of the delete button."""
if selected.empty():
self.cancel_button.setEnabled(False)
self.cancel_button.setEnabled(
self.can_cancel(self.transfer_list.currentIndex())
)
return
self.cancel_button.setEnabled(self.can_cancel(selected.indexes()[0]))

View File

@@ -26,6 +26,8 @@ class QEditFlightDialog(QDialog):
self.game_model = game_model
self.flight = flight
self.package_model = package_model
self.events = GameUpdateEvents()
self.setWindowTitle("Edit flight")
self.setWindowIcon(EVENT_ICONS["strike"])
@@ -34,11 +36,24 @@ class QEditFlightDialog(QDialog):
layout = QVBoxLayout()
self.flight_planner = QFlightPlanner(package_model, flight, game_model)
self.flight_planner.squadron_changed.connect(self.on_squadron_change)
layout.addWidget(self.flight_planner)
self.setLayout(layout)
self.finished.connect(self.on_close)
def on_close(self, _result) -> None:
EventStream.put_nowait(GameUpdateEvents().update_flight(self.flight))
def on_squadron_change(self, flight: Flight):
self.events = GameUpdateEvents().delete_flight(self.flight)
self.events = self.events.new_flight(flight)
self.game_model.ato_model.client_slots_changed.emit()
self.flight = flight
self.reject()
new_dialog = QEditFlightDialog(
self.game_model, self.package_model, flight, self.parent()
)
new_dialog.show()
def on_close(self, _result) -> None:
self.events = self.events.update_flight(self.flight)
EventStream.put_nowait(self.events)
self.game_model.ato_model.client_slots_changed.emit()

View File

@@ -1,3 +1,4 @@
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QTabWidget
from game.ato.flight import Flight
@@ -10,6 +11,8 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import QFlightWay
class QFlightPlanner(QTabWidget):
squadron_changed = Signal(Flight)
def __init__(self, package_model: PackageModel, flight: Flight, gm: GameModel):
super().__init__()
@@ -28,6 +31,7 @@ class QFlightPlanner(QTabWidget):
self.general_settings_tab.flight_size_changed.connect(
self.payload_tab.resize_for_flight
)
self.general_settings_tab.squadron_changed.connect(self.squadron_changed)
self.addTab(self.general_settings_tab, "General Flight settings")
self.addTab(self.payload_tab, "Payload")
self.addTab(self.waypoint_tab, "Waypoints")

View File

@@ -11,14 +11,21 @@ from PySide6.QtWidgets import (
QHBoxLayout,
QCheckBox,
QVBoxLayout,
QPushButton,
QDialog,
QWidget,
)
from game import Game
from game.ato.closestairfields import ClosestAirfields
from game.ato.flight import Flight
from game.ato.flightroster import FlightRoster
from game.ato.iflightroster import IFlightRoster
from game.dcs.aircrafttype import AircraftType
from game.squadrons import Squadron
from game.squadrons.pilot import Pilot
from game.theater import ControlPoint, OffMapSpawn
from game.utils import nautical_miles
from qt_ui.models import PackageModel
@@ -237,8 +244,49 @@ class FlightRosterEditor(QVBoxLayout):
controls.replace(squadron, new_roster)
class QSquadronSelector(QDialog):
def __init__(self, flight: Flight, parent: Optional[QWidget] = None):
super().__init__(parent)
self.flight = flight
self.parent = parent
self.init()
def init(self):
vbox = QVBoxLayout()
self.setLayout(vbox)
self.selector = QComboBox()
air_wing = self.flight.coalition.air_wing
for squadron in air_wing.best_squadrons_for(
self.flight.package.target,
self.flight.flight_type,
self.flight.roster.max_size,
self.flight.is_helo,
True,
ignore_range=True,
):
if squadron is self.flight.squadron:
continue
self.selector.addItem(
f"{squadron.name} - {squadron.aircraft.variant_id}", squadron
)
vbox.addWidget(self.selector)
hbox = QHBoxLayout()
accept = QPushButton("Accept")
accept.clicked.connect(self.accept)
hbox.addWidget(accept)
cancel = QPushButton("Cancel")
cancel.clicked.connect(self.reject)
hbox.addWidget(cancel)
vbox.addLayout(hbox)
class QFlightSlotEditor(QGroupBox):
flight_resized = Signal(int)
squadron_changed = Signal(Flight)
def __init__(
self,
@@ -250,6 +298,10 @@ class QFlightSlotEditor(QGroupBox):
self.package_model = package_model
self.flight = flight
self.game = game
self.closest_airfields = ClosestAirfields(
flight.package.target,
list(game.theater.control_points_for(self.flight.coalition.player)),
)
available = self.flight.squadron.untasked_aircraft
max_count = self.flight.count + available
if max_count > 4:
@@ -268,7 +320,12 @@ class QFlightSlotEditor(QGroupBox):
layout.addWidget(self.aircraft_count_spinner, 0, 1)
layout.addWidget(QLabel("Squadron:"), 1, 0)
layout.addWidget(QLabel(str(self.flight.squadron)), 1, 1)
hbox = QHBoxLayout()
hbox.addWidget(QLabel(str(self.flight.squadron)))
squadron_btn = QPushButton("Change Squadron")
squadron_btn.clicked.connect(self._change_squadron)
hbox.addWidget(squadron_btn)
layout.addLayout(hbox, 1, 1)
layout.addWidget(QLabel("Assigned pilots:"), 2, 0)
self.roster_editor = FlightRosterEditor(flight.squadron, flight.roster)
@@ -276,6 +333,46 @@ class QFlightSlotEditor(QGroupBox):
self.setLayout(layout)
def _change_squadron(self):
dialog = QSquadronSelector(self.flight)
if dialog.exec():
squadron: Optional[Squadron] = dialog.selector.currentData()
if not squadron:
return
flight = Flight(
self.package_model.package,
squadron,
self.flight.count,
self.flight.flight_type,
self.flight.start_type,
self._find_divert_field(squadron.aircraft, squadron.location),
frequency=self.flight.frequency,
cargo=self.flight.cargo,
channel=self.flight.tacan,
callsign_tcn=self.flight.tcn_name,
)
self.package_model.add_flight(flight)
self.package_model.delete_flight(self.flight)
self.squadron_changed.emit(flight)
def _find_divert_field(
self, aircraft: AircraftType, arrival: ControlPoint
) -> Optional[ControlPoint]:
divert_limit = nautical_miles(150)
for airfield in self.closest_airfields.operational_airfields_within(
divert_limit
):
if airfield.captured != self.flight.coalition.player:
continue
if airfield == arrival:
continue
if not airfield.can_operate(aircraft):
continue
if isinstance(airfield, OffMapSpawn):
continue
return airfield
return None
def _changed_aircraft_count(self):
old_count = self.flight.count
new_count = int(self.aircraft_count_spinner.value())

View File

@@ -49,7 +49,9 @@ class QFlightStartType(QGroupBox):
# Otherwise, set the start_type as configured for AI.
# https://github.com/dcs-liberation/dcs_liberation/issues/1567
if self.flight.roster.player_count > 0:
if isinstance(self.flight.departure, OffMapSpawn):
return
elif self.flight.roster.player_count > 0:
self.flight.start_type = (
self.flight.coalition.game.settings.default_start_type_client
)

View File

@@ -21,6 +21,7 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import (
class QGeneralFlightSettingsTab(QFrame):
flight_size_changed = Signal()
squadron_changed = Signal(Flight)
def __init__(
self,
@@ -36,6 +37,7 @@ class QGeneralFlightSettingsTab(QFrame):
self.flight_slot_editor = QFlightSlotEditor(package_model, flight, game.game)
self.flight_slot_editor.flight_resized.connect(self.flight_size_changed)
self.flight_slot_editor.squadron_changed.connect(self.squadron_changed)
for pc in self.flight_slot_editor.roster_editor.pilot_controls:
pc.player_toggled.connect(self.on_player_toggle)
pc.player_toggled.connect(

View File

@@ -146,11 +146,6 @@ class QFactionUnits(QScrollArea):
self, cb: QComboBox, callback: Callable, predicate: Callable
):
for ac_dcs in sorted(AircraftType.each_dcs_type(), key=lambda x: x.id):
if (
ac_dcs not in self.faction.country.planes
and ac_dcs not in self.faction.country.helicopters
):
continue
for ac in AircraftType.for_dcs_type(ac_dcs):
if ac in self.faction.aircraft:
continue