diff --git a/qt_ui/windows/AirWingDialog.py b/qt_ui/windows/AirWingDialog.py index ef2a9c80..4863934b 100644 --- a/qt_ui/windows/AirWingDialog.py +++ b/qt_ui/windows/AirWingDialog.py @@ -28,6 +28,7 @@ from qt_ui.models import AirWingModel, AtoModel, GameModel, SquadronModel from qt_ui.simcontroller import SimController from qt_ui.windows.AirWingConfigurationDialog import AirWingConfigurationDialog from qt_ui.windows.SquadronDialog import SquadronDialog +from qt_ui.windows.newgame.WizardPages.QFactionSelection import QFactionUnits class SquadronDelegate(TwoColumnRowDelegate): @@ -238,6 +239,8 @@ class AirWingTabs(QTabWidget): def __init__(self, game_model: GameModel) -> None: super().__init__() + self.game_model = game_model + self.addTab( SquadronList( game_model.ato_model, @@ -260,18 +263,33 @@ class AirWingTabs(QTabWidget): if game_model.game.settings.enable_air_wing_adjustments: pb = QPushButton("Open Air Wing Config Dialog") - pb.clicked.connect(lambda _: self.open_awcd(game_model)) + pb.clicked.connect(self.open_awcd) pb.setMaximumWidth(300) layout = QHBoxLayout() layout.addWidget(pb) w = QWidget(layout=layout) self.addTab(w, "Cheats") - def open_awcd(self, gm: GameModel): - AirWingConfigurationDialog(gm.game, True, self).exec_() + self.addTab( + QFactionUnits( + game_model.game.coalition_for(True).faction, + self, + ), + "Faction OWNFOR", + ) + self.addTab( + QFactionUnits( + game_model.game.coalition_for(False).faction, + self, + ), + "Faction OPFOR", + ) + + def open_awcd(self): + AirWingConfigurationDialog(self.game_model.game, True, self).exec_() events = GameUpdateEvents().begin_new_turn() EventStream.put_nowait(events) - gm.ato_model.on_sim_update(events) + self.game_model.ato_model.on_sim_update(events) class AirWingDialog(QDialog): diff --git a/qt_ui/windows/newgame/WizardPages/QFactionSelection.py b/qt_ui/windows/newgame/WizardPages/QFactionSelection.py index befb2ce1..9d4e1186 100644 --- a/qt_ui/windows/newgame/WizardPages/QFactionSelection.py +++ b/qt_ui/windows/newgame/WizardPages/QFactionSelection.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals from copy import deepcopy -from typing import Union +from typing import Union, Callable, Set, Optional from PySide6 import QtWidgets, QtGui from PySide6.QtCore import Qt @@ -12,9 +12,16 @@ from PySide6.QtWidgets import ( QCheckBox, QLabel, QTextBrowser, + QPushButton, + QComboBox, + QHBoxLayout, ) +from game.ato import FlightType from game.campaignloader import Campaign +from game.dcs.aircrafttype import AircraftType +from game.dcs.groundunittype import GroundUnitType +from game.dcs.unittype import UnitType from game.factions import Faction, FACTIONS from qt_ui.windows.newgame.jinja_env import jinja_env @@ -29,7 +36,13 @@ class QFactionUnits(QScrollArea): self.faction = faction self._create_checkboxes() - def _add_checkboxes(self, units: set, counter: int, grid: QGridLayout) -> int: + def _add_checkboxes( + self, + units: set, + counter: int, + grid: QGridLayout, + combo_layout: Optional[QHBoxLayout] = None, + ) -> int: counter += 1 for i, v in enumerate(sorted(units, key=lambda x: str(x)), counter): cb = QCheckBox(str(v)) @@ -37,6 +50,9 @@ class QFactionUnits(QScrollArea): self.checkboxes[str(v)] = cb grid.addWidget(cb, i, 1) counter += 1 + if combo_layout: + counter += 1 + grid.addLayout(combo_layout, counter, 1) counter += 1 return counter @@ -44,18 +60,47 @@ class QFactionUnits(QScrollArea): counter = 0 self.checkboxes: dict[str, QCheckBox] = {} grid = QGridLayout() + grid.setColumnStretch(1, 1) if len(self.faction.aircraft) > 0: + self.add_ac_combo = QComboBox() + hbox = self._create_aircraft_combobox( + self.add_ac_combo, + lambda: self._on_add_ac(self.faction.aircraft, self.add_ac_combo), + self._aircraft_predicate, + ) grid.addWidget(QLabel("Aircraft:"), counter, 0) - counter = self._add_checkboxes(self.faction.aircraft, counter, grid) + counter = self._add_checkboxes(self.faction.aircraft, counter, grid, hbox) if len(self.faction.awacs) > 0: + self.add_awacs_combo = QComboBox() + hbox = self._create_aircraft_combobox( + self.add_awacs_combo, + lambda: self._on_add_ac(self.faction.awacs, self.add_awacs_combo), + self._awacs_predicate, + ) grid.addWidget(QLabel("AWACS:"), counter, 0) - counter = self._add_checkboxes(self.faction.awacs, counter, grid) + counter = self._add_checkboxes(self.faction.awacs, counter, grid, hbox) if len(self.faction.tankers) > 0: + self.add_tanker_combo = QComboBox() + hbox = self._create_aircraft_combobox( + self.add_tanker_combo, + lambda: self._on_add_ac(self.faction.tankers, self.add_tanker_combo), + self._tanker_predicate, + ) grid.addWidget(QLabel("Tankers:"), counter, 0) - counter = self._add_checkboxes(self.faction.tankers, counter, grid) + counter = self._add_checkboxes(self.faction.tankers, counter, grid, hbox) if len(self.faction.frontline_units) > 0: + self.add_frontline_combo = QComboBox() + hbox = self._create_unit_combobox( + self.add_frontline_combo, + lambda: self._on_add_unit( + self.faction.frontline_units, self.add_frontline_combo + ), + self.faction.frontline_units, + ) grid.addWidget(QLabel("Frontlines vehicles:"), counter, 0) - counter = self._add_checkboxes(self.faction.frontline_units, counter, grid) + counter = self._add_checkboxes( + self.faction.frontline_units, counter, grid, hbox + ) if len(self.faction.artillery_units) > 0: grid.addWidget(QLabel("Artillery units:"), counter, 0) counter = self._add_checkboxes(self.faction.artillery_units, counter, grid) @@ -82,6 +127,76 @@ class QFactionUnits(QScrollArea): self.content.setLayout(grid) + def _aircraft_predicate(self, ac: AircraftType): + if ( + FlightType.AEWC not in ac.task_priorities + and FlightType.REFUELING not in ac.task_priorities + ): + self.add_ac_combo.addItem(ac.variant_id, ac) + + def _awacs_predicate(self, ac: AircraftType): + if FlightType.AEWC in ac.task_priorities: + self.add_awacs_combo.addItem(ac.variant_id, ac) + + def _tanker_predicate(self, ac: AircraftType): + if FlightType.REFUELING in ac.task_priorities: + self.add_tanker_combo.addItem(ac.variant_id, ac) + + def _create_aircraft_combobox( + self, cb: QComboBox, callback: Callable, predicate: Callable + ): + for ac_dcs in sorted(AircraftType.each_dcs_type(), key=lambda x: x.id): + if ( + ac_dcs not in self.faction.country.planes + and ac_dcs not in self.faction.country.helicopters + ): + continue + for ac in AircraftType.for_dcs_type(ac_dcs): + if ac in self.faction.aircraft: + continue + predicate(ac) + add_ac = QPushButton("+") + add_ac.setStyleSheet("QPushButton{ font-weight: bold; }") + add_ac.setFixedWidth(50) + add_ac.clicked.connect(callback) + hbox = QHBoxLayout() + hbox.addWidget(cb) + hbox.addWidget(add_ac) + return hbox + + def _create_unit_combobox( + self, cb: QComboBox, callback: Callable, units: Set[GroundUnitType] + ): + for dcs_unit in sorted(GroundUnitType.each_dcs_type(), key=lambda x: x.id): + if dcs_unit not in self.faction.country.vehicles: + continue + for unit in GroundUnitType.for_dcs_type(dcs_unit): + if unit in units: + continue + cb.addItem(unit.variant_id, unit) + add_unit = QPushButton("+") + add_unit.setStyleSheet("QPushButton{ font-weight: bold; }") + add_unit.setFixedWidth(50) + add_unit.clicked.connect(callback) + hbox = QHBoxLayout() + hbox.addWidget(cb) + hbox.addWidget(add_unit) + return hbox + + def _on_add_unit(self, units: Set[UnitType], cb: QComboBox): + units.add(cb.currentData()) + if self.faction.__dict__.get("accessible_units"): + # invalidate the cached property + del self.faction.__dict__["accessible_units"] + self.updateFaction(self.faction) + + def _on_add_ac(self, aircraft: Set[AircraftType], cb: QComboBox): + aircraft.add(cb.currentData()) + if self.faction.__dict__.get("all_aircrafts"): + # invalidate the cached property + del self.faction.__dict__["all_aircrafts"] + self.updateFaction(self.faction) + def updateFaction(self, faction: Faction): self.faction = faction self.content = QWidget() diff --git a/requirements.txt b/requirements.txt index 689fc4b8..005bf5e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,7 +32,7 @@ pluggy==1.5.0 pre-commit==3.7.0 pydantic==2.7.1 pydantic-settings==2.2.1 -pydcs @ git+https://github.com/dcs-retribution/pydcs@a8f3a0b26b78c37c88e58e7f32a30305a1568de5 +pydcs @ git+https://github.com/dcs-retribution/pydcs@805088f20263025eda61398d10383e4d73945dc7 pyinstaller==5.13.2 pyinstaller-hooks-contrib==2024.0 pyparsing==3.1.2