diff --git a/game/ato/flight.py b/game/ato/flight.py index 3333aafa..cbc4c500 100644 --- a/game/ato/flight.py +++ b/game/ato/flight.py @@ -111,11 +111,14 @@ class Flight(SidcDescribable, RadioFrequencyContainer, TacanContainer): is_f18 = self.squadron.aircraft.dcs_unit_type.id == FA_18C_hornet.id on_land = not self.squadron.location.is_fleet if on_land and is_f18 and self.coalition.game.settings.atflir_autoswap: - self.loadout.pylons[F18_TGP_PYLON] = Weapon.with_clsid( - str( - FA_18C_hornet.Pylon4.AN_AAQ_28_LITENING___Targeting_Pod_[1]["clsid"] + for fm in self.roster.members: + fm.loadout.pylons[F18_TGP_PYLON] = Weapon.with_clsid( + str( + FA_18C_hornet.Pylon4.AN_AAQ_28_LITENING___Targeting_Pod_[1][ + "clsid" + ] + ) ) - ) @property def flight_plan(self) -> FlightPlan[Any]: diff --git a/game/ato/flightmembers.py b/game/ato/flightmembers.py index 028106f9..d76c569c 100644 --- a/game/ato/flightmembers.py +++ b/game/ato/flightmembers.py @@ -10,7 +10,7 @@ from .loadouts import Loadout from ..data.weapons import Weapon if TYPE_CHECKING: - from game.squadrons import Pilot + from game.squadrons import Pilot, Squadron from .flight import Flight @@ -20,14 +20,16 @@ class FlightMembers(IFlightRoster): self.members: list[FlightMember] = [] self.resize(initial_size) + @property + def squadron(self) -> Squadron: + return self.flight.squadron + @staticmethod def from_roster(flight: Flight, roster: FlightRoster) -> FlightMembers: members = FlightMembers(flight) loadout = Loadout.default_for(flight) if flight.squadron.aircraft.name == "F-15I Ra'am": - loadout.pylons[16] = Weapon.with_clsid( - "{IDF_MODS_PROJECT_F-15I_Raam_Dome}" - ) + loadout.pylons[16] = Weapon.with_clsid("{IDF_MODS_PROJECT_F-15I_Raam_Dome}") members.members = [FlightMember(p, loadout) for p in roster.pilots] return members @@ -61,9 +63,7 @@ class FlightMembers(IFlightRoster): else: loadout = Loadout.default_for(self.flight) if self.flight.squadron.aircraft.name == "F-15I Ra'am": - loadout.pylons[16] = Weapon.with_clsid( - "{IDF_MODS_PROJECT_F-15I_Raam_Dome}" - ) + loadout.pylons[16] = Weapon.with_clsid("{IDF_MODS_PROJECT_F-15I_Raam_Dome}") for _ in range(new_size - self.max_size): member = FlightMember(self.flight.squadron.claim_available_pilot(), loadout) member.use_custom_loadout = loadout.is_custom diff --git a/game/ato/flightroster.py b/game/ato/flightroster.py index a9ef5016..859b891c 100644 --- a/game/ato/flightroster.py +++ b/game/ato/flightroster.py @@ -11,10 +11,14 @@ if TYPE_CHECKING: class FlightRoster(IFlightRoster): def __init__(self, squadron: Squadron, initial_size: int = 0) -> None: - self.squadron = squadron + self._squadron = squadron self.pilots: list[Optional[Pilot]] = [] self.resize(initial_size) + @property + def squadron(self) -> Squadron: + return self._squadron + def iter_pilots(self) -> Iterator[Pilot | None]: yield from self.pilots diff --git a/game/ato/iflightroster.py b/game/ato/iflightroster.py index 32231445..62ea8c6b 100644 --- a/game/ato/iflightroster.py +++ b/game/ato/iflightroster.py @@ -4,7 +4,7 @@ from abc import ABC, abstractmethod from typing import Optional, TYPE_CHECKING, Iterator if TYPE_CHECKING: - from game.squadrons import Pilot + from game.squadrons import Pilot, Squadron class IFlightRoster(ABC): @@ -16,6 +16,11 @@ class IFlightRoster(ABC): def pilot_at(self, idx: int) -> Pilot | None: ... + @property + @abstractmethod + def squadron(self) -> Squadron: + ... + @property @abstractmethod def max_size(self) -> int: diff --git a/game/missiongenerator/aircraft/aircraftgenerator.py b/game/missiongenerator/aircraft/aircraftgenerator.py index e2ca9704..553506ae 100644 --- a/game/missiongenerator/aircraft/aircraftgenerator.py +++ b/game/missiongenerator/aircraft/aircraftgenerator.py @@ -275,7 +275,7 @@ class AircraftGenerator: or flight.client_count and ( not self.need_ecm - or flight.loadout.has_weapon_of_type(WeaponType.JAMMER) + or flight.any_member_has_weapon_of_type(WeaponType.JAMMER) ) ): self.ewrj_package_dict[id(flight.package)].append(group) diff --git a/game/missiongenerator/aircraft/flightgroupspawner.py b/game/missiongenerator/aircraft/flightgroupspawner.py index f3b19e87..c7831a0e 100644 --- a/game/missiongenerator/aircraft/flightgroupspawner.py +++ b/game/missiongenerator/aircraft/flightgroupspawner.py @@ -149,7 +149,7 @@ class FlightGroupSpawner: raise RuntimeError( f"Cannot spawn fixed-wing aircraft at {cp} because of insufficient ground spawn slots." ) - pilot_count = len(self.flight.roster.pilots) + pilot_count = len(self.flight.roster.members) if ( not is_heli and self.flight.roster.player_count != pilot_count diff --git a/qt_ui/blocksignals.py b/qt_ui/blocksignals.py index 8019946a..0dff148d 100644 --- a/qt_ui/blocksignals.py +++ b/qt_ui/blocksignals.py @@ -1,7 +1,7 @@ from collections.abc import Iterator from contextlib import contextmanager -from PySide6.QtWidgets import QWidget +from PySide2.QtWidgets import QWidget @contextmanager diff --git a/qt_ui/windows/mission/flight/QFlightPlanner.py b/qt_ui/windows/mission/flight/QFlightPlanner.py index 19a868e5..2b878458 100644 --- a/qt_ui/windows/mission/flight/QFlightPlanner.py +++ b/qt_ui/windows/mission/flight/QFlightPlanner.py @@ -16,10 +16,14 @@ class QFlightPlanner(QTabWidget): self.payload_tab = QFlightPayloadTab(flight, gm.game) self.waypoint_tab = QFlightWaypointTab(gm.game, package_model.package, flight) - 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 = QGeneralFlightSettingsTab( + gm, + package_model, + flight, + self.waypoint_tab.flight_waypoint_list, + self.payload_tab, + ) self.general_settings_tab.flight_size_changed.connect( self.payload_tab.resize_for_flight ) diff --git a/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py b/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py index 1a84790e..17f83b0f 100644 --- a/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py +++ b/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py @@ -5,11 +5,11 @@ from PySide2.QtWidgets import ( QLabel, QHBoxLayout, QVBoxLayout, - QScrollArea, QWidget, QSpinBox, QSlider, QCheckBox, + QScrollArea, ) from game import Game @@ -147,7 +147,12 @@ class QFlightPayloadTab(QFrame): scroll.setWidgetResizable(True) scroll.setWidget(scroll_content) scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - layout.addWidget(scroll) + layout.addWidget(scroll, stretch=1) + + self.property_editor = PropertyEditor( + self.flight, self.member_selector.selected_member + ) + scrolling_layout.addLayout(self.property_editor) # Docs Link docsText = QLabel( @@ -156,26 +161,15 @@ class QFlightPayloadTab(QFrame): docsText.setAlignment(Qt.AlignCenter) docsText.setOpenExternalLinks(True) - self.scroll_area = QScrollArea() - self.property_editor = QWidget() - self.property_editor.setLayout(PropertyEditor(self.flight)) - self.scroll_area.setWidget(self.property_editor) - layout.addWidget(self.scroll_area) - self.fuel_selector = DcsFuelSelector(flight) 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) + layout.addWidget(self.payload_editor, stretch=3) layout.addWidget(docsText) self.setLayout(layout) @@ -232,7 +226,7 @@ class QFlightPayloadTab(QFrame): 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(): diff --git a/qt_ui/windows/mission/flight/payload/propertycheckbox.py b/qt_ui/windows/mission/flight/payload/propertycheckbox.py index f5d1caee..f2599db2 100644 --- a/qt_ui/windows/mission/flight/payload/propertycheckbox.py +++ b/qt_ui/windows/mission/flight/payload/propertycheckbox.py @@ -1,4 +1,4 @@ -from PySide6.QtWidgets import QCheckBox +from PySide2.QtWidgets import QCheckBox from dcs.unitpropertydescription import UnitPropertyDescription from game.ato.flightmember import FlightMember diff --git a/qt_ui/windows/mission/flight/payload/propertycombobox.py b/qt_ui/windows/mission/flight/payload/propertycombobox.py index 0d407982..08471b48 100644 --- a/qt_ui/windows/mission/flight/payload/propertycombobox.py +++ b/qt_ui/windows/mission/flight/payload/propertycombobox.py @@ -1,4 +1,4 @@ -from PySide6.QtWidgets import QComboBox +from PySide2.QtWidgets import QComboBox from dcs.unitpropertydescription import UnitPropertyDescription from game.ato.flightmember import FlightMember diff --git a/qt_ui/windows/mission/flight/payload/propertyeditor.py b/qt_ui/windows/mission/flight/payload/propertyeditor.py index c17f8cea..bb3f30b0 100644 --- a/qt_ui/windows/mission/flight/payload/propertyeditor.py +++ b/qt_ui/windows/mission/flight/payload/propertyeditor.py @@ -1,7 +1,8 @@ import logging -from typing import Callable +from typing import Callable, Optional -from PySide2.QtWidgets import QGridLayout, QLabel +from PySide2.QtCore import QRect +from PySide2.QtWidgets import QGridLayout, QLabel, QWidget from dcs.unitpropertydescription import UnitPropertyDescription from game.ato import Flight @@ -23,6 +24,10 @@ class PropertyEditor(QGridLayout): self.flight_member = flight_member self.flight_member_update_listeners: list[Callable[[FlightMember], None]] = [] + self.build_props(flight) + + def build_props(self, flight): + self.setGeometry(QRect()) for row, prop in enumerate(flight.unit_type.iter_props()): if prop.label is None: if prop.control != "label": @@ -54,7 +59,7 @@ class PropertyEditor(QGridLayout): if widget is not None: self.addWidget(widget, row, 1) - def control_for_property(self, prop: UnitPropertyDescription) -> QWidget | None: + def control_for_property(self, prop: UnitPropertyDescription) -> Optional[QWidget]: # Valid values are: # "checkbox", "comboList", "groupbox", "label", "slider", "spinbox" match prop.control: diff --git a/qt_ui/windows/mission/flight/payload/propertyspinbox.py b/qt_ui/windows/mission/flight/payload/propertyspinbox.py index f9a52913..cd059608 100644 --- a/qt_ui/windows/mission/flight/payload/propertyspinbox.py +++ b/qt_ui/windows/mission/flight/payload/propertyspinbox.py @@ -1,4 +1,4 @@ -from PySide6.QtWidgets import QSpinBox +from PySide2.QtWidgets import QSpinBox from dcs.unitpropertydescription import UnitPropertyDescription from game.ato.flightmember import FlightMember diff --git a/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py b/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py index 3c9bd96d..759a4be6 100644 --- a/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py +++ b/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py @@ -91,8 +91,10 @@ class PilotSelector(QComboBox): class PilotControls(QHBoxLayout): + player_toggled = Signal() + def __init__( - self, squadron: Union[Squadron, None], roster: Optional[FlightRoster], idx: int + self, squadron: Optional[Squadron], roster: Optional[FlightRoster], idx: int ) -> None: super().__init__() self.roster = roster @@ -125,6 +127,7 @@ class PilotControls(QHBoxLayout): logging.error("Cannot toggle state of a pilot when none is selected") return pilot.player = checked + self.player_toggled.emit() def on_pilot_changed(self, index: int) -> None: pilot = self.selector.itemData(index) @@ -168,7 +171,9 @@ class PilotControls(QHBoxLayout): class FlightRosterEditor(QVBoxLayout): MAX_PILOTS = 4 - def __init__(self, squadron: Union[Squadron, None], roster: Union[IFlightRoster, None]) -> None: + def __init__( + self, squadron: Optional[Squadron], roster: Optional[IFlightRoster] + ) -> None: super().__init__() self.roster = roster diff --git a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py index ca4e86d8..24eb2b61 100644 --- a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py +++ b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py @@ -3,6 +3,7 @@ from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout from game.ato.flight import Flight from qt_ui.models import PackageModel, GameModel +from qt_ui.windows.mission.flight.payload.QFlightPayloadTab import QFlightPayloadTab from qt_ui.windows.mission.flight.settings.FlightPlanPropertiesGroup import ( FlightPlanPropertiesGroup, ) @@ -27,11 +28,16 @@ class QGeneralFlightSettingsTab(QFrame): package_model: PackageModel, flight: Flight, flight_wpt_list: QFlightWaypointList, + payload_tab: QFlightPayloadTab, ): super().__init__() self.flight_slot_editor = QFlightSlotEditor(package_model, flight, game.game) self.flight_slot_editor.flight_resized.connect(self.flight_size_changed) + for pc in self.flight_slot_editor.roster_editor.pilot_controls: + pc.player_toggled.connect( + lambda: payload_tab.property_editor.build_props(flight) + ) widgets = [ QFlightTypeTaskInfo(flight),