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