Implement manual squadron transfers.

Lightly tested but seems to work fine.

https://github.com/dcs-liberation/dcs_liberation/issues/1145
This commit is contained in:
Dan Albert
2021-08-28 18:09:33 -07:00
parent cd15de6d42
commit c2e5cba061
7 changed files with 148 additions and 43 deletions

17
qt_ui/errorreporter.py Normal file
View File

@@ -0,0 +1,17 @@
import logging
from collections import Iterator
from contextlib import contextmanager
from typing import Type
from PySide2.QtWidgets import QDialog, QMessageBox
@contextmanager
def report_errors(
title: str, parent: QDialog, error_type: Type[Exception] = Exception
) -> Iterator[None]:
try:
yield
except error_type as ex:
logging.exception(title)
QMessageBox().critical(parent, title, str(ex), QMessageBox.Ok)

View File

@@ -13,6 +13,7 @@ from PySide2.QtWidgets import QApplication, QSplashScreen
from dcs.payloads import PayloadDirectories
from game import Game, VERSION, persistency
from game.campaignloader.campaign import Campaign
from game.data.weapons import WeaponGroup, Pylon, Weapon
from game.db import FACTIONS
from game.dcs.aircrafttype import AircraftType
@@ -27,7 +28,6 @@ from qt_ui import (
)
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.QLiberationWindow import QLiberationWindow
from game.campaignloader.campaign import Campaign
from qt_ui.windows.newgame.QNewGameWizard import DEFAULT_BUDGET
from qt_ui.windows.preferences.QLiberationFirstStartWindow import (
QLiberationFirstStartWindow,

View File

@@ -10,7 +10,6 @@ from PySide2.QtCore import (
)
from PySide2.QtGui import QStandardItemModel, QStandardItem, QIcon
from PySide2.QtWidgets import (
QAbstractItemView,
QDialog,
QListView,
QVBoxLayout,
@@ -32,38 +31,7 @@ from game.dcs.aircrafttype import AircraftType
from game.squadrons import AirWing, Pilot, Squadron
from game.theater import ControlPoint, ConflictTheater
from gen.flights.flight import FlightType
from qt_ui.models import AirWingModel, SquadronModel
from qt_ui.uiconstants import AIRCRAFT_ICONS
from qt_ui.windows.AirWingDialog import SquadronDelegate
from qt_ui.windows.SquadronDialog import SquadronDialog
class SquadronList(QListView):
"""List view for displaying the air wing's squadrons."""
def __init__(self, air_wing_model: AirWingModel) -> None:
super().__init__()
self.air_wing_model = air_wing_model
self.dialog: Optional[SquadronDialog] = None
self.setIconSize(QSize(91, 24))
self.setItemDelegate(SquadronDelegate(self.air_wing_model))
self.setModel(self.air_wing_model)
self.selectionModel().setCurrentIndex(
self.air_wing_model.index(0, 0, QModelIndex()), QItemSelectionModel.Select
)
# self.setIconSize(QSize(91, 24))
self.setSelectionBehavior(QAbstractItemView.SelectItems)
self.doubleClicked.connect(self.on_double_click)
def on_double_click(self, index: QModelIndex) -> None:
if not index.isValid():
return
self.dialog = SquadronDialog(
SquadronModel(self.air_wing_model.squadron_at_index(index)), self
)
self.dialog.show()
class AllowedMissionTypeControls(QVBoxLayout):

View File

@@ -17,6 +17,7 @@ from PySide2.QtWidgets import (
)
from game.squadrons import Squadron
from game.theater import ConflictTheater
from gen.flights.flight import Flight
from qt_ui.delegates import TwoColumnRowDelegate
from qt_ui.models import GameModel, AirWingModel, SquadronModel
@@ -56,9 +57,10 @@ class SquadronDelegate(TwoColumnRowDelegate):
class SquadronList(QListView):
"""List view for displaying the air wing's squadrons."""
def __init__(self, air_wing_model: AirWingModel) -> None:
def __init__(self, air_wing_model: AirWingModel, theater: ConflictTheater) -> None:
super().__init__()
self.air_wing_model = air_wing_model
self.theater = theater
self.dialog: Optional[SquadronDialog] = None
self.setIconSize(QSize(91, 24))
@@ -76,7 +78,9 @@ class SquadronList(QListView):
if not index.isValid():
return
self.dialog = SquadronDialog(
SquadronModel(self.air_wing_model.squadron_at_index(index)), self
SquadronModel(self.air_wing_model.squadron_at_index(index)),
self.theater,
self,
)
self.dialog.show()
@@ -194,7 +198,10 @@ class AirWingTabs(QTabWidget):
def __init__(self, game_model: GameModel) -> None:
super().__init__()
self.addTab(SquadronList(game_model.blue_air_wing_model), "Squadrons")
self.addTab(
SquadronList(game_model.blue_air_wing_model, game_model.game.theater),
"Squadrons",
)
self.addTab(AirInventoryView(game_model), "Inventory")

View File

@@ -1,5 +1,5 @@
import logging
from typing import Callable
from typing import Callable, Iterator, Optional
from PySide2.QtCore import (
QItemSelectionModel,
@@ -16,11 +16,14 @@ from PySide2.QtWidgets import (
QHBoxLayout,
QLabel,
QCheckBox,
QComboBox,
)
from game.squadrons import Pilot
from game.squadrons import Pilot, Squadron
from game.theater import ControlPoint, ConflictTheater
from gen.flights.flight import FlightType
from qt_ui.delegates import TwoColumnRowDelegate
from qt_ui.errorreporter import report_errors
from qt_ui.models import SquadronModel
@@ -90,10 +93,50 @@ class AutoAssignedTaskControls(QVBoxLayout):
self.squadron_model.set_auto_assignable(task, checked)
class SquadronDestinationComboBox(QComboBox):
def __init__(self, squadron: Squadron, theater: ConflictTheater) -> None:
super().__init__()
self.squadron = squadron
self.theater = theater
room = squadron.location.unclaimed_parking()
self.addItem(
f"Remain at {squadron.location} (room for {room} more aircraft)", None
)
selected_index: Optional[int] = None
for idx, destination in enumerate(sorted(self.iter_destinations(), key=str), 1):
if destination == squadron.destination:
selected_index = idx
room = destination.unclaimed_parking()
self.addItem(
f"Transfer to {destination} (room for {room} more aircraft)",
destination,
)
if squadron.destination is None:
selected_index = 0
if selected_index is not None:
self.setCurrentIndex(selected_index)
def iter_destinations(self) -> Iterator[ControlPoint]:
size = self.squadron.expected_size_next_turn
for control_point in self.theater.control_points_for(self.squadron.player):
if control_point == self:
continue
if not control_point.can_operate(self.squadron.aircraft):
continue
if control_point.unclaimed_parking() < size:
continue
yield control_point
class SquadronDialog(QDialog):
"""Dialog window showing a squadron."""
def __init__(self, squadron_model: SquadronModel, parent) -> None:
def __init__(
self, squadron_model: SquadronModel, theater: ConflictTheater, parent
) -> None:
super().__init__(parent)
self.squadron_model = squadron_model
@@ -117,6 +160,15 @@ class SquadronDialog(QDialog):
columns.addWidget(self.pilot_list)
button_panel = QHBoxLayout()
self.transfer_destination = SquadronDestinationComboBox(
squadron_model.squadron, theater
)
self.transfer_destination.currentIndexChanged.connect(
self.on_destination_changed
)
button_panel.addWidget(self.transfer_destination)
button_panel.addStretch()
layout.addLayout(button_panel)
@@ -132,6 +184,18 @@ class SquadronDialog(QDialog):
self.toggle_leave_button.clicked.connect(self.toggle_leave)
button_panel.addWidget(self.toggle_leave_button, alignment=Qt.AlignRight)
@property
def squadron(self) -> Squadron:
return self.squadron_model.squadron
def on_destination_changed(self, index: int) -> None:
with report_errors("Could not change squadron destination", self):
destination = self.transfer_destination.itemData(index)
if destination is None:
self.squadron.cancel_relocation()
else:
self.squadron.plan_relocation(destination)
def check_disabled_button_states(
self, button: QPushButton, index: QModelIndex
) -> bool: