mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Allow per pilot loadouts and properties.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/3092.
This commit is contained in:
13
qt_ui/blocksignals.py
Normal file
13
qt_ui/blocksignals.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
|
||||
from PySide6.QtWidgets import QWidget
|
||||
|
||||
|
||||
@contextmanager
|
||||
def block_signals(widget: QWidget) -> Iterator[None]:
|
||||
blocked = widget.blockSignals(True)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
widget.blockSignals(blocked)
|
||||
@@ -121,7 +121,7 @@ class AircraftInventoryData:
|
||||
flight_type = flight.flight_type.value
|
||||
target = flight.package.target.name
|
||||
for idx in range(0, num_units):
|
||||
pilot = flight.roster.pilots[idx]
|
||||
pilot = flight.roster.pilot_at(idx)
|
||||
if pilot is None:
|
||||
pilot_name = "Unassigned"
|
||||
player = ""
|
||||
|
||||
@@ -89,9 +89,10 @@ class QFlightCreator(QDialog):
|
||||
roster = None
|
||||
else:
|
||||
roster = FlightRoster(
|
||||
squadron, initial_size=self.flight_size_spinner.value()
|
||||
squadron,
|
||||
initial_size=self.flight_size_spinner.value(),
|
||||
)
|
||||
self.roster_editor = FlightRosterEditor(roster)
|
||||
self.roster_editor = FlightRosterEditor(squadron, roster)
|
||||
self.flight_size_spinner.valueChanged.connect(self.roster_editor.resize)
|
||||
self.squadron_selector.currentIndexChanged.connect(self.on_squadron_changed)
|
||||
roster_layout = QHBoxLayout()
|
||||
@@ -232,7 +233,7 @@ class QFlightCreator(QDialog):
|
||||
self.roster_editor.replace(None)
|
||||
if squadron is not None:
|
||||
self.roster_editor.replace(
|
||||
FlightRoster(squadron, self.flight_size_spinner.value())
|
||||
squadron, FlightRoster(squadron, self.flight_size_spinner.value())
|
||||
)
|
||||
self.on_departure_changed(squadron.location)
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ class QFlightPlanner(QTabWidget):
|
||||
self.general_settings_tab = QGeneralFlightSettingsTab(
|
||||
gm, package_model, flight, self.waypoint_tab.flight_waypoint_list
|
||||
)
|
||||
self.payload_tab = QFlightPayloadTab(flight, gm.game)
|
||||
self.general_settings_tab.flight_size_changed.connect(
|
||||
self.payload_tab.resize_for_flight
|
||||
)
|
||||
self.waypoint_tab = QFlightWaypointTab(gm.game, package_model.package, flight)
|
||||
self.waypoint_tab.loadout_changed.connect(self.payload_tab.reload_from_flight)
|
||||
self.addTab(self.general_settings_tab, "General Flight settings")
|
||||
self.addTab(self.payload_tab, "Payload")
|
||||
|
||||
@@ -9,26 +9,41 @@ from PySide2.QtWidgets import (
|
||||
QWidget,
|
||||
QSpinBox,
|
||||
QSlider,
|
||||
QCheckBox,
|
||||
)
|
||||
|
||||
from game import Game
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightmember import FlightMember
|
||||
from game.ato.loadouts import Loadout
|
||||
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
|
||||
from .QLoadoutEditor import QLoadoutEditor
|
||||
from .propertyeditor import PropertyEditor
|
||||
|
||||
|
||||
class DcsLoadoutSelector(QComboBox):
|
||||
def __init__(self, flight: Flight) -> None:
|
||||
def __init__(self, flight: Flight, member: FlightMember) -> None:
|
||||
super().__init__()
|
||||
for loadout in Loadout.iter_for(flight):
|
||||
self.addItem(loadout.name, loadout)
|
||||
self.model().sort(0)
|
||||
self.setDisabled(flight.loadout.is_custom)
|
||||
if flight.loadout.is_custom:
|
||||
self.setDisabled(member.loadout.is_custom)
|
||||
if member.loadout.is_custom:
|
||||
self.setCurrentText(Loadout.default_for(flight).name)
|
||||
else:
|
||||
self.setCurrentText(flight.loadout.name)
|
||||
self.setCurrentText(member.loadout.name)
|
||||
|
||||
|
||||
class FlightMemberSelector(QSpinBox):
|
||||
def __init__(self, flight: Flight, parent: QWidget | None = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.flight = flight
|
||||
self.setMinimum(0)
|
||||
self.setMaximum(flight.count - 1)
|
||||
|
||||
@property
|
||||
def selected_member(self) -> FlightMember:
|
||||
return self.flight.roster.members[self.value()]
|
||||
|
||||
|
||||
class DcsFuelSelector(QHBoxLayout):
|
||||
@@ -99,12 +114,41 @@ class QFlightPayloadTab(QFrame):
|
||||
def __init__(self, flight: Flight, game: Game):
|
||||
super(QFlightPayloadTab, self).__init__()
|
||||
self.flight = flight
|
||||
self.payload_editor = QLoadoutEditor(flight, game)
|
||||
self.payload_editor = QLoadoutEditor(
|
||||
flight, self.flight.roster.members[0], game
|
||||
)
|
||||
self.payload_editor.toggled.connect(self.on_custom_toggled)
|
||||
self.payload_editor.saved.connect(self.on_saved_payload)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.member_selector = FlightMemberSelector(self.flight, self)
|
||||
self.member_selector.valueChanged.connect(self.rebind_to_selected_member)
|
||||
layout.addLayout(QLabeledWidget("Flight member:", self.member_selector))
|
||||
self.same_loadout_for_all_checkbox = QCheckBox(
|
||||
"Use same loadout for all flight members"
|
||||
)
|
||||
self.same_loadout_for_all_checkbox.setChecked(
|
||||
self.flight.use_same_loadout_for_all_members
|
||||
)
|
||||
self.same_loadout_for_all_checkbox.toggled.connect(self.on_same_loadout_toggled)
|
||||
layout.addWidget(self.same_loadout_for_all_checkbox)
|
||||
layout.addWidget(
|
||||
QLabel(
|
||||
"<strong>Warning: AI flights should use the same loadout for all members.</strong>"
|
||||
)
|
||||
)
|
||||
|
||||
scroll_content = QWidget()
|
||||
scrolling_layout = QVBoxLayout()
|
||||
scroll_content.setLayout(scrolling_layout)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setWidget(scroll_content)
|
||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
layout.addWidget(scroll)
|
||||
|
||||
# Docs Link
|
||||
docsText = QLabel(
|
||||
'<a href="https://github.com/dcs-retribution/dcs-retribution/wiki/Custom-Loadouts"><span style="color:#FFFFFF;">How to create your own default loadout</span></a>'
|
||||
@@ -122,6 +166,13 @@ class QFlightPayloadTab(QFrame):
|
||||
layout.addLayout(self.fuel_selector)
|
||||
|
||||
self.loadout_selector = DcsLoadoutSelector(flight)
|
||||
self.property_editor = PropertyEditor(
|
||||
self.flight, self.member_selector.selected_member
|
||||
)
|
||||
scrolling_layout.addLayout(self.property_editor)
|
||||
self.loadout_selector = DcsLoadoutSelector(
|
||||
flight, self.member_selector.selected_member
|
||||
)
|
||||
self.loadout_selector.currentIndexChanged.connect(self.on_new_loadout)
|
||||
layout.addWidget(self.loadout_selector)
|
||||
layout.addWidget(self.payload_editor, stretch=1)
|
||||
@@ -129,8 +180,27 @@ class QFlightPayloadTab(QFrame):
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def resize_for_flight(self) -> None:
|
||||
self.member_selector.setMaximum(self.flight.count - 1)
|
||||
|
||||
def reload_from_flight(self) -> None:
|
||||
self.loadout_selector.setCurrentText(self.flight.loadout.name)
|
||||
self.loadout_selector.setCurrentText(
|
||||
self.member_selector.selected_member.loadout.name
|
||||
)
|
||||
|
||||
def rebind_to_selected_member(self) -> None:
|
||||
member = self.member_selector.selected_member
|
||||
self.property_editor.set_flight_member(member)
|
||||
self.loadout_selector.setCurrentText(member.loadout.name)
|
||||
self.loadout_selector.setDisabled(member.loadout.is_custom)
|
||||
self.payload_editor.set_flight_member(member)
|
||||
if self.member_selector.value() != 0:
|
||||
self.loadout_selector.setDisabled(
|
||||
self.flight.use_same_loadout_for_all_members
|
||||
)
|
||||
self.payload_editor.setDisabled(
|
||||
self.flight.use_same_loadout_for_all_members
|
||||
)
|
||||
|
||||
def loadout_at(self, index: int) -> Loadout:
|
||||
loadout = self.loadout_selector.itemData(index)
|
||||
@@ -145,18 +215,30 @@ class QFlightPayloadTab(QFrame):
|
||||
return loadout
|
||||
|
||||
def on_new_loadout(self, index: int) -> None:
|
||||
self.flight.loadout = self.loadout_at(index)
|
||||
self.member_selector.selected_member.loadout = self.loadout_at(index)
|
||||
self.payload_editor.reset_pylons()
|
||||
|
||||
def on_custom_toggled(self, use_custom: bool) -> None:
|
||||
self.loadout_selector.setDisabled(use_custom)
|
||||
member = self.member_selector.selected_member
|
||||
member.use_custom_loadout = use_custom
|
||||
if use_custom:
|
||||
self.flight.loadout = self.flight.loadout.derive_custom("Custom")
|
||||
member.loadout = member.loadout.derive_custom("Custom")
|
||||
else:
|
||||
self.flight.loadout = self.current_loadout()
|
||||
member.loadout = self.current_loadout()
|
||||
self.payload_editor.reset_pylons()
|
||||
|
||||
def on_saved_payload(self, payload_name: str) -> None:
|
||||
loadout = self.flight.loadout
|
||||
self.loadout_selector.addItem(payload_name, loadout)
|
||||
self.loadout_selector.setCurrentIndex(self.loadout_selector.count() - 1)
|
||||
|
||||
def on_same_loadout_toggled(self, checked: bool) -> None:
|
||||
self.flight.use_same_loadout_for_all_members = checked
|
||||
if self.member_selector.value():
|
||||
self.loadout_selector.setDisabled(checked)
|
||||
self.payload_editor.setDisabled(checked)
|
||||
if checked:
|
||||
self.flight.roster.use_same_loadout_for_all_members()
|
||||
if self.member_selector.value():
|
||||
self.rebind_to_selected_member()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from collections.abc import Iterator
|
||||
from dataclasses import dataclass
|
||||
from shutil import copyfile
|
||||
from typing import Dict, Union
|
||||
@@ -18,20 +19,23 @@ from dcs import lua
|
||||
|
||||
from game import Game
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightmember import FlightMember
|
||||
from game.data.weapons import Pylon
|
||||
from game.persistency import payloads_dir
|
||||
from qt_ui.blocksignals import block_signals
|
||||
from qt_ui.windows.mission.flight.payload.QPylonEditor import QPylonEditor
|
||||
|
||||
|
||||
class QLoadoutEditor(QGroupBox):
|
||||
saved = Signal(str)
|
||||
|
||||
def __init__(self, flight: Flight, game: Game) -> None:
|
||||
def __init__(self, flight: Flight, flight_member: FlightMember, game: Game) -> None:
|
||||
super().__init__("Use custom loadout")
|
||||
self.flight = flight
|
||||
self.flight_member = flight_member
|
||||
self.game = game
|
||||
self.setCheckable(True)
|
||||
self.setChecked(flight.loadout.is_custom)
|
||||
self.setChecked(flight_member.loadout.is_custom)
|
||||
|
||||
vbox = QVBoxLayout(self)
|
||||
layout = QGridLayout(self)
|
||||
@@ -40,7 +44,7 @@ class QLoadoutEditor(QGroupBox):
|
||||
label = QLabel(f"<b>{pylon.number}</b>")
|
||||
label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
|
||||
layout.addWidget(label, i, 0)
|
||||
layout.addWidget(QPylonEditor(game, flight, pylon), i, 1)
|
||||
layout.addWidget(QPylonEditor(game, flight, flight_member, pylon), i, 1)
|
||||
|
||||
vbox.addLayout(layout)
|
||||
|
||||
@@ -60,8 +64,18 @@ class QLoadoutEditor(QGroupBox):
|
||||
|
||||
self.setLayout(vbox)
|
||||
|
||||
for i in self.findChildren(QPylonEditor):
|
||||
i.set_from(self.flight.loadout)
|
||||
for pylon_editor in self.iter_pylon_editors():
|
||||
pylon_editor.set_from(self.flight_member.loadout)
|
||||
|
||||
def iter_pylon_editors(self) -> Iterator[QPylonEditor]:
|
||||
yield from self.findChildren(QPylonEditor)
|
||||
|
||||
def set_flight_member(self, flight_member: FlightMember) -> None:
|
||||
self.flight_member = flight_member
|
||||
with block_signals(self):
|
||||
self.setChecked(self.flight_member.use_custom_loadout)
|
||||
for pylon_editor in self.iter_pylon_editors():
|
||||
pylon_editor.set_flight_member(flight_member)
|
||||
|
||||
def _backup_payloads(self) -> None:
|
||||
ac_id = self.flight.unit_type.dcs_unit_type.id
|
||||
@@ -146,10 +160,10 @@ class QLoadoutEditor(QGroupBox):
|
||||
return payload_name_input
|
||||
|
||||
def reset_pylons(self) -> None:
|
||||
self.flight.use_custom_loadout = self.isChecked()
|
||||
self.flight_member.use_custom_loadout = self.isChecked()
|
||||
if not self.isChecked():
|
||||
for i in self.findChildren(QPylonEditor):
|
||||
i.set_from(self.flight.loadout)
|
||||
for pylon_editor in self.iter_pylon_editors():
|
||||
pylon_editor.set_from(self.flight_member.loadout)
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -6,19 +6,23 @@ from PySide2.QtWidgets import QComboBox
|
||||
|
||||
from game import Game
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightmember import FlightMember
|
||||
from game.ato.loadouts import Loadout
|
||||
from game.data.weapons import Pylon, Weapon
|
||||
|
||||
|
||||
class QPylonEditor(QComboBox):
|
||||
def __init__(self, game: Game, flight: Flight, pylon: Pylon) -> None:
|
||||
def __init__(
|
||||
self, game: Game, flight: Flight, flight_member: FlightMember, pylon: Pylon
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.flight = flight
|
||||
self.flight_member = flight_member
|
||||
self.pylon = pylon
|
||||
self.game = game
|
||||
self.has_added_clean_item = False
|
||||
|
||||
current = self.flight.loadout.pylons.get(self.pylon.number)
|
||||
current = self.flight_member.loadout.pylons.get(self.pylon.number)
|
||||
|
||||
self.addItem("None", None)
|
||||
if self.game.settings.restrict_weapons_by_date:
|
||||
@@ -35,7 +39,7 @@ class QPylonEditor(QComboBox):
|
||||
|
||||
def on_pylon_change(self):
|
||||
selected: Optional[Weapon] = self.currentData()
|
||||
self.flight.loadout.pylons[self.pylon.number] = selected
|
||||
self.flight_member.loadout.pylons[self.pylon.number] = selected
|
||||
|
||||
if selected is None:
|
||||
logging.debug(f"Pylon {self.pylon.number} emptied")
|
||||
@@ -70,5 +74,9 @@ class QPylonEditor(QComboBox):
|
||||
return "None"
|
||||
return weapon.name
|
||||
|
||||
def set_flight_member(self, flight_member: FlightMember) -> None:
|
||||
self.flight_member = flight_member
|
||||
self.set_from(self.flight_member.loadout)
|
||||
|
||||
def set_from(self, loadout: Loadout) -> None:
|
||||
self.setCurrentText(self.matching_weapon_name(loadout))
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
from PySide6.QtWidgets import QCheckBox
|
||||
from dcs.unitpropertydescription import UnitPropertyDescription
|
||||
|
||||
from game.ato import Flight
|
||||
from game.ato.flightmember import FlightMember
|
||||
from .missingpropertydataerror import MissingPropertyDataError
|
||||
|
||||
|
||||
class PropertyCheckBox(QCheckBox):
|
||||
def __init__(self, flight: Flight, prop: UnitPropertyDescription) -> None:
|
||||
def __init__(
|
||||
self, flight_member: FlightMember, prop: UnitPropertyDescription
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.flight = flight
|
||||
self.flight_member = flight_member
|
||||
self.prop = prop
|
||||
|
||||
if prop.default is None:
|
||||
raise MissingPropertyDataError("default cannot be None")
|
||||
|
||||
self.setChecked(self.flight.props.get(self.prop.identifier, self.prop.default))
|
||||
self.setChecked(
|
||||
self.flight_member.properties.get(self.prop.identifier, self.prop.default)
|
||||
)
|
||||
self.toggled.connect(self.on_toggle)
|
||||
|
||||
def on_toggle(self, checked: bool) -> None:
|
||||
self.flight.props[self.prop.identifier] = checked
|
||||
self.flight_member.properties[self.prop.identifier] = checked
|
||||
|
||||
def set_flight_member(self, flight_member: FlightMember) -> None:
|
||||
self.flight_member = flight_member
|
||||
self.setChecked(
|
||||
self.flight_member.properties.get(self.prop.identifier, self.prop.default)
|
||||
)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from PySide6.QtWidgets import QComboBox
|
||||
from dcs.unitpropertydescription import UnitPropertyDescription
|
||||
|
||||
from game.ato import Flight
|
||||
from game.ato.flightmember import FlightMember
|
||||
from .missingpropertydataerror import MissingPropertyDataError
|
||||
|
||||
|
||||
class PropertyComboBox(QComboBox):
|
||||
def __init__(self, flight: Flight, prop: UnitPropertyDescription) -> None:
|
||||
def __init__(
|
||||
self, flight_member: FlightMember, prop: UnitPropertyDescription
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.flight = flight
|
||||
self.flight_member = flight_member
|
||||
self.prop = prop
|
||||
|
||||
if prop.values is None:
|
||||
@@ -16,7 +18,9 @@ class PropertyComboBox(QComboBox):
|
||||
if prop.default is None:
|
||||
raise MissingPropertyDataError("default cannot be None")
|
||||
|
||||
current_value = self.flight.props.get(self.prop.identifier, self.prop.default)
|
||||
current_value = self.flight_member.properties.get(
|
||||
self.prop.identifier, self.prop.default
|
||||
)
|
||||
|
||||
for ident, text in self.prop.values.items():
|
||||
self.addItem(text, ident)
|
||||
@@ -26,4 +30,12 @@ class PropertyComboBox(QComboBox):
|
||||
self.currentIndexChanged.connect(self.on_selection_changed)
|
||||
|
||||
def on_selection_changed(self, _index: int) -> None:
|
||||
self.flight.props[self.prop.identifier] = self.currentData()
|
||||
self.flight_member.properties[self.prop.identifier] = self.currentData()
|
||||
|
||||
def set_flight_member(self, flight_member: FlightMember) -> None:
|
||||
self.flight_member = flight_member
|
||||
self.setCurrentText(
|
||||
self.flight_member.properties.get(
|
||||
self.prop.identifier, self.prop.values[self.prop.default]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import logging
|
||||
from typing import Callable
|
||||
|
||||
from PySide2.QtWidgets import QGridLayout, QLabel
|
||||
from dcs.unitpropertydescription import UnitPropertyDescription
|
||||
|
||||
from game.ato import Flight
|
||||
from game.ato.flightmember import FlightMember
|
||||
from .missingpropertydataerror import MissingPropertyDataError
|
||||
from .propertycheckbox import PropertyCheckBox
|
||||
from .propertycombobox import PropertyComboBox
|
||||
@@ -16,9 +18,10 @@ class UnhandledControlTypeError(RuntimeError):
|
||||
|
||||
|
||||
class PropertyEditor(QGridLayout):
|
||||
def __init__(self, flight: Flight) -> None:
|
||||
def __init__(self, flight: Flight, flight_member: FlightMember) -> None:
|
||||
super().__init__()
|
||||
self.flight = flight
|
||||
self.flight_member = flight_member
|
||||
self.flight_member_update_listeners: list[Callable[[FlightMember], None]] = []
|
||||
|
||||
for row, prop in enumerate(flight.unit_type.iter_props()):
|
||||
if prop.label is None:
|
||||
@@ -56,12 +59,23 @@ class PropertyEditor(QGridLayout):
|
||||
# "checkbox", "comboList", "groupbox", "label", "slider", "spinbox"
|
||||
match prop.control:
|
||||
case "checkbox":
|
||||
return PropertyCheckBox(self.flight, prop)
|
||||
widget = PropertyCheckBox(self.flight_member, prop)
|
||||
self.flight_member_update_listeners.append(widget.set_flight_member)
|
||||
return widget
|
||||
case "comboList":
|
||||
return PropertyComboBox(self.flight, prop)
|
||||
widget = PropertyComboBox(self.flight_member, prop)
|
||||
self.flight_member_update_listeners.append(widget.set_flight_member)
|
||||
return widget
|
||||
case "groupbox" | "label":
|
||||
return None
|
||||
case "slider" | "spinbox":
|
||||
return PropertySpinBox(self.flight, prop)
|
||||
widget = PropertySpinBox(self.flight_member, prop)
|
||||
self.flight_member_update_listeners.append(widget.set_flight_member)
|
||||
return widget
|
||||
case _:
|
||||
raise UnhandledControlTypeError(prop.control)
|
||||
|
||||
def set_flight_member(self, flight_member: FlightMember) -> None:
|
||||
self.flight_member = flight_member
|
||||
for listener in self.flight_member_update_listeners:
|
||||
listener(self.flight_member)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from PySide6.QtWidgets import QSpinBox
|
||||
from dcs.unitpropertydescription import UnitPropertyDescription
|
||||
|
||||
from game.ato import Flight
|
||||
from game.ato.flightmember import FlightMember
|
||||
from .missingpropertydataerror import MissingPropertyDataError
|
||||
|
||||
|
||||
class PropertySpinBox(QSpinBox):
|
||||
def __init__(self, flight: Flight, prop: UnitPropertyDescription) -> None:
|
||||
def __init__(
|
||||
self, flight_member: FlightMember, prop: UnitPropertyDescription
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.flight = flight
|
||||
self.flight_member = flight_member
|
||||
self.prop = prop
|
||||
|
||||
if prop.minimum is None:
|
||||
@@ -20,9 +22,17 @@ class PropertySpinBox(QSpinBox):
|
||||
|
||||
self.setMinimum(prop.minimum)
|
||||
self.setMaximum(prop.maximum)
|
||||
self.setValue(self.flight.props.get(self.prop.identifier, self.prop.default))
|
||||
self.setValue(
|
||||
self.flight_member.properties.get(self.prop.identifier, self.prop.default)
|
||||
)
|
||||
|
||||
self.valueChanged.connect(self.on_value_changed)
|
||||
|
||||
def on_value_changed(self, value: int) -> None:
|
||||
self.flight.props[self.prop.identifier] = value
|
||||
self.flight_member.properties[self.prop.identifier] = value
|
||||
|
||||
def set_flight_member(self, flight_member: FlightMember) -> None:
|
||||
self.flight_member = flight_member
|
||||
self.setValue(
|
||||
self.flight_member.properties.get(self.prop.identifier, self.prop.default)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import logging
|
||||
from typing import Optional, Callable
|
||||
from typing import Optional, Callable, Union
|
||||
|
||||
from PySide2.QtCore import Signal, QModelIndex
|
||||
from PySide2.QtWidgets import (
|
||||
@@ -16,6 +16,8 @@ from PySide2.QtWidgets import (
|
||||
from game import Game
|
||||
from game.ato.flight import Flight
|
||||
from game.ato.flightroster import FlightRoster
|
||||
from game.ato.iflightroster import IFlightRoster
|
||||
from game.squadrons import Squadron
|
||||
from game.squadrons.pilot import Pilot
|
||||
from qt_ui.models import PackageModel
|
||||
|
||||
@@ -23,8 +25,11 @@ from qt_ui.models import PackageModel
|
||||
class PilotSelector(QComboBox):
|
||||
available_pilots_changed = Signal()
|
||||
|
||||
def __init__(self, roster: Optional[FlightRoster], idx: int) -> None:
|
||||
def __init__(
|
||||
self, squadron: Union[Squadron, None], roster: Optional[IFlightRoster], idx: int
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.squadron = squadron
|
||||
self.roster = roster
|
||||
self.pilot_index = idx
|
||||
self.rebuild()
|
||||
@@ -40,10 +45,13 @@ class PilotSelector(QComboBox):
|
||||
self.setDisabled(True)
|
||||
return
|
||||
|
||||
if self.squadron is None:
|
||||
raise RuntimeError("squadron cannot be None if roster is set")
|
||||
|
||||
self.setEnabled(True)
|
||||
self.addItem("Unassigned", None)
|
||||
choices = list(self.roster.squadron.available_pilots)
|
||||
current_pilot = self.roster.pilots[self.pilot_index]
|
||||
choices = list(self.squadron.available_pilots)
|
||||
current_pilot = self.roster.pilot_at(self.pilot_index)
|
||||
if current_pilot is not None:
|
||||
choices.append(current_pilot)
|
||||
# Put players first, otherwise alphabetically.
|
||||
@@ -71,23 +79,26 @@ class PilotSelector(QComboBox):
|
||||
# The roster resize is handled separately, so we have no pilots to remove.
|
||||
return
|
||||
pilot = self.itemData(index)
|
||||
if pilot == self.roster.pilots[self.pilot_index]:
|
||||
if pilot == self.roster.pilot_at(self.pilot_index):
|
||||
return
|
||||
self.roster.set_pilot(self.pilot_index, pilot)
|
||||
self.available_pilots_changed.emit()
|
||||
|
||||
def replace(self, new_roster: Optional[FlightRoster]) -> None:
|
||||
def replace(self, squadron: Squadron, new_roster: Optional[FlightRoster]) -> None:
|
||||
self.squadron = squadron
|
||||
self.roster = new_roster
|
||||
self.rebuild()
|
||||
|
||||
|
||||
class PilotControls(QHBoxLayout):
|
||||
def __init__(self, roster: Optional[FlightRoster], idx: int) -> None:
|
||||
def __init__(
|
||||
self, squadron: Union[Squadron, None], roster: Optional[FlightRoster], idx: int
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.roster = roster
|
||||
self.pilot_index = idx
|
||||
|
||||
self.selector = PilotSelector(roster, idx)
|
||||
self.selector = PilotSelector(squadron, roster, idx)
|
||||
self.selector.currentIndexChanged.connect(self.on_pilot_changed)
|
||||
self.addWidget(self.selector)
|
||||
|
||||
@@ -95,8 +106,8 @@ class PilotControls(QHBoxLayout):
|
||||
self.player_checkbox.setToolTip("Checked if this pilot is a player.")
|
||||
self.on_pilot_changed(self.selector.currentIndex())
|
||||
enabled = False
|
||||
if self.roster is not None and self.roster.squadron is not None:
|
||||
enabled = self.roster.squadron.aircraft.flyable
|
||||
if self.roster is not None and squadron is not None:
|
||||
enabled = squadron.aircraft.flyable
|
||||
self.player_checkbox.setEnabled(enabled)
|
||||
self.addWidget(self.player_checkbox)
|
||||
|
||||
@@ -106,7 +117,7 @@ class PilotControls(QHBoxLayout):
|
||||
def pilot(self) -> Optional[Pilot]:
|
||||
if self.roster is None or self.pilot_index >= self.roster.max_size:
|
||||
return None
|
||||
return self.roster.pilots[self.pilot_index]
|
||||
return self.roster.pilot_at(self.pilot_index)
|
||||
|
||||
def on_player_toggled(self, checked: bool) -> None:
|
||||
pilot = self.pilot
|
||||
@@ -145,19 +156,19 @@ class PilotControls(QHBoxLayout):
|
||||
finally:
|
||||
self.player_checkbox.blockSignals(False)
|
||||
|
||||
def replace(self, new_roster: Optional[FlightRoster]) -> None:
|
||||
def replace(self, squadron: Squadron, 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)
|
||||
self.selector.replace(squadron, new_roster)
|
||||
|
||||
|
||||
class FlightRosterEditor(QVBoxLayout):
|
||||
MAX_PILOTS = 4
|
||||
|
||||
def __init__(self, roster: Optional[FlightRoster]) -> None:
|
||||
def __init__(self, squadron: Union[Squadron, None], roster: Union[IFlightRoster, None]) -> None:
|
||||
super().__init__()
|
||||
self.roster = roster
|
||||
|
||||
@@ -170,7 +181,7 @@ class FlightRosterEditor(QVBoxLayout):
|
||||
|
||||
return callback
|
||||
|
||||
controls = PilotControls(roster, pilot_idx)
|
||||
controls = PilotControls(squadron, roster, pilot_idx)
|
||||
controls.selector.available_pilots_changed.connect(
|
||||
make_reset_callback(pilot_idx)
|
||||
)
|
||||
@@ -193,15 +204,17 @@ class FlightRosterEditor(QVBoxLayout):
|
||||
for controls in self.pilot_controls[new_size:]:
|
||||
controls.disable_and_clear()
|
||||
|
||||
def replace(self, new_roster: Optional[FlightRoster]) -> None:
|
||||
def replace(self, squadron: Squadron, 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)
|
||||
controls.replace(squadron, new_roster)
|
||||
|
||||
|
||||
class QFlightSlotEditor(QGroupBox):
|
||||
flight_resized = Signal(int)
|
||||
|
||||
def __init__(self, package_model: PackageModel, flight: Flight, game: Game):
|
||||
super().__init__("Slots")
|
||||
self.package_model = package_model
|
||||
@@ -228,7 +241,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.roster)
|
||||
self.roster_editor = FlightRosterEditor(flight.squadron, flight.roster)
|
||||
layout.addLayout(self.roster_editor, 2, 1)
|
||||
|
||||
self.setLayout(layout)
|
||||
@@ -251,3 +264,4 @@ class QFlightSlotEditor(QGroupBox):
|
||||
self.flight.resize(old_count)
|
||||
return
|
||||
self.roster_editor.resize(new_count)
|
||||
self.flight_resized.emit(new_count)
|
||||
|
||||
@@ -19,7 +19,7 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import (
|
||||
|
||||
|
||||
class QGeneralFlightSettingsTab(QFrame):
|
||||
on_flight_settings_changed = Signal()
|
||||
flight_size_changed = Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -30,13 +30,16 @@ class QGeneralFlightSettingsTab(QFrame):
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.flight_slot_editor = QFlightSlotEditor(package_model, flight, game.game)
|
||||
self.flight_slot_editor.flight_resized.connect(self.flight_size_changed)
|
||||
|
||||
widgets = [
|
||||
QFlightTypeTaskInfo(flight),
|
||||
QCommsEditor(flight, game),
|
||||
FlightPlanPropertiesGroup(
|
||||
game.game, package_model, flight, flight_wpt_list
|
||||
),
|
||||
QFlightSlotEditor(package_model, flight, game.game),
|
||||
self.flight_slot_editor,
|
||||
QFlightStartType(package_model, flight),
|
||||
QFlightCustomName(flight),
|
||||
]
|
||||
@@ -45,6 +48,7 @@ class QGeneralFlightSettingsTab(QFrame):
|
||||
for w in widgets:
|
||||
layout.addWidget(w, row, 0)
|
||||
row += 1
|
||||
|
||||
vstretch = QVBoxLayout()
|
||||
vstretch.addStretch()
|
||||
layout.addLayout(vstretch, row, 0)
|
||||
|
||||
@@ -226,9 +226,11 @@ class QFlightWaypointTab(QFrame):
|
||||
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()
|
||||
for member in self.flight.iter_members():
|
||||
if not member.loadout.is_custom:
|
||||
member.loadout = Loadout.default_for(self.flight)
|
||||
self.loadout_changed.emit()
|
||||
self.flight_waypoint_list.update_list()
|
||||
self.on_change()
|
||||
|
||||
def on_change(self):
|
||||
|
||||
Reference in New Issue
Block a user