From 192741af36c424415004fb509c4f669c4556803e Mon Sep 17 00:00:00 2001 From: Raffson Date: Sat, 31 Dec 2022 23:25:26 +0100 Subject: [PATCH] Make units selectable in faction overview during campaign gen Resolves #35 Resolves #40 --- changelog.md | 1 + game/factions/faction.py | 12 +- qt_ui/windows/newgame/QNewGameWizard.py | 140 ++++++++++++++++++- resources/factions/wrl_taskforcered.json | 2 +- resources/ui/templates/factiontemplate_EN.j2 | 60 +------- resources/ui/templates/factiontemplate_FR.j2 | 50 +------ 6 files changed, 145 insertions(+), 120 deletions(-) diff --git a/changelog.md b/changelog.md index 6d871dac..b834bc31 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ * **[Mission Generation]** Given a CAS flight was planned, delay ground force attack until first CAS flight is on station * **[Mission Generation]** Add option to switch ATFLIR to LITENING automatically for ground based F-18C flights * **[Cheat Menu]** Option to instantly transfer squadrons across bases. +* **[UI]** Add selectable units in faction overview during campaign generation. ## Fixes * **[UI]** Removed deprecated options diff --git a/game/factions/faction.py b/game/factions/faction.py index 8ff00471..ddea0e76 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -60,7 +60,7 @@ class Faction: description: str = field(default="") # Available aircraft - aircrafts: List[AircraftType] = field(default_factory=list) + aircraft: List[AircraftType] = field(default_factory=list) # Available awacs aircraft awacs: List[AircraftType] = field(default_factory=list) @@ -178,6 +178,10 @@ class Faction: ) return sorted(air_defenses) + @cached_property + def aircrafts(self) -> list[UnitType[Any]]: + return list(self.aircraft + self.awacs + self.tankers) + @classmethod def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction: faction = Faction(locales=json.get("locales")) @@ -206,14 +210,10 @@ class Faction: faction.authors = json.get("authors", "") faction.description = json.get("description", "") - faction.aircrafts = [AircraftType.named(n) for n in json.get("aircrafts", [])] + faction.aircraft = [AircraftType.named(n) for n in json.get("aircrafts", [])] faction.awacs = [AircraftType.named(n) for n in json.get("awacs", [])] faction.tankers = [AircraftType.named(n) for n in json.get("tankers", [])] - faction.aircrafts = list( - set(faction.aircrafts + faction.awacs + faction.tankers) - ) - faction.frontline_units = [ GroundUnitType.named(n) for n in json.get("frontline_units", []) ] diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index b0efaf39..c774150c 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -1,16 +1,28 @@ from __future__ import unicode_literals import logging +import math from datetime import datetime, timedelta from typing import List from PySide2 import QtGui, QtWidgets from PySide2.QtCore import QDate, QItemSelectionModel, QPoint, Qt, Signal -from PySide2.QtWidgets import QCheckBox, QLabel, QTextEdit, QVBoxLayout, QTextBrowser +from PySide2.QtWidgets import ( + QCheckBox, + QLabel, + QTextEdit, + QVBoxLayout, + QTextBrowser, + QWidget, + QGridLayout, + QScrollArea, + QSizePolicy, +) from jinja2 import Environment, FileSystemLoader, select_autoescape from game.campaignloader.campaign import Campaign, DEFAULT_BUDGET from game.dcs.aircrafttype import AircraftType +from game.dcs.unittype import UnitType from game.factions import FACTIONS, Faction from game.settings import Settings from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings @@ -236,6 +248,87 @@ class IntroPage(QtWidgets.QWizardPage): self.setLayout(layout) +class QFactionUnits(QScrollArea): + def __init__(self, faction: Faction, parent=None): + super().__init__() + self.setWidgetResizable(True) + self.content = QWidget() + self.setWidget(self.content) + self.parent = parent + self.faction = faction + self._create_checkboxes() + + def _add_checkboxes(self, units: list, counter: int, grid: QGridLayout) -> int: + counter += 1 + for i, v in enumerate(sorted(units, key=lambda x: x.name), counter): + cb = QCheckBox(v.name) + cb.setCheckState(Qt.CheckState.Checked) + self.checkboxes[v.name] = cb + grid.addWidget(cb, i, 1) + counter += 1 + counter += 1 + return counter + + def _create_checkboxes(self): + counter = 0 + self.checkboxes: dict[str, QCheckBox] = {} + grid = QGridLayout() + if len(self.faction.aircraft) > 0: + grid.addWidget(QLabel("Aircraft:"), counter, 0) + counter = self._add_checkboxes(self.faction.aircraft, counter, grid) + if len(self.faction.awacs) > 0: + grid.addWidget(QLabel("AWACS:"), counter, 0) + counter = self._add_checkboxes(self.faction.awacs, counter, grid) + if len(self.faction.tankers) > 0: + grid.addWidget(QLabel("Tankers:"), counter, 0) + counter = self._add_checkboxes(self.faction.tankers, counter, grid) + if len(self.faction.frontline_units) > 0: + grid.addWidget(QLabel("Frontlines vehicles:"), counter, 0) + counter = self._add_checkboxes(self.faction.frontline_units, counter, grid) + if len(self.faction.artillery_units) > 0: + grid.addWidget(QLabel("Artillery units:"), counter, 0) + counter = self._add_checkboxes(self.faction.artillery_units, counter, grid) + if len(self.faction.logistics_units) > 0: + grid.addWidget(QLabel("Logistics units:"), counter, 0) + counter = self._add_checkboxes(self.faction.logistics_units, counter, grid) + if len(self.faction.infantry_units) > 0: + grid.addWidget(QLabel("Infantry units:"), counter, 0) + counter = self._add_checkboxes(self.faction.infantry_units, counter, grid) + if len(self.faction.preset_groups) > 0: + grid.addWidget(QLabel("Preset groups:"), counter, 0) + counter = self._add_checkboxes(self.faction.preset_groups, counter, grid) + if len(self.faction.air_defense_units) > 0: + grid.addWidget(QLabel("Air defenses:"), counter, 0) + counter = self._add_checkboxes( + self.faction.air_defense_units, counter, grid + ) + if len(self.faction.naval_units) > 0: + grid.addWidget(QLabel("Naval units:"), counter, 0) + counter = self._add_checkboxes(self.faction.naval_units, counter, grid) + if len(self.faction.missiles) > 0: + grid.addWidget(QLabel("Missile units:"), counter, 0) + self._add_checkboxes(self.faction.missiles, counter, grid) + + self.content.setLayout(grid) + + def updateFaction(self, faction: Faction): + self.faction = faction + self.content = QWidget() + self.setWidget(self.content) + self._create_checkboxes() + self.update() + if self.parent: + self.parent.update() + + def updateFactionUnits(self, units: list): + deletes = [] + for a in units: + if not self.checkboxes[a.name].isChecked(): + deletes.append(a) + for d in deletes: + units.remove(d) + + class FactionSelection(QtWidgets.QWizardPage): def __init__(self, parent=None): super(FactionSelection, self).__init__(parent) @@ -259,8 +352,6 @@ class FactionSelection(QtWidgets.QWizardPage): blueFaction = QtWidgets.QLabel("Player Faction :") self.blueFactionSelect = QtWidgets.QComboBox() - for f in FACTIONS: - self.blueFactionSelect.addItem(f) blueFaction.setBuddy(self.blueFactionSelect) redFaction = QtWidgets.QLabel("Enemy Faction :") @@ -271,26 +362,41 @@ class FactionSelection(QtWidgets.QWizardPage): self.blueFactionDescription = QTextBrowser() self.blueFactionDescription.setReadOnly(True) self.blueFactionDescription.setOpenExternalLinks(True) + self.blueFactionDescription.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.blueFactionDescription.setMaximumHeight(120) self.redFactionDescription = QTextBrowser() self.redFactionDescription.setReadOnly(True) self.redFactionDescription.setOpenExternalLinks(True) + self.redFactionDescription.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.redFactionDescription.setMaximumHeight(120) # Setup default selected factions for i, r in enumerate(FACTIONS): - self.redFactionSelect.addItem(r) + self.blueFactionSelect.addItem(r, FACTIONS[r]) + self.redFactionSelect.addItem(r, FACTIONS[r]) if r == "Russia 1990": self.redFactionSelect.setCurrentIndex(i) if r == "USA 2005": self.blueFactionSelect.setCurrentIndex(i) + # Faction units + self.blueFactionUnits = QFactionUnits( + self.blueFactionSelect.currentData(), self.blueGroupLayout + ) + self.redFactionUnits = QFactionUnits( + self.redFactionSelect.currentData(), self.redGroupLayout + ) + self.blueGroupLayout.addWidget(blueFaction, 0, 0) self.blueGroupLayout.addWidget(self.blueFactionSelect, 0, 1) self.blueGroupLayout.addWidget(self.blueFactionDescription, 1, 0, 1, 2) + self.blueGroupLayout.addWidget(self.blueFactionUnits, 2, 0, 1, 2) self.redGroupLayout.addWidget(redFaction, 0, 0) self.redGroupLayout.addWidget(self.redFactionSelect, 0, 1) self.redGroupLayout.addWidget(self.redFactionDescription, 1, 0, 1, 2) + self.redGroupLayout.addWidget(self.redFactionUnits, 2, 0, 1, 2) self.factionsGroupLayout.addLayout(self.blueGroupLayout) self.factionsGroupLayout.addLayout(self.redGroupLayout) @@ -348,13 +454,35 @@ class FactionSelection(QtWidgets.QWizardPage): self.blueFactionDescription.setText(blue_faction_txt) self.redFactionDescription.setText(red_faction_txt) + self.blueGroupLayout.removeWidget(self.blueFactionUnits) + self.blueFactionUnits.updateFaction(blue_faction) + self.blueGroupLayout.addWidget(self.blueFactionUnits, 2, 0, 1, 2) + self.redGroupLayout.removeWidget(self.redFactionUnits) + self.redFactionUnits.updateFaction(red_faction) + self.redGroupLayout.addWidget(self.redFactionUnits, 2, 0, 1, 2) + + @staticmethod + def _filter_selected_units(qfu: QFactionUnits) -> Faction: + qfu.updateFactionUnits(qfu.faction.aircrafts) + qfu.updateFactionUnits(qfu.faction.awacs) + qfu.updateFactionUnits(qfu.faction.tankers) + qfu.updateFactionUnits(qfu.faction.frontline_units) + qfu.updateFactionUnits(qfu.faction.artillery_units) + qfu.updateFactionUnits(qfu.faction.logistics_units) + qfu.updateFactionUnits(qfu.faction.infantry_units) + qfu.updateFactionUnits(qfu.faction.preset_groups) + qfu.updateFactionUnits(qfu.faction.air_defense_units) + qfu.updateFactionUnits(qfu.faction.naval_units) + qfu.updateFactionUnits(qfu.faction.missiles) + return qfu.faction + @property def selected_blue_faction(self) -> Faction: - return FACTIONS[self.blueFactionSelect.currentText()] + return self._filter_selected_units(self.blueFactionUnits) @property def selected_red_faction(self) -> Faction: - return FACTIONS[self.redFactionSelect.currentText()] + return self._filter_selected_units(self.redFactionUnits) class TheaterConfiguration(QtWidgets.QWizardPage): diff --git a/resources/factions/wrl_taskforcered.json b/resources/factions/wrl_taskforcered.json index b68a0be2..0969c38e 100644 --- a/resources/factions/wrl_taskforcered.json +++ b/resources/factions/wrl_taskforcered.json @@ -76,7 +76,7 @@ "Paratrooper AKS", "Paratrooper RPG-16" ], -"missiles": [ + "missiles": [ "SSM SS-1C Scud-B" ], "preset_groups": [ diff --git a/resources/ui/templates/factiontemplate_EN.j2 b/resources/ui/templates/factiontemplate_EN.j2 index b48f3349..1e40e777 100644 --- a/resources/ui/templates/factiontemplate_EN.j2 +++ b/resources/ui/templates/factiontemplate_EN.j2 @@ -1,62 +1,4 @@ -{{ faction.description|safe }} +Description: {{ faction.description|safe }}
Author(s): {{ faction.authors }} -

- - -Potential aircraft: -

- The aircraft that will be present in the game are specified by the campaign. - Only aircraft in this list will be allowed, but not all aircraft in this - list will necessarily be available. -

-

- If the campaign you chose doesn't include the aircraft you want to fly, you - can mod the campaign by following the squadron section of the - - custom campaigns guide. -

- -
- -Frontlines vehicles: - -
- -Artillery units: - -
- -Air defenses: - -
diff --git a/resources/ui/templates/factiontemplate_FR.j2 b/resources/ui/templates/factiontemplate_FR.j2 index e772c3e2..a411a5fc 100644 --- a/resources/ui/templates/factiontemplate_FR.j2 +++ b/resources/ui/templates/factiontemplate_FR.j2 @@ -1,50 +1,4 @@ -{{ faction.description|safe }} +Description: {{ faction.description|safe }}
-Auteur(s): {{ faction.authors }} -

- - -Aéronefs disponibles : - -
- -Véhicules disponibles : - -
- -Pièces d'artillerie : - -
- -Défense Sol-Air: - -
+Auteur(s): {{ faction.authors }} \ No newline at end of file