From 0923add51411e6579b2a2d39d9932870f474e281 Mon Sep 17 00:00:00 2001 From: Raffson Date: Sat, 27 May 2023 16:44:01 +0200 Subject: [PATCH] Break QNewGameWizard up into pieces --- qt_ui/windows/newgame/QCampaignList.py | 67 -- qt_ui/windows/newgame/QNewGameWizard.py | 812 +----------------- qt_ui/windows/newgame/SettingNames.py | 12 + .../newgame/WizardPages/QFactionSelection.py | 248 ++++++ .../newgame/WizardPages/QGeneratorSettings.py | 236 +++++ .../newgame/WizardPages/QNewGameSettings.py | 84 ++ .../WizardPages/QTheaterConfiguration.py | 293 +++++++ qt_ui/windows/newgame/jinja_env.py | 14 + 8 files changed, 898 insertions(+), 868 deletions(-) delete mode 100644 qt_ui/windows/newgame/QCampaignList.py create mode 100644 qt_ui/windows/newgame/SettingNames.py create mode 100644 qt_ui/windows/newgame/WizardPages/QFactionSelection.py create mode 100644 qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py create mode 100644 qt_ui/windows/newgame/WizardPages/QNewGameSettings.py create mode 100644 qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py create mode 100644 qt_ui/windows/newgame/jinja_env.py diff --git a/qt_ui/windows/newgame/QCampaignList.py b/qt_ui/windows/newgame/QCampaignList.py deleted file mode 100644 index 0054d0ad..00000000 --- a/qt_ui/windows/newgame/QCampaignList.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from PySide2 import QtGui -from PySide2.QtCore import QItemSelectionModel, QModelIndex, Qt -from PySide2.QtGui import QPixmap, QStandardItem, QStandardItemModel -from PySide2.QtWidgets import QAbstractItemView, QListView - -from game.campaignloader.campaign import Campaign -from qt_ui.liberation_install import get_dcs_install_directory - - -class QCampaignItem(QStandardItem): - def __init__(self, campaign: Campaign) -> None: - super(QCampaignItem, self).__init__() - self.setData(campaign, QCampaignList.CampaignRole) - - # Define terrain icon path from the DCS installation directory by default - dcs_path = get_dcs_install_directory() - icon_path = dcs_path / campaign.menu_thumbnail_dcs_relative_path - - # If the path does not exist (user does not have the terrain installed), - # use the old icons as fallback to avoid an ugly campaign list with missing icons - if not icon_path.exists(): - icon_path = campaign.fallback_icon_path - - self.setIcon(QtGui.QIcon(QPixmap(str(icon_path)))) - self.setEditable(False) - if campaign.is_compatible: - name = campaign.name - else: - name = f"[INCOMPATIBLE] {campaign.name}" - self.setText(name) - - -class QCampaignList(QListView): - CampaignRole = Qt.UserRole - - def __init__(self, campaigns: list[Campaign], show_incompatible: bool) -> None: - super(QCampaignList, self).__init__() - self.campaign_model = QStandardItemModel(self) - self.setModel(self.campaign_model) - self.setMinimumWidth(250) - self.setMinimumHeight(350) - self.campaigns = campaigns - self.setSelectionBehavior(QAbstractItemView.SelectItems) - self.setup_content(show_incompatible) - - @property - def selected_campaign(self) -> Optional[Campaign]: - return self.currentIndex().data(QCampaignList.CampaignRole) - - def setup_content(self, show_incompatible: bool) -> None: - self.selectionModel().blockSignals(True) - try: - self.campaign_model.clear() - for campaign in self.campaigns: - if show_incompatible or campaign.is_compatible: - item = QCampaignItem(campaign) - self.campaign_model.appendRow(item) - finally: - self.selectionModel().blockSignals(False) - - self.selectionModel().setCurrentIndex( - self.campaign_model.index(0, 0, QModelIndex()), QItemSelectionModel.Select - ) diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index 090fd28b..edb07c83 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -1,101 +1,20 @@ from __future__ import unicode_literals import logging -from copy import deepcopy -from datetime import datetime, timedelta -from typing import List +from datetime import timedelta from PySide2 import QtGui, QtWidgets -from PySide2.QtCore import QDate, QItemSelectionModel, QPoint, Qt, Signal -from PySide2.QtWidgets import ( - QCheckBox, - QLabel, - QTextEdit, - QVBoxLayout, - QTextBrowser, - QWidget, - QGridLayout, - QScrollArea, -) -from jinja2 import Environment, FileSystemLoader, select_autoescape -from game.campaignloader.campaign import Campaign, DEFAULT_BUDGET +from game.campaignloader.campaign import Campaign from game.dcs.aircrafttype import AircraftType -from game.factions import FACTIONS, Faction from game.settings import Settings from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings -from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar -from qt_ui.widgets.spinsliders import CurrencySpinner, FloatSpinSlider, TimeInputs from qt_ui.windows.AirWingConfigurationDialog import AirWingConfigurationDialog -from qt_ui.windows.newgame.QCampaignList import QCampaignList - -jinja_env = Environment( - loader=FileSystemLoader("resources/ui/templates"), - autoescape=select_autoescape( - disabled_extensions=("",), - default_for_string=True, - default=True, - ), - trim_blocks=True, - lstrip_blocks=True, -) - -DEFAULT_MISSION_LENGTH: timedelta = timedelta(minutes=60) - - -""" -Possible time periods for new games - - `Name`: daytime(day, month, year), - -`Identifier` is the name that will appear in the menu -The object is a python datetime object -""" -TIME_PERIODS = { - "WW2 - Winter [1944]": datetime(1944, 1, 1), - "WW2 - Spring [1944]": datetime(1944, 4, 1), - "WW2 - Summer [1944]": datetime(1944, 6, 1), - "WW2 - Fall [1944]": datetime(1944, 10, 1), - "Early Cold War - Winter [1952]": datetime(1952, 1, 1), - "Early Cold War - Spring [1952]": datetime(1952, 4, 1), - "Early Cold War - Summer [1952]": datetime(1952, 6, 1), - "Early Cold War - Fall [1952]": datetime(1952, 10, 1), - "Cold War - Winter [1970]": datetime(1970, 1, 1), - "Cold War - Spring [1970]": datetime(1970, 4, 1), - "Cold War - Summer [1970]": datetime(1970, 6, 1), - "Cold War - Fall [1970]": datetime(1970, 10, 1), - "Late Cold War - Winter [1985]": datetime(1985, 1, 1), - "Late Cold War - Spring [1985]": datetime(1985, 4, 1), - "Late Cold War - Summer [1985]": datetime(1985, 6, 1), - "Late Cold War - Fall [1985]": datetime(1985, 10, 1), - "Gulf War - Winter [1990]": datetime(1990, 1, 1), - "Gulf War - Spring [1990]": datetime(1990, 4, 1), - "Gulf War - Summer [1990]": datetime(1990, 6, 1), - "Mid-90s - Winter [1995]": datetime(1995, 1, 1), - "Mid-90s - Spring [1995]": datetime(1995, 4, 1), - "Mid-90s - Summer [1995]": datetime(1995, 6, 1), - "Mid-90s - Fall [1995]": datetime(1995, 10, 1), - "Gulf War - Fall [1990]": datetime(1990, 10, 1), - "Modern - Winter [2010]": datetime(2010, 1, 1), - "Modern - Spring [2010]": datetime(2010, 4, 1), - "Modern - Summer [2010]": datetime(2010, 6, 1), - "Modern - Fall [2010]": datetime(2010, 10, 1), - "Georgian War [2008]": datetime(2008, 8, 7), - "Syrian War [2011]": datetime(2011, 3, 15), - "6 days war [1967]": datetime(1967, 6, 5), - "Yom Kippour War [1973]": datetime(1973, 10, 6), - "First Lebanon War [1982]": datetime(1982, 6, 6), - "Arab-Israeli War [1948]": datetime(1948, 5, 15), -} - -# fmt: off -RUNWAY_REPAIR = f"{Settings.automate_runway_repair=}".split("=")[0].split(".")[1] -FRONTLINE = f"{Settings.automate_front_line_reinforcements=}".split("=")[0].split(".")[1] -AIRCRAFT = f"{Settings.automate_aircraft_reinforcements=}".split("=")[0].split(".")[1] -MISSION_LENGTH = f"{Settings.desired_player_mission_duration=}".split("=")[0].split(".")[1] -SUPER_CARRIER = f"{Settings.supercarrier=}".split("=")[0].split(".")[1] -SQN_AC_LIMITS = f"{Settings.enable_squadron_aircraft_limits=}".split("=")[0].split(".")[1] -# fmt: on +from qt_ui.windows.newgame.SettingNames import RUNWAY_REPAIR, FRONTLINE, AIRCRAFT, MISSION_LENGTH, SUPER_CARRIER +from qt_ui.windows.newgame.WizardPages.QFactionSelection import FactionSelection +from qt_ui.windows.newgame.WizardPages.QGeneratorSettings import GeneratorOptions +from qt_ui.windows.newgame.WizardPages.QNewGameSettings import NewGameSettings +from qt_ui.windows.newgame.WizardPages.QTheaterConfiguration import TheaterConfiguration, TIME_PERIODS class NewGameWizard(QtWidgets.QWizard): @@ -114,12 +33,12 @@ class NewGameWizard(QtWidgets.QWizard): self.addPage(self.faction_selection_page) self.go_page = GeneratorOptions(self.campaigns[0], self) self.addPage(self.go_page) - self.difficulty_page = DifficultyAndAutomationOptions(self) - self.difficulty_page.set_campaign_values(self.campaigns[0]) + self.settings_page = NewGameSettings(self) + self.settings_page.set_campaign_values(self.campaigns[0]) # Update difficulty page on campaign select self.theater_page.campaign_selected.connect(lambda c: self.update_settings(c)) - self.addPage(self.difficulty_page) + self.addPage(self.settings_page) self.addPage(ConclusionPage(self)) self.setPixmap( @@ -244,7 +163,7 @@ class NewGameWizard(QtWidgets.QWizard): super(NewGameWizard, self).accept() def update_settings(self, campaign: Campaign) -> None: - self.difficulty_page.set_campaign_values(campaign) + self.settings_page.set_campaign_values(campaign) self.go_page.update_settings(campaign) @@ -269,715 +188,6 @@ 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) - - self.setTitle("Faction selection") - self.setSubTitle( - "\nChoose the two opposing factions and select the player side." - ) - self.setPixmap( - QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap("./resources/ui/misc/generator.png"), - ) - - self.setMinimumHeight(250) - - # Factions selection - self.factionsGroup = QtWidgets.QGroupBox("Factions") - self.factionsGroupLayout = QtWidgets.QHBoxLayout() - self.blueGroupLayout = QtWidgets.QGridLayout() - self.redGroupLayout = QtWidgets.QGridLayout() - - blueFaction = QtWidgets.QLabel("Player Faction :") - self.blueFactionSelect = QtWidgets.QComboBox() - blueFaction.setBuddy(self.blueFactionSelect) - - redFaction = QtWidgets.QLabel("Enemy Faction :") - self.redFactionSelect = QtWidgets.QComboBox() - redFaction.setBuddy(self.redFactionSelect) - - # Faction description - 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.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) - self.factionsGroup.setLayout(self.factionsGroupLayout) - - # Docs Link - docsText = QtWidgets.QLabel( - 'How to create your own faction' - ) - docsText.setAlignment(Qt.AlignCenter) - docsText.setOpenExternalLinks(True) - - # Link form fields - self.registerField("blueFaction", self.blueFactionSelect) - self.registerField("redFaction", self.redFactionSelect) - - # Build layout - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.factionsGroup) - layout.addWidget(docsText) - self.setLayout(layout) - self.updateUnitRecap() - - self.blueFactionSelect.activated.connect(self.updateUnitRecap) - self.redFactionSelect.activated.connect(self.updateUnitRecap) - - def setDefaultFactions(self, campaign: Campaign): - """Set default faction for selected campaign""" - - self.blueFactionSelect.clear() - self.redFactionSelect.clear() - - for f in FACTIONS: - self.blueFactionSelect.addItem(f) - - for i, r in enumerate(FACTIONS): - self.redFactionSelect.addItem(r) - if r == campaign.recommended_enemy_faction: - self.redFactionSelect.setCurrentIndex(i) - if r == campaign.recommended_player_faction: - self.blueFactionSelect.setCurrentIndex(i) - - self.updateUnitRecap() - - def updateUnitRecap(self): - red_faction = FACTIONS[self.redFactionSelect.currentText()] - blue_faction = FACTIONS[self.blueFactionSelect.currentText()] - - template = jinja_env.get_template("factiontemplate_EN.j2") - - blue_faction_txt = template.render({"faction": blue_faction}) - red_faction_txt = template.render({"faction": red_faction}) - - 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: - fac = deepcopy(qfu.faction) - qfu.updateFactionUnits(fac.aircrafts) - qfu.updateFactionUnits(fac.awacs) - qfu.updateFactionUnits(fac.tankers) - qfu.updateFactionUnits(fac.frontline_units) - qfu.updateFactionUnits(fac.artillery_units) - qfu.updateFactionUnits(fac.logistics_units) - qfu.updateFactionUnits(fac.infantry_units) - qfu.updateFactionUnits(fac.preset_groups) - qfu.updateFactionUnits(fac.air_defense_units) - qfu.updateFactionUnits(fac.naval_units) - qfu.updateFactionUnits(fac.missiles) - return fac - - @property - def selected_blue_faction(self) -> Faction: - return self._filter_selected_units(self.blueFactionUnits) - - @property - def selected_red_faction(self) -> Faction: - return self._filter_selected_units(self.redFactionUnits) - - -class TheaterConfiguration(QtWidgets.QWizardPage): - campaign_selected = Signal(Campaign) - - def __init__( - self, - campaigns: List[Campaign], - faction_selection: FactionSelection, - parent=None, - ) -> None: - super().__init__(parent) - - self.faction_selection = faction_selection - - self.setTitle("Theater configuration") - self.setSubTitle("\nChoose a terrain and time period for this game.") - self.setPixmap( - QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap("./resources/ui/wizard/logo1.png"), - ) - - self.setPixmap( - QtWidgets.QWizard.WatermarkPixmap, - QtGui.QPixmap("./resources/ui/wizard/watermark3.png"), - ) - - # List of campaigns - show_incompatible_campaigns_checkbox = QCheckBox( - text="Show incompatible campaigns" - ) - show_incompatible_campaigns_checkbox.setChecked(False) - self.campaignList = QCampaignList( - campaigns, show_incompatible_campaigns_checkbox.isChecked() - ) - show_incompatible_campaigns_checkbox.toggled.connect( - lambda checked: self.campaignList.setup_content(show_incompatible=checked) - ) - self.registerField("selectedCampaign", self.campaignList) - - # Faction description - self.campaignMapDescription = QTextBrowser() - self.campaignMapDescription.setReadOnly(True) - self.campaignMapDescription.setOpenExternalLinks(True) - self.campaignMapDescription.setMaximumHeight(200) - - self.performanceText = QTextEdit("") - self.performanceText.setReadOnly(True) - self.performanceText.setMaximumHeight(90) - - # Campaign settings - mapSettingsGroup = QtWidgets.QGroupBox("Map Settings") - mapSettingsLayout = QtWidgets.QGridLayout() - invertMap = QtWidgets.QCheckBox() - self.registerField("invertMap", invertMap) - mapSettingsLayout.addWidget(QtWidgets.QLabel("Invert Map"), 0, 0) - mapSettingsLayout.addWidget(invertMap, 0, 1) - self.advanced_iads = QtWidgets.QCheckBox() - self.registerField("advanced_iads", self.advanced_iads) - self.iads_label = QtWidgets.QLabel("Advanced IADS (WIP)") - mapSettingsLayout.addWidget(self.iads_label, 1, 0) - mapSettingsLayout.addWidget(self.advanced_iads, 1, 1) - mapSettingsGroup.setLayout(mapSettingsLayout) - - # Time Period - timeGroup = QtWidgets.QGroupBox("Time Period") - timePeriod = QtWidgets.QLabel("Start date :") - timePeriodSelect = QtWidgets.QComboBox() - timePeriodPresetLabel = QLabel("Use preset :") - timePeriodPreset = QtWidgets.QCheckBox() - timePeriodPreset.setChecked(True) - self.calendar = QLiberationCalendar() - self.calendar.setSelectedDate(QDate()) - self.calendar.setDisabled(True) - - def onTimePeriodChanged(): - self.calendar.setSelectedDate( - list(TIME_PERIODS.values())[timePeriodSelect.currentIndex()] - ) - - timePeriodSelect.currentTextChanged.connect(onTimePeriodChanged) - - for r in TIME_PERIODS: - timePeriodSelect.addItem(r) - timePeriod.setBuddy(timePeriodSelect) - timePeriodSelect.setCurrentIndex(21) - - def onTimePeriodCheckboxChanged(): - if timePeriodPreset.isChecked(): - self.calendar.setDisabled(True) - timePeriodSelect.setDisabled(False) - onTimePeriodChanged() - else: - self.calendar.setDisabled(False) - timePeriodSelect.setDisabled(True) - - timePeriodPreset.stateChanged.connect(onTimePeriodCheckboxChanged) - - # Bind selection method for campaign selection - def on_campaign_selected(): - template = jinja_env.get_template("campaigntemplate_EN.j2") - template_perf = jinja_env.get_template( - "campaign_performance_template_EN.j2" - ) - campaign = self.campaignList.selected_campaign - self.setField("selectedCampaign", campaign) - if campaign is None: - self.campaignMapDescription.setText("No campaign selected") - self.performanceText.setText("No campaign selected") - return - - self.campaignMapDescription.setText(template.render({"campaign": campaign})) - self.faction_selection.setDefaultFactions(campaign) - self.performanceText.setText( - template_perf.render({"performance": campaign.performance}) - ) - - if (start_date := campaign.recommended_start_date) is not None: - self.calendar.setSelectedDate( - QDate(start_date.year, start_date.month, start_date.day) - ) - timePeriodPreset.setChecked(False) - else: - timePeriodPreset.setChecked(True) - self.advanced_iads.setEnabled(campaign.advanced_iads) - self.iads_label.setEnabled(campaign.advanced_iads) - self.advanced_iads.setChecked(campaign.advanced_iads) - if not campaign.advanced_iads: - self.advanced_iads.setToolTip( - "Advanced IADS is not supported by this campaign" - ) - else: - self.advanced_iads.setToolTip("Enable Advanced IADS") - - self.campaign_selected.emit(campaign) - - self.campaignList.selectionModel().setCurrentIndex( - self.campaignList.indexAt(QPoint(1, 1)), QItemSelectionModel.Rows - ) - - self.campaignList.selectionModel().selectionChanged.connect( - on_campaign_selected - ) - on_campaign_selected() - - docsText = QtWidgets.QLabel( - "

