mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Convert flight creator to pilot roster.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1143
This commit is contained in:
parent
8edb952800
commit
558502d8ea
@ -144,7 +144,7 @@ class Event:
|
|||||||
def _commit_pilot_experience(ato: AirTaskingOrder) -> None:
|
def _commit_pilot_experience(ato: AirTaskingOrder) -> None:
|
||||||
for package in ato.packages:
|
for package in ato.packages:
|
||||||
for flight in package.flights:
|
for flight in package.flights:
|
||||||
for idx, pilot in enumerate(flight.pilots):
|
for idx, pilot in enumerate(flight.roster.pilots):
|
||||||
if pilot is None:
|
if pilot is None:
|
||||||
logging.error(
|
logging.error(
|
||||||
f"Cannot award experience to pilot #{idx} of {flight} "
|
f"Cannot award experience to pilot #{idx} of {flight} "
|
||||||
|
|||||||
@ -13,7 +13,6 @@ from typing import (
|
|||||||
List,
|
List,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Optional,
|
Optional,
|
||||||
Iterable,
|
|
||||||
Iterator,
|
Iterator,
|
||||||
Sequence,
|
Sequence,
|
||||||
)
|
)
|
||||||
@ -142,8 +141,12 @@ class Squadron:
|
|||||||
def return_pilot(self, pilot: Pilot) -> None:
|
def return_pilot(self, pilot: Pilot) -> None:
|
||||||
self.available_pilots.append(pilot)
|
self.available_pilots.append(pilot)
|
||||||
|
|
||||||
def return_pilots(self, pilots: Iterable[Pilot]) -> None:
|
def return_pilots(self, pilots: Sequence[Pilot]) -> None:
|
||||||
self.available_pilots.extend(pilots)
|
# Return in reverse so that returning two pilots and then getting two more
|
||||||
|
# results in the same ordering. This happens commonly when resetting rosters in
|
||||||
|
# the UI, when we clear the roster because the UI is updating, then end up
|
||||||
|
# repopulating the same size flight from the same squadron.
|
||||||
|
self.available_pilots.extend(reversed(pilots))
|
||||||
|
|
||||||
def enlist_new_pilots(self, count: int) -> None:
|
def enlist_new_pilots(self, count: int) -> None:
|
||||||
new_pilots = [Pilot(self.faker.name()) for _ in range(count)]
|
new_pilots = [Pilot(self.faker.name()) for _ in range(count)]
|
||||||
|
|||||||
@ -62,7 +62,7 @@ class UnitMap:
|
|||||||
self.airlifts: Dict[str, AirliftUnit] = {}
|
self.airlifts: Dict[str, AirliftUnit] = {}
|
||||||
|
|
||||||
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None:
|
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None:
|
||||||
for pilot, unit in zip(flight.pilots, group.units):
|
for pilot, unit in zip(flight.roster.pilots, group.units):
|
||||||
# The actual name is a String (the pydcs translatable string), which
|
# The actual name is a String (the pydcs translatable string), which
|
||||||
# doesn't define __eq__.
|
# doesn't define __eq__.
|
||||||
name = str(unit.name)
|
name = str(unit.name)
|
||||||
|
|||||||
@ -803,7 +803,7 @@ class AircraftConflictGenerator:
|
|||||||
self._setup_payload(flight, group)
|
self._setup_payload(flight, group)
|
||||||
self._setup_livery(flight, group)
|
self._setup_livery(flight, group)
|
||||||
|
|
||||||
for unit, pilot in zip(group.units, flight.pilots):
|
for unit, pilot in zip(group.units, flight.roster.pilots):
|
||||||
player = pilot is not None and pilot.player
|
player = pilot is not None and pilot.player
|
||||||
self.set_skill(unit, pilot, blue=flight.departure.captured)
|
self.set_skill(unit, pilot, blue=flight.departure.captured)
|
||||||
# Do not generate player group with late activation.
|
# Do not generate player group with late activation.
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List, Optional, TYPE_CHECKING, Type, Union
|
from typing import List, Optional, TYPE_CHECKING, Type, Union
|
||||||
@ -200,6 +201,49 @@ class FlightWaypoint:
|
|||||||
return waypoint
|
return waypoint
|
||||||
|
|
||||||
|
|
||||||
|
class FlightRoster:
|
||||||
|
def __init__(self, squadron: Squadron, initial_size: int = 0) -> None:
|
||||||
|
self.squadron = squadron
|
||||||
|
self.pilots: list[Optional[Pilot]] = []
|
||||||
|
self.resize(initial_size)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_size(self) -> int:
|
||||||
|
return len(self.pilots)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def player_count(self) -> int:
|
||||||
|
return len([p for p in self.pilots if p is not None and p.player])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def missing_pilots(self) -> int:
|
||||||
|
return len([p for p in self.pilots if p is None])
|
||||||
|
|
||||||
|
def resize(self, new_size: int) -> None:
|
||||||
|
if self.max_size > new_size:
|
||||||
|
self.squadron.return_pilots(
|
||||||
|
[p for p in self.pilots[new_size:] if p is not None]
|
||||||
|
)
|
||||||
|
self.pilots = self.pilots[:new_size]
|
||||||
|
return
|
||||||
|
self.pilots.extend(
|
||||||
|
[
|
||||||
|
self.squadron.claim_available_pilot()
|
||||||
|
for _ in range(new_size - self.max_size)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_pilot(self, index: int, pilot: Optional[Pilot]) -> None:
|
||||||
|
if pilot is not None:
|
||||||
|
self.squadron.claim_pilot(pilot)
|
||||||
|
if (current_pilot := self.pilots[index]) is not None:
|
||||||
|
self.squadron.return_pilot(current_pilot)
|
||||||
|
self.pilots[index] = pilot
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
self.squadron.return_pilots([p for p in self.pilots if p is not None])
|
||||||
|
|
||||||
|
|
||||||
class Flight:
|
class Flight:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -214,11 +258,15 @@ class Flight:
|
|||||||
divert: Optional[ControlPoint],
|
divert: Optional[ControlPoint],
|
||||||
custom_name: Optional[str] = None,
|
custom_name: Optional[str] = None,
|
||||||
cargo: Optional[TransferOrder] = None,
|
cargo: Optional[TransferOrder] = None,
|
||||||
|
roster: Optional[FlightRoster] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.package = package
|
self.package = package
|
||||||
self.country = country
|
self.country = country
|
||||||
self.squadron = squadron
|
self.squadron = squadron
|
||||||
self.pilots = [squadron.claim_available_pilot() for _ in range(count)]
|
if roster is None:
|
||||||
|
self.roster = FlightRoster(self.squadron, initial_size=count)
|
||||||
|
else:
|
||||||
|
self.roster = roster
|
||||||
self.departure = departure
|
self.departure = departure
|
||||||
self.arrival = arrival
|
self.arrival = arrival
|
||||||
self.divert = divert
|
self.divert = divert
|
||||||
@ -244,11 +292,11 @@ class Flight:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def count(self) -> int:
|
def count(self) -> int:
|
||||||
return len(self.pilots)
|
return self.roster.max_size
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def client_count(self) -> int:
|
def client_count(self) -> int:
|
||||||
return len([p for p in self.pilots if p is not None and p.player])
|
return self.roster.player_count
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_type(self) -> Type[FlyingType]:
|
def unit_type(self) -> Type[FlyingType]:
|
||||||
@ -263,32 +311,17 @@ class Flight:
|
|||||||
return self.flight_plan.waypoints[1:]
|
return self.flight_plan.waypoints[1:]
|
||||||
|
|
||||||
def resize(self, new_size: int) -> None:
|
def resize(self, new_size: int) -> None:
|
||||||
if self.count > new_size:
|
self.roster.resize(new_size)
|
||||||
self.squadron.return_pilots(
|
|
||||||
p for p in self.pilots[new_size:] if p is not None
|
|
||||||
)
|
|
||||||
self.pilots = self.pilots[:new_size]
|
|
||||||
return
|
|
||||||
self.pilots.extend(
|
|
||||||
[
|
|
||||||
self.squadron.claim_available_pilot()
|
|
||||||
for _ in range(new_size - self.count)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_pilot(self, index: int, pilot: Optional[Pilot]) -> None:
|
def set_pilot(self, index: int, pilot: Optional[Pilot]) -> None:
|
||||||
if pilot is not None:
|
self.roster.set_pilot(index, pilot)
|
||||||
self.squadron.claim_pilot(pilot)
|
|
||||||
if (current_pilot := self.pilots[index]) is not None:
|
|
||||||
self.squadron.return_pilot(current_pilot)
|
|
||||||
self.pilots[index] = pilot
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def missing_pilots(self) -> int:
|
def missing_pilots(self) -> int:
|
||||||
return len([p for p in self.pilots if p is None])
|
return self.roster.missing_pilots
|
||||||
|
|
||||||
def clear_roster(self) -> None:
|
def clear_roster(self) -> None:
|
||||||
self.squadron.return_pilots([p for p in self.pilots if p is not None])
|
self.roster.clear()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
name = db.unit_type_name(self.unit_type)
|
name = db.unit_type_name(self.unit_type)
|
||||||
|
|||||||
@ -115,7 +115,7 @@ class AircraftInventoryData:
|
|||||||
flight_type = flight.flight_type.value
|
flight_type = flight.flight_type.value
|
||||||
target = flight.package.target.name
|
target = flight.package.target.name
|
||||||
for idx in range(0, num_units):
|
for idx in range(0, num_units):
|
||||||
pilot = flight.pilots[idx]
|
pilot = flight.roster.pilots[idx]
|
||||||
if pilot is None:
|
if pilot is None:
|
||||||
pilot_name = "Unassigned"
|
pilot_name = "Unassigned"
|
||||||
player = ""
|
player = ""
|
||||||
|
|||||||
@ -10,6 +10,7 @@ from PySide2.QtWidgets import (
|
|||||||
QPushButton,
|
QPushButton,
|
||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
|
QHBoxLayout,
|
||||||
)
|
)
|
||||||
from dcs.unittype import FlyingType
|
from dcs.unittype import FlyingType
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ from game import Game
|
|||||||
from game.squadrons import Squadron
|
from game.squadrons import Squadron
|
||||||
from game.theater import ControlPoint, OffMapSpawn
|
from game.theater import ControlPoint, OffMapSpawn
|
||||||
from gen.ato import Package
|
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.uiconstants import EVENT_ICONS
|
||||||
from qt_ui.widgets.QFlightSizeSpinner import QFlightSizeSpinner
|
from qt_ui.widgets.QFlightSizeSpinner import QFlightSizeSpinner
|
||||||
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
|
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.QFlightTypeComboBox import QFlightTypeComboBox
|
||||||
from qt_ui.widgets.combos.QOriginAirfieldSelector import QOriginAirfieldSelector
|
from qt_ui.widgets.combos.QOriginAirfieldSelector import QOriginAirfieldSelector
|
||||||
from qt_ui.windows.mission.flight.SquadronSelector import SquadronSelector
|
from qt_ui.windows.mission.flight.SquadronSelector import SquadronSelector
|
||||||
|
from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import FlightRosterEditor
|
||||||
|
|
||||||
|
|
||||||
class QFlightCreator(QDialog):
|
class QFlightCreator(QDialog):
|
||||||
@ -46,7 +48,7 @@ class QFlightCreator(QDialog):
|
|||||||
|
|
||||||
self.task_selector = QFlightTypeComboBox(self.game.theater, package.target)
|
self.task_selector = QFlightTypeComboBox(self.game.theater, package.target)
|
||||||
self.task_selector.setCurrentIndex(0)
|
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))
|
layout.addLayout(QLabeledWidget("Task:", self.task_selector))
|
||||||
|
|
||||||
self.aircraft_selector = QAircraftTypeSelector(
|
self.aircraft_selector = QAircraftTypeSelector(
|
||||||
@ -93,13 +95,20 @@ class QFlightCreator(QDialog):
|
|||||||
self.update_max_size(self.departure.available)
|
self.update_max_size(self.departure.available)
|
||||||
layout.addLayout(QLabeledWidget("Size:", self.flight_size_spinner))
|
layout.addLayout(QLabeledWidget("Size:", self.flight_size_spinner))
|
||||||
|
|
||||||
self.client_slots_spinner = QFlightSizeSpinner(
|
squadron = self.squadron_selector.currentData()
|
||||||
min_size=0, max_size=self.flight_size_spinner.value(), default_size=0
|
if squadron is None:
|
||||||
|
roster = None
|
||||||
|
else:
|
||||||
|
roster = FlightRoster(
|
||||||
|
squadron, initial_size=self.flight_size_spinner.value()
|
||||||
)
|
)
|
||||||
self.flight_size_spinner.valueChanged.connect(
|
self.roster_editor = FlightRosterEditor(roster)
|
||||||
lambda v: self.client_slots_spinner.setMaximum(v)
|
self.flight_size_spinner.valueChanged.connect(self.resize_roster)
|
||||||
)
|
self.squadron_selector.currentIndexChanged.connect(self.on_squadron_changed)
|
||||||
layout.addLayout(QLabeledWidget("Client Slots:", self.client_slots_spinner))
|
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
|
# 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
|
# 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):
|
def set_custom_name_text(self, text: str):
|
||||||
self.custom_name_text = text
|
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]:
|
def verify_form(self) -> Optional[str]:
|
||||||
aircraft: Optional[Type[FlyingType]] = self.aircraft_selector.currentData()
|
aircraft: Optional[Type[FlyingType]] = self.aircraft_selector.currentData()
|
||||||
squadron: Optional[Squadron] = self.squadron_selector.currentData()
|
squadron: Optional[Squadron] = self.squadron_selector.currentData()
|
||||||
@ -181,7 +194,7 @@ class QFlightCreator(QDialog):
|
|||||||
origin = self.departure.currentData()
|
origin = self.departure.currentData()
|
||||||
arrival = self.arrival.currentData()
|
arrival = self.arrival.currentData()
|
||||||
divert = self.divert.currentData()
|
divert = self.divert.currentData()
|
||||||
size = self.flight_size_spinner.value()
|
roster = self.roster_editor.roster
|
||||||
|
|
||||||
if arrival is None:
|
if arrival is None:
|
||||||
arrival = origin
|
arrival = origin
|
||||||
@ -190,22 +203,17 @@ class QFlightCreator(QDialog):
|
|||||||
self.package,
|
self.package,
|
||||||
self.country,
|
self.country,
|
||||||
squadron,
|
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,
|
task,
|
||||||
self.start_type.currentText(),
|
self.start_type.currentText(),
|
||||||
origin,
|
origin,
|
||||||
arrival,
|
arrival,
|
||||||
divert,
|
divert,
|
||||||
custom_name=self.custom_name_text,
|
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
|
# noinspection PyUnresolvedReferences
|
||||||
self.created.emit(flight)
|
self.created.emit(flight)
|
||||||
@ -234,13 +242,21 @@ class QFlightCreator(QDialog):
|
|||||||
self.start_type.setCurrentText(self.restore_start_type)
|
self.start_type.setCurrentText(self.restore_start_type)
|
||||||
self.restore_start_type = None
|
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.aircraft_selector.update_items(
|
||||||
self.task_selector.currentData(),
|
task, self.game.aircraft_inventory.available_types_for_player
|
||||||
self.game.aircraft_inventory.available_types_for_player,
|
|
||||||
)
|
)
|
||||||
self.squadron_selector.update_items(
|
self.squadron_selector.update_items(task, self.aircraft_selector.currentData())
|
||||||
self.task_selector.currentData(), 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:
|
def update_max_size(self, available: int) -> None:
|
||||||
|
|||||||
@ -28,7 +28,11 @@ class SquadronSelector(QComboBox):
|
|||||||
self, task: Optional[FlightType], aircraft: Optional[Type[FlyingType]]
|
self, task: Optional[FlightType], aircraft: Optional[Type[FlyingType]]
|
||||||
) -> None:
|
) -> None:
|
||||||
current_squadron = self.currentData()
|
current_squadron = self.currentData()
|
||||||
|
self.blockSignals(True)
|
||||||
|
try:
|
||||||
self.clear()
|
self.clear()
|
||||||
|
finally:
|
||||||
|
self.blockSignals(False)
|
||||||
if task is None:
|
if task is None:
|
||||||
self.addItem("No task selected", None)
|
self.addItem("No task selected", None)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -15,16 +15,16 @@ from PySide2.QtWidgets import (
|
|||||||
|
|
||||||
from game import Game
|
from game import Game
|
||||||
from game.squadrons import Pilot
|
from game.squadrons import Pilot
|
||||||
from gen.flights.flight import Flight
|
from gen.flights.flight import Flight, FlightRoster
|
||||||
from qt_ui.models import PackageModel
|
from qt_ui.models import PackageModel
|
||||||
|
|
||||||
|
|
||||||
class PilotSelector(QComboBox):
|
class PilotSelector(QComboBox):
|
||||||
available_pilots_changed = Signal()
|
available_pilots_changed = Signal()
|
||||||
|
|
||||||
def __init__(self, flight: Flight, idx: int) -> None:
|
def __init__(self, roster: Optional[FlightRoster], idx: int) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.flight = flight
|
self.roster = roster
|
||||||
self.pilot_index = idx
|
self.pilot_index = idx
|
||||||
self.rebuild()
|
self.rebuild()
|
||||||
|
|
||||||
@ -34,15 +34,15 @@ class PilotSelector(QComboBox):
|
|||||||
|
|
||||||
def _do_rebuild(self) -> None:
|
def _do_rebuild(self) -> None:
|
||||||
self.clear()
|
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.addItem("No aircraft", None)
|
||||||
self.setDisabled(True)
|
self.setDisabled(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.setEnabled(True)
|
self.setEnabled(True)
|
||||||
self.addItem("Unassigned", None)
|
self.addItem("Unassigned", None)
|
||||||
choices = list(self.flight.squadron.available_pilots)
|
choices = list(self.roster.squadron.available_pilots)
|
||||||
current_pilot = self.flight.pilots[self.pilot_index]
|
current_pilot = self.roster.pilots[self.pilot_index]
|
||||||
if current_pilot is not None:
|
if current_pilot is not None:
|
||||||
choices.append(current_pilot)
|
choices.append(current_pilot)
|
||||||
# Put players first, otherwise alphabetically.
|
# 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.
|
# The roster resize is handled separately, so we have no pilots to remove.
|
||||||
return
|
return
|
||||||
pilot = self.itemData(index)
|
pilot = self.itemData(index)
|
||||||
if pilot == self.flight.pilots[self.pilot_index]:
|
if pilot == self.roster.pilots[self.pilot_index]:
|
||||||
return
|
return
|
||||||
self.flight.set_pilot(self.pilot_index, pilot)
|
self.roster.set_pilot(self.pilot_index, pilot)
|
||||||
self.available_pilots_changed.emit()
|
self.available_pilots_changed.emit()
|
||||||
|
|
||||||
|
def replace(self, new_roster: Optional[FlightRoster]) -> None:
|
||||||
|
self.roster = new_roster
|
||||||
|
self.rebuild()
|
||||||
|
|
||||||
|
|
||||||
class PilotControls(QHBoxLayout):
|
class PilotControls(QHBoxLayout):
|
||||||
def __init__(self, flight: Flight, idx: int) -> None:
|
def __init__(self, roster: Optional[FlightRoster], idx: int) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.flight = flight
|
self.roster = roster
|
||||||
self.pilot_index = idx
|
self.pilot_index = idx
|
||||||
|
|
||||||
self.selector = PilotSelector(flight, idx)
|
self.selector = PilotSelector(roster, idx)
|
||||||
self.selector.currentIndexChanged.connect(self.on_pilot_changed)
|
self.selector.currentIndexChanged.connect(self.on_pilot_changed)
|
||||||
self.addWidget(self.selector)
|
self.addWidget(self.selector)
|
||||||
|
|
||||||
@ -95,9 +99,9 @@ class PilotControls(QHBoxLayout):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def pilot(self) -> Optional[Pilot]:
|
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 None
|
||||||
return self.flight.pilots[self.pilot_index]
|
return self.roster.pilots[self.pilot_index]
|
||||||
|
|
||||||
def on_player_toggled(self, checked: bool) -> None:
|
def on_player_toggled(self, checked: bool) -> None:
|
||||||
pilot = self.pilot
|
pilot = self.pilot
|
||||||
@ -130,12 +134,21 @@ class PilotControls(QHBoxLayout):
|
|||||||
finally:
|
finally:
|
||||||
self.player_checkbox.blockSignals(False)
|
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):
|
class FlightRosterEditor(QVBoxLayout):
|
||||||
MAX_PILOTS = 4
|
MAX_PILOTS = 4
|
||||||
|
|
||||||
def __init__(self, flight: Flight) -> None:
|
def __init__(self, roster: Optional[FlightRoster]) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.roster = roster
|
||||||
|
|
||||||
self.pilot_controls = []
|
self.pilot_controls = []
|
||||||
for pilot_idx in range(self.MAX_PILOTS):
|
for pilot_idx in range(self.MAX_PILOTS):
|
||||||
@ -146,7 +159,7 @@ class FlightRosterEditor(QVBoxLayout):
|
|||||||
|
|
||||||
return callback
|
return callback
|
||||||
|
|
||||||
controls = PilotControls(flight, pilot_idx)
|
controls = PilotControls(roster, pilot_idx)
|
||||||
controls.selector.available_pilots_changed.connect(
|
controls.selector.available_pilots_changed.connect(
|
||||||
make_reset_callback(pilot_idx)
|
make_reset_callback(pilot_idx)
|
||||||
)
|
)
|
||||||
@ -167,6 +180,13 @@ class FlightRosterEditor(QVBoxLayout):
|
|||||||
for controls in self.pilot_controls[new_size:]:
|
for controls in self.pilot_controls[new_size:]:
|
||||||
controls.disable_and_clear()
|
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):
|
class QFlightSlotEditor(QGroupBox):
|
||||||
def __init__(self, package_model: PackageModel, flight: Flight, game: Game):
|
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(str(self.flight.squadron)), 1, 1)
|
||||||
|
|
||||||
layout.addWidget(QLabel("Assigned pilots:"), 2, 0)
|
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)
|
layout.addLayout(self.roster_editor, 2, 1)
|
||||||
|
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user