Convert flight creator to pilot roster.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1143
This commit is contained in:
Dan Albert
2021-06-02 01:48:39 -07:00
parent 8edb952800
commit 558502d8ea
9 changed files with 147 additions and 71 deletions

View File

@@ -115,7 +115,7 @@ class AircraftInventoryData:
flight_type = flight.flight_type.value
target = flight.package.target.name
for idx in range(0, num_units):
pilot = flight.pilots[idx]
pilot = flight.roster.pilots[idx]
if pilot is None:
pilot_name = "Unassigned"
player = ""

View File

@@ -10,6 +10,7 @@ from PySide2.QtWidgets import (
QPushButton,
QVBoxLayout,
QLineEdit,
QHBoxLayout,
)
from dcs.unittype import FlyingType
@@ -17,7 +18,7 @@ from game import Game
from game.squadrons import Squadron
from game.theater import ControlPoint, OffMapSpawn
from gen.ato import Package
from gen.flights.flight import Flight
from gen.flights.flight import Flight, FlightRoster
from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.widgets.QFlightSizeSpinner import QFlightSizeSpinner
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
@@ -26,6 +27,7 @@ from qt_ui.widgets.combos.QArrivalAirfieldSelector import QArrivalAirfieldSelect
from qt_ui.widgets.combos.QFlightTypeComboBox import QFlightTypeComboBox
from qt_ui.widgets.combos.QOriginAirfieldSelector import QOriginAirfieldSelector
from qt_ui.windows.mission.flight.SquadronSelector import SquadronSelector
from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import FlightRosterEditor
class QFlightCreator(QDialog):
@@ -46,7 +48,7 @@ class QFlightCreator(QDialog):
self.task_selector = QFlightTypeComboBox(self.game.theater, package.target)
self.task_selector.setCurrentIndex(0)
self.task_selector.currentTextChanged.connect(self.on_task_changed)
self.task_selector.currentIndexChanged.connect(self.on_task_changed)
layout.addLayout(QLabeledWidget("Task:", self.task_selector))
self.aircraft_selector = QAircraftTypeSelector(
@@ -93,13 +95,20 @@ class QFlightCreator(QDialog):
self.update_max_size(self.departure.available)
layout.addLayout(QLabeledWidget("Size:", self.flight_size_spinner))
self.client_slots_spinner = QFlightSizeSpinner(
min_size=0, max_size=self.flight_size_spinner.value(), default_size=0
)
self.flight_size_spinner.valueChanged.connect(
lambda v: self.client_slots_spinner.setMaximum(v)
)
layout.addLayout(QLabeledWidget("Client Slots:", self.client_slots_spinner))
squadron = self.squadron_selector.currentData()
if squadron is None:
roster = None
else:
roster = FlightRoster(
squadron, initial_size=self.flight_size_spinner.value()
)
self.roster_editor = FlightRosterEditor(roster)
self.flight_size_spinner.valueChanged.connect(self.resize_roster)
self.squadron_selector.currentIndexChanged.connect(self.on_squadron_changed)
roster_layout = QHBoxLayout()
layout.addLayout(roster_layout)
roster_layout.addWidget(QLabel("Assigned pilots:"))
roster_layout.addLayout(self.roster_editor)
# When an off-map spawn overrides the start type to in-flight, we save
# the selected type into this value. If a non-off-map spawn is selected
@@ -142,6 +151,10 @@ class QFlightCreator(QDialog):
def set_custom_name_text(self, text: str):
self.custom_name_text = text
def resize_roster(self, new_size: int) -> None:
self.roster_editor.roster.resize(new_size)
self.roster_editor.resize(new_size)
def verify_form(self) -> Optional[str]:
aircraft: Optional[Type[FlyingType]] = self.aircraft_selector.currentData()
squadron: Optional[Squadron] = self.squadron_selector.currentData()
@@ -181,7 +194,7 @@ class QFlightCreator(QDialog):
origin = self.departure.currentData()
arrival = self.arrival.currentData()
divert = self.divert.currentData()
size = self.flight_size_spinner.value()
roster = self.roster_editor.roster
if arrival is None:
arrival = origin
@@ -190,22 +203,17 @@ class QFlightCreator(QDialog):
self.package,
self.country,
squadron,
size,
# A bit of a hack to work around the old API. Not actually relevant because
# the roster is passed explicitly. Needs a refactor.
roster.max_size,
task,
self.start_type.currentText(),
origin,
arrival,
divert,
custom_name=self.custom_name_text,
roster=roster,
)
for pilot, idx in zip(flight.pilots, range(self.client_slots_spinner.value())):
if pilot is None:
logging.error(
f"Cannot create client slot because {flight} has no pilot for "
f"aircraft {idx}"
)
continue
pilot.player = True
# noinspection PyUnresolvedReferences
self.created.emit(flight)
@@ -234,14 +242,22 @@ class QFlightCreator(QDialog):
self.start_type.setCurrentText(self.restore_start_type)
self.restore_start_type = None
def on_task_changed(self) -> None:
def on_task_changed(self, index: int) -> None:
task = self.task_selector.itemData(index)
self.aircraft_selector.update_items(
self.task_selector.currentData(),
self.game.aircraft_inventory.available_types_for_player,
)
self.squadron_selector.update_items(
self.task_selector.currentData(), self.aircraft_selector.currentData()
task, self.game.aircraft_inventory.available_types_for_player
)
self.squadron_selector.update_items(task, self.aircraft_selector.currentData())
def on_squadron_changed(self, index: int) -> None:
squadron = self.squadron_selector.itemData(index)
# Clear the roster first so we return the pilots to the pool. This way if we end
# up repopulating from the same squadron we'll get the same pilots back.
self.roster_editor.replace(None)
if squadron is not None:
self.roster_editor.replace(
FlightRoster(squadron, self.flight_size_spinner.value())
)
def update_max_size(self, available: int) -> None:
self.flight_size_spinner.setMaximum(min(available, 4))