Want more campaigns? You can " - 'offer to help, ' - 'play a community campaign, ' - 'or create your own.' - "

" - ) - docsText.setAlignment(Qt.AlignCenter) - docsText.setOpenExternalLinks(True) - - # Register fields - self.registerField("timePeriod", timePeriodSelect) - self.registerField("usePreset", timePeriodPreset) - - timeGroupLayout = QtWidgets.QGridLayout() - timeGroupLayout.addWidget(timePeriodPresetLabel, 0, 0) - timeGroupLayout.addWidget(timePeriodPreset, 0, 1) - timeGroupLayout.addWidget(timePeriod, 1, 0) - timeGroupLayout.addWidget(timePeriodSelect, 1, 1) - timeGroupLayout.addWidget(self.calendar, 0, 2, 3, 1) - timeGroup.setLayout(timeGroupLayout) - - layout = QtWidgets.QGridLayout() - layout.setColumnMinimumWidth(0, 20) - layout.addWidget(self.campaignList, 0, 0, 5, 1) - layout.addWidget(show_incompatible_campaigns_checkbox, 5, 0, 1, 1) - layout.addWidget(docsText, 6, 0, 1, 1) - layout.addWidget(self.campaignMapDescription, 0, 1, 1, 1) - layout.addWidget(self.performanceText, 1, 1, 1, 1) - layout.addWidget(mapSettingsGroup, 2, 1, 1, 1) - layout.addWidget(timeGroup, 3, 1, 3, 1) - self.setLayout(layout) - - -class BudgetInputs(QtWidgets.QGridLayout): - def __init__(self, label: str, value: int) -> None: - super().__init__() - self.addWidget(QtWidgets.QLabel(label), 0, 0) - - minimum = 0 - maximum = 5000 - - slider = QtWidgets.QSlider(Qt.Horizontal) - slider.setMinimum(minimum) - slider.setMaximum(maximum) - slider.setValue(value) - self.starting_money = CurrencySpinner(minimum, maximum, value) - slider.valueChanged.connect(lambda x: self.starting_money.setValue(x)) - self.starting_money.valueChanged.connect(lambda x: slider.setValue(x)) - - self.addWidget(slider, 1, 0) - self.addWidget(self.starting_money, 1, 1) - - -class DifficultyAndAutomationOptions(QtWidgets.QWizardPage): - def __init__(self, parent=None) -> None: - super().__init__(parent) - - self.setTitle("Difficulty and automation options") - self.setSubTitle( - "\nOptions controlling game difficulty and level of " "player involvement." - ) - self.setPixmap( - QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap("./resources/ui/wizard/logo1.png"), - ) - - layout = QtWidgets.QVBoxLayout() - - economy_group = QtWidgets.QGroupBox("Economy options") - layout.addWidget(economy_group) - economy_layout = QtWidgets.QVBoxLayout() - economy_group.setLayout(economy_layout) - - economy_layout.addWidget(QLabel("Player income multiplier")) - self.player_income = FloatSpinSlider(0, 5, 1, divisor=10) - self.registerField("player_income_multiplier", self.player_income.spinner) - economy_layout.addLayout(self.player_income) - - economy_layout.addWidget(QLabel("Enemy income multiplier")) - self.enemy_income = FloatSpinSlider(0, 5, 1, divisor=10) - self.registerField("enemy_income_multiplier", self.enemy_income.spinner) - economy_layout.addLayout(self.enemy_income) - - self.player_budget = BudgetInputs("Player starting budget", DEFAULT_BUDGET) - self.registerField("starting_money", self.player_budget.starting_money) - economy_layout.addLayout(self.player_budget) - - self.enemy_budget = BudgetInputs("Enemy starting budget", DEFAULT_BUDGET) - self.registerField("enemy_starting_money", self.enemy_budget.starting_money) - economy_layout.addLayout(self.enemy_budget) - - new_squadron_rules = QtWidgets.QCheckBox("Enable new squadron rules") - self.registerField("use_new_squadron_rules", new_squadron_rules) - economy_layout.addWidget(new_squadron_rules) - economy_layout.addWidget( - QLabel( - "With new squadron rules enabled, squadrons will not be able to exceed a maximum number of aircraft " - "(configurable), and the campaign will begin with all squadrons at full strength." - ) - ) - - assist_group = QtWidgets.QGroupBox("Player assists") - layout.addWidget(assist_group) - assist_layout = QtWidgets.QGridLayout() - assist_group.setLayout(assist_layout) - - assist_layout.addWidget(QtWidgets.QLabel("Automate runway repairs"), 0, 0) - self.runway_repairs = QtWidgets.QCheckBox() - self.registerField(RUNWAY_REPAIR, self.runway_repairs) - assist_layout.addWidget(self.runway_repairs, 0, 1, Qt.AlignRight) - - assist_layout.addWidget(QtWidgets.QLabel("Automate front-line purchases"), 1, 0) - self.front_line = QtWidgets.QCheckBox() - self.registerField(FRONTLINE, self.front_line) - assist_layout.addWidget(self.front_line, 1, 1, Qt.AlignRight) - - assist_layout.addWidget(QtWidgets.QLabel("Automate aircraft purchases"), 2, 0) - self.aircraft = QtWidgets.QCheckBox() - self.registerField(AIRCRAFT, self.aircraft) - assist_layout.addWidget(self.aircraft, 2, 1, Qt.AlignRight) - - self.setLayout(layout) - - def set_campaign_values(self, campaign: Campaign) -> None: - self.player_budget.starting_money.setValue(campaign.recommended_player_money) - self.enemy_budget.starting_money.setValue(campaign.recommended_enemy_money) - self.player_income.spinner.setValue( - int(campaign.recommended_player_income_multiplier * 10) - ) - self.enemy_income.spinner.setValue( - int(campaign.recommended_enemy_income_multiplier * 10) - ) - s = campaign.settings - self.runway_repairs.setChecked(s.get(RUNWAY_REPAIR, False)) - self.front_line.setChecked(s.get(FRONTLINE, False)) - self.aircraft.setChecked(s.get(AIRCRAFT, False)) - - -class GeneratorOptions(QtWidgets.QWizardPage): - def __init__(self, campaign: Campaign, parent=None): - super().__init__(parent) - - self.setTitle("Generator settings") - self.setSubTitle("\nOptions affecting the generation of the game.") - self.setPixmap( - QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap("./resources/ui/wizard/logo1.png"), - ) - - # Campaign settings - generatorSettingsGroup = QtWidgets.QGroupBox("Generator Settings") - self.no_carrier = QtWidgets.QCheckBox() - self.registerField("no_carrier", self.no_carrier) - self.no_lha = QtWidgets.QCheckBox() - self.registerField("no_lha", self.no_lha) - self.supercarrier = QtWidgets.QCheckBox() - self.registerField(SUPER_CARRIER, self.supercarrier) - self.no_player_navy = QtWidgets.QCheckBox() - self.registerField("no_player_navy", self.no_player_navy) - self.no_enemy_navy = QtWidgets.QCheckBox() - self.registerField("no_enemy_navy", self.no_enemy_navy) - self.desired_player_mission_duration = TimeInputs( - DEFAULT_MISSION_LENGTH, minimum=30, maximum=150 - ) - self.registerField(MISSION_LENGTH, self.desired_player_mission_duration.spinner) - - generatorLayout = QtWidgets.QGridLayout() - generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0) - generatorLayout.addWidget(self.no_carrier, 1, 1) - generatorLayout.addWidget(QtWidgets.QLabel("No LHA"), 2, 0) - generatorLayout.addWidget(self.no_lha, 2, 1) - generatorLayout.addWidget(QtWidgets.QLabel("Use Supercarrier module"), 3, 0) - generatorLayout.addWidget(self.supercarrier, 3, 1) - generatorLayout.addWidget(QtWidgets.QLabel("No Player Navy"), 4, 0) - generatorLayout.addWidget(self.no_player_navy, 4, 1) - generatorLayout.addWidget(QtWidgets.QLabel("No Enemy Navy"), 5, 0) - generatorLayout.addWidget(self.no_enemy_navy, 5, 1) - generatorLayout.addWidget(QtWidgets.QLabel("Desired mission duration"), 6, 0) - generatorLayout.addLayout(self.desired_player_mission_duration, 7, 0) - generatorSettingsGroup.setLayout(generatorLayout) - - modSettingsGroup = QtWidgets.QGroupBox("Mod Settings") - self.a4_skyhawk = QtWidgets.QCheckBox() - self.registerField("a4_skyhawk", self.a4_skyhawk) - self.a6a_intruder = QtWidgets.QCheckBox() - self.registerField("a6a_intruder", self.a6a_intruder) - self.a7e_corsair2 = QtWidgets.QCheckBox() - self.registerField("a7e_corsair2", self.a7e_corsair2) - self.hercules = QtWidgets.QCheckBox() - self.registerField("hercules", self.hercules) - self.uh_60l = QtWidgets.QCheckBox() - self.registerField("uh_60l", self.uh_60l) - self.f4bc_phantom = QtWidgets.QCheckBox() - self.registerField("f4bc_phantom", self.f4bc_phantom) - self.f15d_baz = QtWidgets.QCheckBox() - self.registerField("f15d_baz", self.f15d_baz) - self.f_16_idf = QtWidgets.QCheckBox() - self.registerField("f_16_idf", self.f_16_idf) - self.fa_18efg = QtWidgets.QCheckBox() - self.registerField("fa_18efg", self.fa_18efg) - self.f22_raptor = QtWidgets.QCheckBox() - self.registerField("f22_raptor", self.f22_raptor) - self.f84g_thunderjet = QtWidgets.QCheckBox() - self.registerField("f84g_thunderjet", self.f84g_thunderjet) - self.f100_supersabre = QtWidgets.QCheckBox() - self.registerField("f100_supersabre", self.f100_supersabre) - self.f104_starfighter = QtWidgets.QCheckBox() - self.registerField("f104_starfighter", self.f104_starfighter) - self.f105_thunderchief = QtWidgets.QCheckBox() - self.registerField("f105_thunderchief", self.f105_thunderchief) - self.jas39_gripen = QtWidgets.QCheckBox() - self.registerField("jas39_gripen", self.jas39_gripen) - self.su30_flanker_h = QtWidgets.QCheckBox() - self.registerField("su30_flanker_h", self.su30_flanker_h) - self.su57_felon = QtWidgets.QCheckBox() - self.registerField("su57_felon", self.su57_felon) - self.ov10a_bronco = QtWidgets.QCheckBox() - self.registerField("ov10a_bronco", self.ov10a_bronco) - self.frenchpack = QtWidgets.QCheckBox() - self.registerField("frenchpack", self.frenchpack) - self.high_digit_sams = QtWidgets.QCheckBox() - self.registerField("high_digit_sams", self.high_digit_sams) - self.swedishmilitaryassetspack = QtWidgets.QCheckBox() - self.registerField("swedishmilitaryassetspack", self.swedishmilitaryassetspack) - self.SWPack = QtWidgets.QCheckBox() - self.registerField("SWPack", self.SWPack) - self.spanishnavypack = QtWidgets.QCheckBox() - self.registerField("spanishnavypack", self.spanishnavypack) - self.irondome = QtWidgets.QCheckBox() - self.registerField("irondome", self.irondome) - - modHelpText = QtWidgets.QLabel( - "

Select the mods you have installed. If your chosen factions support them, you'll be able to use these mods in your campaign.

" - ) - modHelpText.setAlignment(Qt.AlignCenter) - - modLayout = QtWidgets.QGridLayout() - modLayout_row = 1 - - mod_pairs = [ - ("A-4E Skyhawk (v2.1.0)", self.a4_skyhawk), - ("A-6A Intruder (v2.7.5.01)", self.a6a_intruder), - ("A-7E Corsair II", self.a7e_corsair2), - ("C-130J-30 Super Hercules", self.hercules), - ( - "F-4B/C Phantom II (v2.8.1.01 Standalone + 29Jan23 Patch)", - self.f4bc_phantom, - ), - ("F-15D Baz (v1.0)", self.f15d_baz), - ("F-16I Sufa & F-16D (v3.6 by IDF Mods Project)", self.f_16_idf), - ("F/A-18E/F/G Super Hornet (version 2.1)", self.fa_18efg), - ("F-22A Raptor", self.f22_raptor), - ("F-84G Thunderjet (v2.5.7.01)", self.f84g_thunderjet), - ("F-100 Super Sabre (v2.7.18.30765 patch 20.10.22)", self.f100_supersabre), - ("F-104 Starfighter (v2.7.11.222.01)", self.f104_starfighter), - ("F-105 Thunderchief (v2.7.12.23x)", self.f105_thunderchief), - ("Frenchpack", self.frenchpack), - ("High Digit SAMs", self.high_digit_sams), - ("Swedish Military Assets pack (1.10)", self.swedishmilitaryassetspack), - ("JAS 39 Gripen (v1.8.5-beta)", self.jas39_gripen), - ("OV-10A Bronco", self.ov10a_bronco), - ("Su-30 Flanker-H (V2.01B)", self.su30_flanker_h), - ("Su-57 Felon", self.su57_felon), - ("UH-60L Black Hawk (v1.3.1)", self.uh_60l), - ("Star Wars Modpack 2.54+", self.SWPack), - ("Spanish Naval Assets pack (desdemicabina 3.2.0)", self.spanishnavypack), - ("Iron Dome (v1.2 by IDF Mods Project)", self.irondome), - ] - - for i in range(len(mod_pairs)): - if i % 15 == 0: - modLayout_row = 1 - col = 2 * (i // 15) - if i % 5 == 0: - # Section break here for readability - modLayout.addWidget(QtWidgets.QWidget(), modLayout_row, col) - modLayout_row += 1 - label, cb = mod_pairs[i] - modLayout.addWidget(QLabel(label), modLayout_row, col) - modLayout.addWidget(cb, modLayout_row, col + 1) - modLayout_row += 1 - - modSettingsGroup.setLayout(modLayout) - - mlayout = QVBoxLayout() - mlayout.addWidget(generatorSettingsGroup) - mlayout.addWidget(modSettingsGroup) - mlayout.addWidget(modHelpText) - self.setLayout(mlayout) - self.update_settings(campaign) - - def update_settings(self, campaign: Campaign) -> None: - s = campaign.settings - - self.no_carrier.setChecked(s.get("no_carrier", False)) - self.no_lha.setChecked(s.get("no_lha", False)) - self.supercarrier.setChecked(s.get(SUPER_CARRIER, False)) - self.no_player_navy.setChecked(s.get("no_player_navy", False)) - self.no_enemy_navy.setChecked(s.get("no_enemy_navy", False)) - self.desired_player_mission_duration.spinner.setValue(s.get(MISSION_LENGTH, 60)) - - self.a4_skyhawk.setChecked(s.get("a4_skyhawk", False)) - self.a6a_intruder.setChecked(s.get("a6a_intruder", False)) - self.a7e_corsair2.setChecked(s.get("a7e_corsair2", False)) - self.hercules.setChecked(s.get("hercules", False)) - self.uh_60l.setChecked(s.get("uh_60l", False)) - self.f4bc_phantom.setChecked(s.get("f4bc_phantom", False)) - self.f15d_baz.setChecked(s.get("f15d_baz", False)) - self.f_16_idf.setChecked(s.get("f_16_idf", False)) - self.fa_18efg.setChecked(s.get("fa_18efg", False)) - self.f22_raptor.setChecked(s.get("f22_raptor", False)) - self.f84g_thunderjet.setChecked(s.get("f84g_thunderjet", False)) - self.f100_supersabre.setChecked(s.get("f100_supersabre", False)) - self.f104_starfighter.setChecked(s.get("f104_starfighter", False)) - self.f105_thunderchief.setChecked(s.get("f105_thunderchief", False)) - self.jas39_gripen.setChecked(s.get("jas39_gripen", False)) - self.su30_flanker_h.setChecked(s.get("su30_flanker_h", False)) - self.su57_felon.setChecked(s.get("su57_felon", False)) - self.ov10a_bronco.setChecked(s.get("ov10a_bronco", False)) - self.frenchpack.setChecked(s.get("frenchpack", False)) - self.high_digit_sams.setChecked(s.get("high_digit_sams", False)) - self.spanishnavypack.setChecked(s.get("spanishnavypack", False)) - self.irondome.setChecked(s.get("irondome", False)) - self.swedishmilitaryassetspack.setChecked( - s.get("swedishmilitaryassetspack", False) - ) - - class ConclusionPage(QtWidgets.QWizardPage): def __init__(self, parent=None): super(ConclusionPage, self).__init__(parent) diff --git a/qt_ui/windows/newgame/SettingNames.py b/qt_ui/windows/newgame/SettingNames.py new file mode 100644 index 00000000..b2e5bd39 --- /dev/null +++ b/qt_ui/windows/newgame/SettingNames.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals + +from game.settings import Settings + +# fmt: off +RUNWAY_REPAIR = f"{Settings.automate_runway_repair=}".split("=")[0].split(".")[1] +FRONTLINE = f"{Settings.automate_front_line_reinforcements=}".split("=")[0].split(".")[1] +AIRCRAFT = f"{Settings.automate_aircraft_reinforcements=}".split("=")[0].split(".")[1] +MISSION_LENGTH = f"{Settings.desired_player_mission_duration=}".split("=")[0].split(".")[1] +SUPER_CARRIER = f"{Settings.supercarrier=}".split("=")[0].split(".")[1] +SQN_AC_LIMITS = f"{Settings.enable_squadron_aircraft_limits=}".split("=")[0].split(".")[1] +# fmt: on diff --git a/qt_ui/windows/newgame/WizardPages/QFactionSelection.py b/qt_ui/windows/newgame/WizardPages/QFactionSelection.py new file mode 100644 index 00000000..caf46e0b --- /dev/null +++ b/qt_ui/windows/newgame/WizardPages/QFactionSelection.py @@ -0,0 +1,248 @@ +from __future__ import unicode_literals + +from copy import deepcopy + +from PySide2 import QtWidgets, QtGui +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QScrollArea, QWidget, QGridLayout, QCheckBox, QLabel, QTextBrowser + +from game.campaignloader import Campaign +from game.factions import Faction, FACTIONS +from qt_ui.windows.newgame.jinja_env import jinja_env + + +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) + + self.setTitle("Faction selection") + self.setSubTitle( + "\nChoose the two opposing factions and select the player side." + ) + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/misc/generator.png"), + ) + + self.setMinimumHeight(250) + + # Factions selection + self.factionsGroup = QtWidgets.QGroupBox("Factions") + self.factionsGroupLayout = QtWidgets.QHBoxLayout() + self.blueGroupLayout = QtWidgets.QGridLayout() + self.redGroupLayout = QtWidgets.QGridLayout() + + blueFaction = QtWidgets.QLabel("Player Faction :") + self.blueFactionSelect = QtWidgets.QComboBox() + blueFaction.setBuddy(self.blueFactionSelect) + + redFaction = QtWidgets.QLabel("Enemy Faction :") + self.redFactionSelect = QtWidgets.QComboBox() + redFaction.setBuddy(self.redFactionSelect) + + # Faction description + 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.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) + self.factionsGroup.setLayout(self.factionsGroupLayout) + + # Docs Link + docsText = QtWidgets.QLabel( + 'How to create your own faction' + ) + docsText.setAlignment(Qt.AlignCenter) + docsText.setOpenExternalLinks(True) + + # Link form fields + self.registerField("blueFaction", self.blueFactionSelect) + self.registerField("redFaction", self.redFactionSelect) + + # Build layout + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.factionsGroup) + layout.addWidget(docsText) + self.setLayout(layout) + self.updateUnitRecap() + + self.blueFactionSelect.activated.connect(self.updateUnitRecap) + self.redFactionSelect.activated.connect(self.updateUnitRecap) + + def setDefaultFactions(self, campaign: Campaign): + """Set default faction for selected campaign""" + + self.blueFactionSelect.clear() + self.redFactionSelect.clear() + + for f in FACTIONS: + self.blueFactionSelect.addItem(f) + + for i, r in enumerate(FACTIONS): + self.redFactionSelect.addItem(r) + if r == campaign.recommended_enemy_faction: + self.redFactionSelect.setCurrentIndex(i) + if r == campaign.recommended_player_faction: + self.blueFactionSelect.setCurrentIndex(i) + + self.updateUnitRecap() + + def updateUnitRecap(self): + red_faction = FACTIONS[self.redFactionSelect.currentText()] + blue_faction = FACTIONS[self.blueFactionSelect.currentText()] + + template = jinja_env.get_template("factiontemplate_EN.j2") + + blue_faction_txt = template.render({"faction": blue_faction}) + red_faction_txt = template.render({"faction": red_faction}) + + 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: + fac = deepcopy(qfu.faction) + qfu.updateFactionUnits(fac.aircrafts) + qfu.updateFactionUnits(fac.awacs) + qfu.updateFactionUnits(fac.tankers) + qfu.updateFactionUnits(fac.frontline_units) + qfu.updateFactionUnits(fac.artillery_units) + qfu.updateFactionUnits(fac.logistics_units) + qfu.updateFactionUnits(fac.infantry_units) + qfu.updateFactionUnits(fac.preset_groups) + qfu.updateFactionUnits(fac.air_defense_units) + qfu.updateFactionUnits(fac.naval_units) + qfu.updateFactionUnits(fac.missiles) + return fac + + @property + def selected_blue_faction(self) -> Faction: + return self._filter_selected_units(self.blueFactionUnits) + + @property + def selected_red_faction(self) -> Faction: + return self._filter_selected_units(self.redFactionUnits) diff --git a/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py b/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py new file mode 100644 index 00000000..c8c1cc95 --- /dev/null +++ b/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py @@ -0,0 +1,236 @@ +from __future__ import unicode_literals + +from datetime import timedelta + +from PySide2 import QtWidgets, QtGui +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QLabel, QVBoxLayout + +from game.campaignloader import Campaign +from game.campaignloader.campaign import DEFAULT_BUDGET +from qt_ui.widgets.spinsliders import CurrencySpinner, TimeInputs +from qt_ui.windows.newgame.SettingNames import MISSION_LENGTH, SUPER_CARRIER + +DEFAULT_MISSION_LENGTH: timedelta = timedelta(minutes=60) + + +class BudgetInputs(QtWidgets.QGridLayout): + def __init__(self, label: str, value: int) -> None: + super().__init__() + self.addWidget(QtWidgets.QLabel(label), 0, 0) + + minimum = 0 + maximum = 5000 + + slider = QtWidgets.QSlider(Qt.Horizontal) + slider.setMinimum(minimum) + slider.setMaximum(maximum) + slider.setValue(value) + self.starting_money = CurrencySpinner(minimum, maximum, value) + slider.valueChanged.connect(lambda x: self.starting_money.setValue(x)) + self.starting_money.valueChanged.connect(lambda x: slider.setValue(x)) + + self.addWidget(slider, 1, 0) + self.addWidget(self.starting_money, 1, 1) + + +class GeneratorOptions(QtWidgets.QWizardPage): + def __init__(self, campaign: Campaign, parent=None): + super().__init__(parent) + + self.setTitle("Generator settings") + self.setSubTitle("\nOptions affecting the generation of the game.") + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/wizard/logo1.png"), + ) + + # Campaign settings + generatorSettingsGroup = QtWidgets.QGroupBox("Generator Settings") + self.no_carrier = QtWidgets.QCheckBox() + self.registerField("no_carrier", self.no_carrier) + self.no_lha = QtWidgets.QCheckBox() + self.registerField("no_lha", self.no_lha) + self.supercarrier = QtWidgets.QCheckBox() + self.registerField(SUPER_CARRIER, self.supercarrier) + self.no_player_navy = QtWidgets.QCheckBox() + self.registerField("no_player_navy", self.no_player_navy) + self.no_enemy_navy = QtWidgets.QCheckBox() + self.registerField("no_enemy_navy", self.no_enemy_navy) + self.desired_player_mission_duration = TimeInputs( + DEFAULT_MISSION_LENGTH, minimum=30, maximum=150 + ) + self.registerField(MISSION_LENGTH, self.desired_player_mission_duration.spinner) + + generatorLayout = QtWidgets.QGridLayout() + generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0) + generatorLayout.addWidget(self.no_carrier, 1, 1) + generatorLayout.addWidget(QtWidgets.QLabel("No LHA"), 2, 0) + generatorLayout.addWidget(self.no_lha, 2, 1) + generatorLayout.addWidget(QtWidgets.QLabel("Use Supercarrier module"), 3, 0) + generatorLayout.addWidget(self.supercarrier, 3, 1) + generatorLayout.addWidget(QtWidgets.QLabel("No Player Navy"), 4, 0) + generatorLayout.addWidget(self.no_player_navy, 4, 1) + generatorLayout.addWidget(QtWidgets.QLabel("No Enemy Navy"), 5, 0) + generatorLayout.addWidget(self.no_enemy_navy, 5, 1) + # generatorLayout.addWidget(QtWidgets.QLabel("Desired mission duration"), 6, 0) + # generatorLayout.addLayout(self.desired_player_mission_duration, 7, 0) + + self.player_budget = BudgetInputs("Player starting budget", DEFAULT_BUDGET) + self.registerField("starting_money", self.player_budget.starting_money) + generatorLayout.addLayout(self.player_budget, 8, 0) + + self.enemy_budget = BudgetInputs("Enemy starting budget", DEFAULT_BUDGET) + self.registerField("enemy_starting_money", self.enemy_budget.starting_money) + generatorLayout.addLayout(self.enemy_budget, 9, 0) + + generatorSettingsGroup.setLayout(generatorLayout) + + modSettingsGroup = QtWidgets.QGroupBox("Mod Settings") + self.a4_skyhawk = QtWidgets.QCheckBox() + self.registerField("a4_skyhawk", self.a4_skyhawk) + self.a6a_intruder = QtWidgets.QCheckBox() + self.registerField("a6a_intruder", self.a6a_intruder) + self.a7e_corsair2 = QtWidgets.QCheckBox() + self.registerField("a7e_corsair2", self.a7e_corsair2) + self.hercules = QtWidgets.QCheckBox() + self.registerField("hercules", self.hercules) + self.uh_60l = QtWidgets.QCheckBox() + self.registerField("uh_60l", self.uh_60l) + self.f4bc_phantom = QtWidgets.QCheckBox() + self.registerField("f4bc_phantom", self.f4bc_phantom) + self.f15d_baz = QtWidgets.QCheckBox() + self.registerField("f15d_baz", self.f15d_baz) + self.f_16_idf = QtWidgets.QCheckBox() + self.registerField("f_16_idf", self.f_16_idf) + self.fa_18efg = QtWidgets.QCheckBox() + self.registerField("fa_18efg", self.fa_18efg) + self.f22_raptor = QtWidgets.QCheckBox() + self.registerField("f22_raptor", self.f22_raptor) + self.f84g_thunderjet = QtWidgets.QCheckBox() + self.registerField("f84g_thunderjet", self.f84g_thunderjet) + self.f100_supersabre = QtWidgets.QCheckBox() + self.registerField("f100_supersabre", self.f100_supersabre) + self.f104_starfighter = QtWidgets.QCheckBox() + self.registerField("f104_starfighter", self.f104_starfighter) + self.f105_thunderchief = QtWidgets.QCheckBox() + self.registerField("f105_thunderchief", self.f105_thunderchief) + self.jas39_gripen = QtWidgets.QCheckBox() + self.registerField("jas39_gripen", self.jas39_gripen) + self.su30_flanker_h = QtWidgets.QCheckBox() + self.registerField("su30_flanker_h", self.su30_flanker_h) + self.su57_felon = QtWidgets.QCheckBox() + self.registerField("su57_felon", self.su57_felon) + self.ov10a_bronco = QtWidgets.QCheckBox() + self.registerField("ov10a_bronco", self.ov10a_bronco) + self.frenchpack = QtWidgets.QCheckBox() + self.registerField("frenchpack", self.frenchpack) + self.high_digit_sams = QtWidgets.QCheckBox() + self.registerField("high_digit_sams", self.high_digit_sams) + self.swedishmilitaryassetspack = QtWidgets.QCheckBox() + self.registerField("swedishmilitaryassetspack", self.swedishmilitaryassetspack) + self.SWPack = QtWidgets.QCheckBox() + self.registerField("SWPack", self.SWPack) + self.spanishnavypack = QtWidgets.QCheckBox() + self.registerField("spanishnavypack", self.spanishnavypack) + self.irondome = QtWidgets.QCheckBox() + self.registerField("irondome", self.irondome) + + modHelpText = QtWidgets.QLabel( + "