View File

@@ -28,7 +28,11 @@ class SquadronSelector(QComboBox):
self, task: Optional[FlightType], aircraft: Optional[Type[FlyingType]]
) -> None:
current_squadron = self.currentData()
self.clear()
self.blockSignals(True)
try:
self.clear()
finally:
self.blockSignals(False)
if task is None:
self.addItem("No task selected", None)
return

View File

@@ -15,16 +15,16 @@ from PySide2.QtWidgets import (
from game import Game
from game.squadrons import Pilot
from gen.flights.flight import Flight
from gen.flights.flight import Flight, FlightRoster
from qt_ui.models import PackageModel
class PilotSelector(QComboBox):
available_pilots_changed = Signal()
def __init__(self, flight: Flight, idx: int) -> None:
def __init__(self, roster: Optional[FlightRoster], idx: int) -> None:
super().__init__()
self.flight = flight
self.roster = roster
self.pilot_index = idx
self.rebuild()
@@ -34,15 +34,15 @@ class PilotSelector(QComboBox):
def _do_rebuild(self) -> None:
self.clear()
if self.pilot_index >= self.flight.count:
if self.roster is None or self.pilot_index >= self.roster.max_size:
self.addItem("No aircraft", None)
self.setDisabled(True)
return
self.setEnabled(True)
self.addItem("Unassigned", None)
choices = list(self.flight.squadron.available_pilots)
current_pilot = self.flight.pilots[self.pilot_index]
choices = list(self.roster.squadron.available_pilots)
current_pilot = self.roster.pilots[self.pilot_index]
if current_pilot is not None:
choices.append(current_pilot)
# Put players first, otherwise alphabetically.
@@ -70,19 +70,23 @@ class PilotSelector(QComboBox):
# The roster resize is handled separately, so we have no pilots to remove.
return
pilot = self.itemData(index)
if pilot == self.flight.pilots[self.pilot_index]:
if pilot == self.roster.pilots[self.pilot_index]:
return
self.flight.set_pilot(self.pilot_index, pilot)
self.roster.set_pilot(self.pilot_index, pilot)
self.available_pilots_changed.emit()
def replace(self, new_roster: Optional[FlightRoster]) -> None:
self.roster = new_roster
self.rebuild()
class PilotControls(QHBoxLayout):
def __init__(self, flight: Flight, idx: int) -> None:
def __init__(self, roster: Optional[FlightRoster], idx: int) -> None:
super().__init__()
self.flight = flight
self.roster = roster
self.pilot_index = idx
self.selector = PilotSelector(flight, idx)
self.selector = PilotSelector(roster, idx)
self.selector.currentIndexChanged.connect(self.on_pilot_changed)
self.addWidget(self.selector)
@@ -95,9 +99,9 @@ class PilotControls(QHBoxLayout):
@property
def pilot(self) -> Optional[Pilot]:
if self.pilot_index >= self.flight.count:
if self.roster is None or self.pilot_index >= self.roster.max_size:
return None
return self.flight.pilots[self.pilot_index]
return self.roster.pilots[self.pilot_index]
def on_player_toggled(self, checked: bool) -> None:
pilot = self.pilot
@@ -130,12 +134,21 @@ class PilotControls(QHBoxLayout):
finally:
self.player_checkbox.blockSignals(False)
def replace(self, new_roster: Optional[FlightRoster]) -> None:
self.roster = new_roster
if self.roster is None or self.pilot_index >= self.roster.max_size:
self.disable_and_clear()
else:
self.enable_and_reset()
self.selector.replace(new_roster)
class FlightRosterEditor(QVBoxLayout):
MAX_PILOTS = 4
def __init__(self, flight: Flight) -> None:
def __init__(self, roster: Optional[FlightRoster]) -> None:
super().__init__()
self.roster = roster
self.pilot_controls = []
for pilot_idx in range(self.MAX_PILOTS):
@@ -146,7 +159,7 @@ class FlightRosterEditor(QVBoxLayout):
return callback
controls = PilotControls(flight, pilot_idx)
controls = PilotControls(roster, pilot_idx)
controls.selector.available_pilots_changed.connect(
make_reset_callback(pilot_idx)
)
@@ -167,6 +180,13 @@ class FlightRosterEditor(QVBoxLayout):
for controls in self.pilot_controls[new_size:]:
controls.disable_and_clear()
def replace(self, new_roster: Optional[FlightRoster]) -> None:
if self.roster is not None:
self.roster.clear()
self.roster = new_roster
for controls in self.pilot_controls:
controls.replace(new_roster)
class QFlightSlotEditor(QGroupBox):
def __init__(self, package_model: PackageModel, flight: Flight, game: Game):
@@ -196,7 +216,7 @@ class QFlightSlotEditor(QGroupBox):
layout.addWidget(QLabel(str(self.flight.squadron)), 1, 1)
layout.addWidget(QLabel("Assigned pilots:"), 2, 0)
self.roster_editor = FlightRosterEditor(flight)
self.roster_editor = FlightRosterEditor(flight.roster)
layout.addLayout(self.roster_editor, 2, 1)
self.setLayout(layout)