Select the mods you have installed. If your chosen factions support them, you'll be able to use these mods in your campaign.

" + ) + modHelpText.setAlignment(Qt.AlignCenter) + + modLayout = QtWidgets.QGridLayout() + modLayout_row = 1 + + mod_pairs = [ + ("A-4E Skyhawk (v2.1.0)", self.a4_skyhawk), + ("A-6A Intruder (v2.7.5.01)", self.a6a_intruder), + ("A-7E Corsair II", self.a7e_corsair2), + ("C-130J-30 Super Hercules", self.hercules), + ( + "F-4B/C Phantom II (v2.8.1.01 Standalone + 29Jan23 Patch)", + self.f4bc_phantom, + ), + ("F-15D Baz (v1.0)", self.f15d_baz), + ("F-16I Sufa & F-16D (v3.6 by IDF Mods Project)", self.f_16_idf), + ("F/A-18E/F/G Super Hornet (version 2.1)", self.fa_18efg), + ("F-22A Raptor", self.f22_raptor), + ("F-84G Thunderjet (v2.5.7.01)", self.f84g_thunderjet), + ("F-100 Super Sabre (v2.7.18.30765 patch 20.10.22)", self.f100_supersabre), + ("F-104 Starfighter (v2.7.11.222.01)", self.f104_starfighter), + ("F-105 Thunderchief (v2.7.12.23x)", self.f105_thunderchief), + ("Frenchpack", self.frenchpack), + ("High Digit SAMs", self.high_digit_sams), + ("Swedish Military Assets pack (1.10)", self.swedishmilitaryassetspack), + ("JAS 39 Gripen (v1.8.5-beta)", self.jas39_gripen), + ("OV-10A Bronco", self.ov10a_bronco), + ("Su-30 Flanker-H (V2.01B)", self.su30_flanker_h), + ("Su-57 Felon", self.su57_felon), + ("UH-60L Black Hawk (v1.3.1)", self.uh_60l), + ("Star Wars Modpack 2.54+", self.SWPack), + ("Spanish Naval Assets pack (desdemicabina 3.2.0)", self.spanishnavypack), + ("Iron Dome (v1.2 by IDF Mods Project)", self.irondome), + ] + + for i in range(len(mod_pairs)): + if i % 15 == 0: + modLayout_row = 1 + col = 2 * (i // 15) + if i % 5 == 0: + # Section break here for readability + modLayout.addWidget(QtWidgets.QWidget(), modLayout_row, col) + modLayout_row += 1 + label, cb = mod_pairs[i] + modLayout.addWidget(QLabel(label), modLayout_row, col) + modLayout.addWidget(cb, modLayout_row, col + 1) + modLayout_row += 1 + + modSettingsGroup.setLayout(modLayout) + + mlayout = QVBoxLayout() + mlayout.addWidget(generatorSettingsGroup) + mlayout.addWidget(modSettingsGroup) + mlayout.addWidget(modHelpText) + self.setLayout(mlayout) + self.update_settings(campaign) + + def update_settings(self, campaign: Campaign) -> None: + s = campaign.settings + + self.player_budget.starting_money.setValue(campaign.recommended_player_money) + self.enemy_budget.starting_money.setValue(campaign.recommended_enemy_money) + + self.no_carrier.setChecked(s.get("no_carrier", False)) + self.no_lha.setChecked(s.get("no_lha", False)) + self.supercarrier.setChecked(s.get(SUPER_CARRIER, False)) + self.no_player_navy.setChecked(s.get("no_player_navy", False)) + self.no_enemy_navy.setChecked(s.get("no_enemy_navy", False)) + # self.desired_player_mission_duration.spinner.setValue(s.get(MISSION_LENGTH, 60)) + + self.a4_skyhawk.setChecked(s.get("a4_skyhawk", False)) + self.a6a_intruder.setChecked(s.get("a6a_intruder", False)) + self.a7e_corsair2.setChecked(s.get("a7e_corsair2", False)) + self.hercules.setChecked(s.get("hercules", False)) + self.uh_60l.setChecked(s.get("uh_60l", False)) + self.f4bc_phantom.setChecked(s.get("f4bc_phantom", False)) + self.f15d_baz.setChecked(s.get("f15d_baz", False)) + self.f_16_idf.setChecked(s.get("f_16_idf", False)) + self.fa_18efg.setChecked(s.get("fa_18efg", False)) + self.f22_raptor.setChecked(s.get("f22_raptor", False)) + self.f84g_thunderjet.setChecked(s.get("f84g_thunderjet", False)) + self.f100_supersabre.setChecked(s.get("f100_supersabre", False)) + self.f104_starfighter.setChecked(s.get("f104_starfighter", False)) + self.f105_thunderchief.setChecked(s.get("f105_thunderchief", False)) + self.jas39_gripen.setChecked(s.get("jas39_gripen", False)) + self.su30_flanker_h.setChecked(s.get("su30_flanker_h", False)) + self.su57_felon.setChecked(s.get("su57_felon", False)) + self.ov10a_bronco.setChecked(s.get("ov10a_bronco", False)) + self.frenchpack.setChecked(s.get("frenchpack", False)) + self.high_digit_sams.setChecked(s.get("high_digit_sams", False)) + self.spanishnavypack.setChecked(s.get("spanishnavypack", False)) + self.irondome.setChecked(s.get("irondome", False)) + self.swedishmilitaryassetspack.setChecked( + s.get("swedishmilitaryassetspack", False) + ) diff --git a/qt_ui/windows/newgame/WizardPages/QNewGameSettings.py b/qt_ui/windows/newgame/WizardPages/QNewGameSettings.py new file mode 100644 index 00000000..7887c836 --- /dev/null +++ b/qt_ui/windows/newgame/WizardPages/QNewGameSettings.py @@ -0,0 +1,84 @@ +from __future__ import unicode_literals + +from PySide2 import QtWidgets, QtGui +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QLabel + +from game.campaignloader import Campaign +from qt_ui.widgets.spinsliders import FloatSpinSlider +from qt_ui.windows.newgame.SettingNames import RUNWAY_REPAIR, FRONTLINE, AIRCRAFT + + +class NewGameSettings(QtWidgets.QWizardPage): + def __init__(self, parent=None) -> None: + super().__init__(parent) + + self.setTitle("Campaign options") + self.setSubTitle( + "\nAll other options unrelated to campaign generation." + ) + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/wizard/logo1.png"), + ) + + layout = QtWidgets.QVBoxLayout() + + economy_group = QtWidgets.QGroupBox("Economy options") + layout.addWidget(economy_group) + economy_layout = QtWidgets.QVBoxLayout() + economy_group.setLayout(economy_layout) + + economy_layout.addWidget(QLabel("Player income multiplier")) + self.player_income = FloatSpinSlider(0, 5, 1, divisor=10) + self.registerField("player_income_multiplier", self.player_income.spinner) + economy_layout.addLayout(self.player_income) + + economy_layout.addWidget(QLabel("Enemy income multiplier")) + self.enemy_income = FloatSpinSlider(0, 5, 1, divisor=10) + self.registerField("enemy_income_multiplier", self.enemy_income.spinner) + economy_layout.addLayout(self.enemy_income) + + new_squadron_rules = QtWidgets.QCheckBox("Enable new squadron rules") + self.registerField("use_new_squadron_rules", new_squadron_rules) + economy_layout.addWidget(new_squadron_rules) + economy_layout.addWidget( + QLabel( + "With new squadron rules enabled, squadrons will not be able to exceed a maximum number of aircraft " + "(configurable), and the campaign will begin with all squadrons at full strength." + ) + ) + + assist_group = QtWidgets.QGroupBox("Player assists") + layout.addWidget(assist_group) + assist_layout = QtWidgets.QGridLayout() + assist_group.setLayout(assist_layout) + + assist_layout.addWidget(QtWidgets.QLabel("Automate runway repairs"), 0, 0) + self.runway_repairs = QtWidgets.QCheckBox() + self.registerField(RUNWAY_REPAIR, self.runway_repairs) + assist_layout.addWidget(self.runway_repairs, 0, 1, Qt.AlignRight) + + assist_layout.addWidget(QtWidgets.QLabel("Automate front-line purchases"), 1, 0) + self.front_line = QtWidgets.QCheckBox() + self.registerField(FRONTLINE, self.front_line) + assist_layout.addWidget(self.front_line, 1, 1, Qt.AlignRight) + + assist_layout.addWidget(QtWidgets.QLabel("Automate aircraft purchases"), 2, 0) + self.aircraft = QtWidgets.QCheckBox() + self.registerField(AIRCRAFT, self.aircraft) + assist_layout.addWidget(self.aircraft, 2, 1, Qt.AlignRight) + + self.setLayout(layout) + + def set_campaign_values(self, campaign: Campaign) -> None: + self.player_income.spinner.setValue( + int(campaign.recommended_player_income_multiplier * 10) + ) + self.enemy_income.spinner.setValue( + int(campaign.recommended_enemy_income_multiplier * 10) + ) + s = campaign.settings + self.runway_repairs.setChecked(s.get(RUNWAY_REPAIR, False)) + self.front_line.setChecked(s.get(FRONTLINE, False)) + self.aircraft.setChecked(s.get(AIRCRAFT, False)) diff --git a/qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py b/qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py new file mode 100644 index 00000000..c67bcfe4 --- /dev/null +++ b/qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py @@ -0,0 +1,293 @@ +from __future__ import unicode_literals, annotations + +from datetime import datetime +from typing import List, Optional + +from PySide2 import QtWidgets, QtGui +from PySide2.QtCore import Signal, QDate, QPoint, QItemSelectionModel, Qt, QModelIndex +from PySide2.QtGui import QStandardItem, QPixmap, QStandardItemModel +from PySide2.QtWidgets import QCheckBox, QTextBrowser, QTextEdit, QLabel, QListView, QAbstractItemView + +from game.campaignloader import Campaign +from qt_ui.liberation_install import get_dcs_install_directory +from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar +from qt_ui.windows.newgame.WizardPages.QFactionSelection import FactionSelection +from qt_ui.windows.newgame.jinja_env import jinja_env + +""" +Possible time periods for new games + + `Name`: daytime(day, month, year), + +`Identifier` is the name that will appear in the menu +The object is a python datetime object +""" +TIME_PERIODS = { + "WW2 - Winter [1944]": datetime(1944, 1, 1), + "WW2 - Spring [1944]": datetime(1944, 4, 1), + "WW2 - Summer [1944]": datetime(1944, 6, 1), + "WW2 - Fall [1944]": datetime(1944, 10, 1), + "Early Cold War - Winter [1952]": datetime(1952, 1, 1), + "Early Cold War - Spring [1952]": datetime(1952, 4, 1), + "Early Cold War - Summer [1952]": datetime(1952, 6, 1), + "Early Cold War - Fall [1952]": datetime(1952, 10, 1), + "Cold War - Winter [1970]": datetime(1970, 1, 1), + "Cold War - Spring [1970]": datetime(1970, 4, 1), + "Cold War - Summer [1970]": datetime(1970, 6, 1), + "Cold War - Fall [1970]": datetime(1970, 10, 1), + "Late Cold War - Winter [1985]": datetime(1985, 1, 1), + "Late Cold War - Spring [1985]": datetime(1985, 4, 1), + "Late Cold War - Summer [1985]": datetime(1985, 6, 1), + "Late Cold War - Fall [1985]": datetime(1985, 10, 1), + "Gulf War - Winter [1990]": datetime(1990, 1, 1), + "Gulf War - Spring [1990]": datetime(1990, 4, 1), + "Gulf War - Summer [1990]": datetime(1990, 6, 1), + "Mid-90s - Winter [1995]": datetime(1995, 1, 1), + "Mid-90s - Spring [1995]": datetime(1995, 4, 1), + "Mid-90s - Summer [1995]": datetime(1995, 6, 1), + "Mid-90s - Fall [1995]": datetime(1995, 10, 1), + "Gulf War - Fall [1990]": datetime(1990, 10, 1), + "Modern - Winter [2010]": datetime(2010, 1, 1), + "Modern - Spring [2010]": datetime(2010, 4, 1), + "Modern - Summer [2010]": datetime(2010, 6, 1), + "Modern - Fall [2010]": datetime(2010, 10, 1), + "Georgian War [2008]": datetime(2008, 8, 7), + "Syrian War [2011]": datetime(2011, 3, 15), + "6 days war [1967]": datetime(1967, 6, 5), + "Yom Kippour War [1973]": datetime(1973, 10, 6), + "First Lebanon War [1982]": datetime(1982, 6, 6), + "Arab-Israeli War [1948]": datetime(1948, 5, 15), +} + + +class TheaterConfiguration(QtWidgets.QWizardPage): + campaign_selected = Signal(Campaign) + + def __init__( + self, + campaigns: List[Campaign], + faction_selection: FactionSelection, + parent=None, + ) -> None: + super().__init__(parent) + + self.faction_selection = faction_selection + + self.setTitle("Theater configuration") + self.setSubTitle("\nChoose a terrain and time period for this game.") + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/wizard/logo1.png"), + ) + + self.setPixmap( + QtWidgets.QWizard.WatermarkPixmap, + QtGui.QPixmap("./resources/ui/wizard/watermark3.png"), + ) + + # List of campaigns + show_incompatible_campaigns_checkbox = QCheckBox( + text="Show incompatible campaigns" + ) + show_incompatible_campaigns_checkbox.setChecked(False) + self.campaignList = QCampaignList( + campaigns, show_incompatible_campaigns_checkbox.isChecked() + ) + show_incompatible_campaigns_checkbox.toggled.connect( + lambda checked: self.campaignList.setup_content(show_incompatible=checked) + ) + self.registerField("selectedCampaign", self.campaignList) + + # Faction description + self.campaignMapDescription = QTextBrowser() + self.campaignMapDescription.setReadOnly(True) + self.campaignMapDescription.setOpenExternalLinks(True) + self.campaignMapDescription.setMaximumHeight(200) + + self.performanceText = QTextEdit("") + self.performanceText.setReadOnly(True) + self.performanceText.setMaximumHeight(90) + + # Campaign settings + mapSettingsGroup = QtWidgets.QGroupBox("Map Settings") + mapSettingsLayout = QtWidgets.QGridLayout() + invertMap = QtWidgets.QCheckBox() + self.registerField("invertMap", invertMap) + mapSettingsLayout.addWidget(QtWidgets.QLabel("Invert Map"), 0, 0) + mapSettingsLayout.addWidget(invertMap, 0, 1) + self.advanced_iads = QtWidgets.QCheckBox() + self.registerField("advanced_iads", self.advanced_iads) + self.iads_label = QtWidgets.QLabel("Advanced IADS (WIP)") + mapSettingsLayout.addWidget(self.iads_label, 1, 0) + mapSettingsLayout.addWidget(self.advanced_iads, 1, 1) + mapSettingsGroup.setLayout(mapSettingsLayout) + + # Time Period + timeGroup = QtWidgets.QGroupBox("Time Period") + timePeriod = QtWidgets.QLabel("Start date :") + timePeriodSelect = QtWidgets.QComboBox() + timePeriodPresetLabel = QLabel("Use preset :") + timePeriodPreset = QtWidgets.QCheckBox() + timePeriodPreset.setChecked(True) + self.calendar = QLiberationCalendar() + self.calendar.setSelectedDate(QDate()) + self.calendar.setDisabled(True) + + def onTimePeriodChanged(): + self.calendar.setSelectedDate( + list(TIME_PERIODS.values())[timePeriodSelect.currentIndex()] + ) + + timePeriodSelect.currentTextChanged.connect(onTimePeriodChanged) + + for r in TIME_PERIODS: + timePeriodSelect.addItem(r) + timePeriod.setBuddy(timePeriodSelect) + timePeriodSelect.setCurrentIndex(21) + + def onTimePeriodCheckboxChanged(): + if timePeriodPreset.isChecked(): + self.calendar.setDisabled(True) + timePeriodSelect.setDisabled(False) + onTimePeriodChanged() + else: + self.calendar.setDisabled(False) + timePeriodSelect.setDisabled(True) + + timePeriodPreset.stateChanged.connect(onTimePeriodCheckboxChanged) + + # Bind selection method for campaign selection + def on_campaign_selected(): + template = jinja_env.get_template("campaigntemplate_EN.j2") + template_perf = jinja_env.get_template( + "campaign_performance_template_EN.j2" + ) + campaign = self.campaignList.selected_campaign + self.setField("selectedCampaign", campaign) + if campaign is None: + self.campaignMapDescription.setText("No campaign selected") + self.performanceText.setText("No campaign selected") + return + + self.campaignMapDescription.setText(template.render({"campaign": campaign})) + self.faction_selection.setDefaultFactions(campaign) + self.performanceText.setText( + template_perf.render({"performance": campaign.performance}) + ) + + if (start_date := campaign.recommended_start_date) is not None: + self.calendar.setSelectedDate( + QDate(start_date.year, start_date.month, start_date.day) + ) + timePeriodPreset.setChecked(False) + else: + timePeriodPreset.setChecked(True) + self.advanced_iads.setEnabled(campaign.advanced_iads) + self.iads_label.setEnabled(campaign.advanced_iads) + self.advanced_iads.setChecked(campaign.advanced_iads) + if not campaign.advanced_iads: + self.advanced_iads.setToolTip( + "Advanced IADS is not supported by this campaign" + ) + else: + self.advanced_iads.setToolTip("Enable Advanced IADS") + + self.campaign_selected.emit(campaign) + + self.campaignList.selectionModel().setCurrentIndex( + self.campaignList.indexAt(QPoint(1, 1)), QItemSelectionModel.Rows + ) + + self.campaignList.selectionModel().selectionChanged.connect( + on_campaign_selected + ) + on_campaign_selected() + + docsText = QtWidgets.QLabel( + "

Want more campaigns? You can " + 'offer to help, ' + 'play a community campaign, ' + 'or create your own.' + "

" + ) + docsText.setAlignment(Qt.AlignCenter) + docsText.setOpenExternalLinks(True) + + # Register fields + self.registerField("timePeriod", timePeriodSelect) + self.registerField("usePreset", timePeriodPreset) + + timeGroupLayout = QtWidgets.QGridLayout() + timeGroupLayout.addWidget(timePeriodPresetLabel, 0, 0) + timeGroupLayout.addWidget(timePeriodPreset, 0, 1) + timeGroupLayout.addWidget(timePeriod, 1, 0) + timeGroupLayout.addWidget(timePeriodSelect, 1, 1) + timeGroupLayout.addWidget(self.calendar, 0, 2, 3, 1) + timeGroup.setLayout(timeGroupLayout) + + layout = QtWidgets.QGridLayout() + layout.setColumnMinimumWidth(0, 20) + layout.addWidget(self.campaignList, 0, 0, 5, 1) + layout.addWidget(show_incompatible_campaigns_checkbox, 5, 0, 1, 1) + layout.addWidget(docsText, 6, 0, 1, 1) + layout.addWidget(self.campaignMapDescription, 0, 1, 1, 1) + layout.addWidget(self.performanceText, 1, 1, 1, 1) + layout.addWidget(mapSettingsGroup, 2, 1, 1, 1) + layout.addWidget(timeGroup, 3, 1, 3, 1) + self.setLayout(layout) + + +class QCampaignItem(QStandardItem): + def __init__(self, campaign: Campaign) -> None: + super(QCampaignItem, self).__init__() + self.setData(campaign, QCampaignList.CampaignRole) + + # Define terrain icon path from the DCS installation directory by default + dcs_path = get_dcs_install_directory() + icon_path = dcs_path / campaign.menu_thumbnail_dcs_relative_path + + # If the path does not exist (user does not have the terrain installed), + # use the old icons as fallback to avoid an ugly campaign list with missing icons + if not icon_path.exists(): + icon_path = campaign.fallback_icon_path + + self.setIcon(QtGui.QIcon(QPixmap(str(icon_path)))) + self.setEditable(False) + if campaign.is_compatible: + name = campaign.name + else: + name = f"[INCOMPATIBLE] {campaign.name}" + self.setText(name) + + +class QCampaignList(QListView): + CampaignRole = Qt.UserRole + + def __init__(self, campaigns: list[Campaign], show_incompatible: bool) -> None: + super(QCampaignList, self).__init__() + self.campaign_model = QStandardItemModel(self) + self.setModel(self.campaign_model) + self.setMinimumWidth(250) + self.setMinimumHeight(350) + self.campaigns = campaigns + self.setSelectionBehavior(QAbstractItemView.SelectItems) + self.setup_content(show_incompatible) + + @property + def selected_campaign(self) -> Optional[Campaign]: + return self.currentIndex().data(QCampaignList.CampaignRole) + + def setup_content(self, show_incompatible: bool) -> None: + self.selectionModel().blockSignals(True) + try: + self.campaign_model.clear() + for campaign in self.campaigns: + if show_incompatible or campaign.is_compatible: + item = QCampaignItem(campaign) + self.campaign_model.appendRow(item) + finally: + self.selectionModel().blockSignals(False) + + self.selectionModel().setCurrentIndex( + self.campaign_model.index(0, 0, QModelIndex()), QItemSelectionModel.Select + ) diff --git a/qt_ui/windows/newgame/jinja_env.py b/qt_ui/windows/newgame/jinja_env.py new file mode 100644 index 00000000..0dbe7dae --- /dev/null +++ b/qt_ui/windows/newgame/jinja_env.py @@ -0,0 +1,14 @@ +from __future__ import unicode_literals + +from jinja2 import Environment, FileSystemLoader, select_autoescape + +jinja_env = Environment( + loader=FileSystemLoader("resources/ui/templates"), + autoescape=select_autoescape( + disabled_extensions=("",), + default_for_string=True, + default=True, + ), + trim_blocks=True, + lstrip_blocks=True, +)