From 4898cdb6148620ac7f0b0141419943849d0467c7 Mon Sep 17 00:00:00 2001 From: Raffson Date: Sat, 27 May 2023 21:48:30 +0200 Subject: [PATCH] Reuse the settings screen in NewGameWizard Resolves #123 --- changelog.md | 1 + game/settings/ISettingsContainer.py | 5 + game/settings/settings.py | 8 +- qt_ui/windows/newgame/QNewGameWizard.py | 28 +-- .../newgame/WizardPages/QFactionSelection.py | 9 +- .../newgame/WizardPages/QGeneratorSettings.py | 27 +-- .../newgame/WizardPages/QNewGameSettings.py | 85 ++------- .../WizardPages/QTheaterConfiguration.py | 9 +- qt_ui/windows/settings/QSettingsWindow.py | 179 ++++++++++++------ qt_ui/windows/settings/plugins.py | 49 ++++- resources/campaigns/AssaultonDamascus.yaml | 1 + 11 files changed, 231 insertions(+), 170 deletions(-) create mode 100644 game/settings/ISettingsContainer.py diff --git a/changelog.md b/changelog.md index df301ba3..7196af1c 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,7 @@ * **[Modding]** Support for SW mod v2.55 * **[Modding]** Support for Spanish & Australian Naval Assets v3.2.0 by desdemicabina * **[Modding]** Support for Iron Dome v1.2 by IDF Mods Project +* **[NewGameWizard]** Re-organized generator options & show the regular settings menu instead of the limited "Difficulty & Automation" page. ## Fixes * **[New Game Wizard]** Settings would not persist when going back to a previous page. diff --git a/game/settings/ISettingsContainer.py b/game/settings/ISettingsContainer.py new file mode 100644 index 00000000..452eaaf6 --- /dev/null +++ b/game/settings/ISettingsContainer.py @@ -0,0 +1,5 @@ +from game.settings import Settings + + +class SettingsContainer: + settings: Settings diff --git a/game/settings/settings.py b/game/settings/settings.py index 3a9f4601..29030520 100644 --- a/game/settings/settings.py +++ b/game/settings/settings.py @@ -257,7 +257,10 @@ class Settings: CAMPAIGN_MANAGEMENT_PAGE, PILOTS_AND_SQUADRONS_SECTION, default=False, - detail="If set, squadrons will be limited to a maximum number of aircraft.", + detail=( + "If set, squadrons will not be able to exceed a maximum number of aircraft " + "(configurable), and the campaign will begin with all squadrons at full strength." + ), ) # HQ Automation @@ -718,6 +721,9 @@ class Settings: new_state = Settings().__dict__ new_state.update(state) self.__dict__.update(new_state) + from game.plugins import LuaPluginManager + + LuaPluginManager().load_settings(self) @classmethod def _field_description(cls, settings_field: Field[Any]) -> OptionDescription: diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index edb07c83..7b30cbad 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -1,20 +1,20 @@ from __future__ import unicode_literals import logging -from datetime import timedelta from PySide2 import QtGui, QtWidgets from game.campaignloader.campaign import Campaign from game.dcs.aircrafttype import AircraftType -from game.settings import Settings from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings from qt_ui.windows.AirWingConfigurationDialog import AirWingConfigurationDialog -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 +from qt_ui.windows.newgame.WizardPages.QTheaterConfiguration import ( + TheaterConfiguration, + TIME_PERIODS, +) class NewGameWizard(QtWidgets.QWizard): @@ -33,8 +33,7 @@ class NewGameWizard(QtWidgets.QWizard): self.addPage(self.faction_selection_page) self.go_page = GeneratorOptions(self.campaigns[0], self) self.addPage(self.go_page) - self.settings_page = NewGameSettings(self) - self.settings_page.set_campaign_values(self.campaigns[0]) + self.settings_page = NewGameSettings(self.campaigns[0], self) # Update difficulty page on campaign select self.theater_page.campaign_selected.connect(lambda c: self.update_settings(c)) @@ -70,21 +69,6 @@ class NewGameWizard(QtWidgets.QWizard): start_date = self.theater_page.calendar.selectedDate().toPython() logging.info("New campaign start date: %s", start_date.strftime("%m/%d/%Y")) - settings = Settings() - settings.__setstate__(campaign.settings) - settings.player_income_multiplier = self.field("player_income_multiplier") / 10 - settings.enemy_income_multiplier = self.field("enemy_income_multiplier") / 10 - settings.automate_runway_repair = self.field(RUNWAY_REPAIR) - settings.automate_front_line_reinforcements = self.field(FRONTLINE) - settings.desired_player_mission_duration = timedelta( - minutes=self.field(MISSION_LENGTH) - ) - settings.enable_squadron_aircraft_limits = self.field("use_new_squadron_rules") - settings.automate_aircraft_reinforcements = self.field(AIRCRAFT) - settings.supercarrier = self.field(SUPER_CARRIER) - settings.perf_culling = ( - campaign.settings.get("perf_culling_distance") is not None - ) generator_settings = GeneratorSettings( start_date=start_date, @@ -138,6 +122,8 @@ class NewGameWizard(QtWidgets.QWizard): logging.info("New campaign theater: %s", theater.terrain.name) + settings = self.settings_page.settings_widget.settings + generator = GameGenerator( blue_faction, red_faction, diff --git a/qt_ui/windows/newgame/WizardPages/QFactionSelection.py b/qt_ui/windows/newgame/WizardPages/QFactionSelection.py index caf46e0b..f97736e7 100644 --- a/qt_ui/windows/newgame/WizardPages/QFactionSelection.py +++ b/qt_ui/windows/newgame/WizardPages/QFactionSelection.py @@ -4,7 +4,14 @@ 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 PySide2.QtWidgets import ( + QScrollArea, + QWidget, + QGridLayout, + QCheckBox, + QLabel, + QTextBrowser, +) from game.campaignloader import Campaign from game.factions import Faction, FACTIONS diff --git a/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py b/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py index c8c1cc95..9bc97f4a 100644 --- a/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py +++ b/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py @@ -8,8 +8,7 @@ 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 +from qt_ui.widgets.spinsliders import CurrencySpinner DEFAULT_MISSION_LENGTH: timedelta = timedelta(minutes=60) @@ -51,38 +50,28 @@ class GeneratorOptions(QtWidgets.QWizardPage): 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) + generatorLayout.addWidget(QtWidgets.QLabel("No Player Navy"), 3, 0) + generatorLayout.addWidget(self.no_player_navy, 3, 1) + generatorLayout.addWidget(QtWidgets.QLabel("No Enemy Navy"), 4, 0) + generatorLayout.addWidget(self.no_enemy_navy, 4, 1) 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) + generatorLayout.addLayout(self.player_budget, 5, 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) + generatorLayout.addLayout(self.enemy_budget, 6, 0) generatorSettingsGroup.setLayout(generatorLayout) @@ -204,10 +193,8 @@ class GeneratorOptions(QtWidgets.QWizardPage): 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)) diff --git a/qt_ui/windows/newgame/WizardPages/QNewGameSettings.py b/qt_ui/windows/newgame/WizardPages/QNewGameSettings.py index 7887c836..ae904452 100644 --- a/qt_ui/windows/newgame/WizardPages/QNewGameSettings.py +++ b/qt_ui/windows/newgame/WizardPages/QNewGameSettings.py @@ -1,84 +1,37 @@ 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 +from game.settings import Settings +from qt_ui.windows.settings.QSettingsWindow import QSettingsWidget class NewGameSettings(QtWidgets.QWizardPage): - def __init__(self, parent=None) -> None: + def __init__(self, campaign: Campaign, parent=None) -> None: super().__init__(parent) self.setTitle("Campaign options") - self.setSubTitle( - "\nAll other options unrelated to campaign generation." - ) + 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." - ) + settings = Settings() + settings.__setstate__(campaign.settings) + settings.player_income_multiplier = ( + campaign.recommended_player_income_multiplier ) + settings.enemy_income_multiplier = campaign.recommended_enemy_income_multiplier + settings.__dict__.update(campaign.settings) + self.settings_widget = QSettingsWidget(settings) + self.setLayout(self.settings_widget.layout) - 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)) + def set_campaign_values(self, c: Campaign): + sw = self.settings_widget + sw.settings.player_income_multiplier = c.recommended_player_income_multiplier + sw.settings.enemy_income_multiplier = c.recommended_enemy_income_multiplier + sw.settings.__dict__.update(c.settings) + print(sw.settings.__dict__) + sw.update_from_settings() diff --git a/qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py b/qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py index c67bcfe4..f7794ba7 100644 --- a/qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py +++ b/qt_ui/windows/newgame/WizardPages/QTheaterConfiguration.py @@ -6,7 +6,14 @@ 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 PySide2.QtWidgets import ( + QCheckBox, + QTextBrowser, + QTextEdit, + QLabel, + QListView, + QAbstractItemView, +) from game.campaignloader import Campaign from qt_ui.liberation_install import get_dcs_install_directory diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index e39a6443..9a07d876 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -1,9 +1,10 @@ import json import logging import textwrap -from typing import Callable import zipfile +from typing import Callable, Optional, Dict +from PySide2 import QtWidgets from PySide2.QtCore import QItemSelectionModel, QPoint, QSize, Qt from PySide2.QtGui import QStandardItem, QStandardItemModel from PySide2.QtWidgets import ( @@ -37,6 +38,7 @@ from game.settings import ( OptionDescription, Settings, ) +from game.settings.ISettingsContainer import SettingsContainer from game.sim import GameUpdateEvents from qt_ui.widgets.QLabeledWidget import QLabeledWidget from qt_ui.widgets.spinsliders import FloatSpinSlider, TimeInputs @@ -45,27 +47,29 @@ from qt_ui.windows.settings.plugins import PluginOptionsPage, PluginsPage class CheatSettingsBox(QGroupBox): - def __init__(self, game: Game, apply_settings: Callable[[], None]) -> None: + def __init__( + self, sc: SettingsContainer, apply_settings: Callable[[], None] + ) -> None: super().__init__("Cheat Settings") self.main_layout = QVBoxLayout() self.setLayout(self.main_layout) self.red_ato_checkbox = QCheckBox() - self.red_ato_checkbox.setChecked(game.settings.show_red_ato) + self.red_ato_checkbox.setChecked(sc.settings.show_red_ato) self.red_ato_checkbox.toggled.connect(apply_settings) self.frontline_cheat_checkbox = QCheckBox() - self.frontline_cheat_checkbox.setChecked(game.settings.enable_frontline_cheats) + self.frontline_cheat_checkbox.setChecked(sc.settings.enable_frontline_cheats) self.frontline_cheat_checkbox.toggled.connect(apply_settings) self.base_capture_cheat_checkbox = QCheckBox() self.base_capture_cheat_checkbox.setChecked( - game.settings.enable_base_capture_cheat + sc.settings.enable_base_capture_cheat ) self.base_capture_cheat_checkbox.toggled.connect(apply_settings) self.transfer_cheat_checkbox = QCheckBox() - self.transfer_cheat_checkbox.setChecked(game.settings.enable_transfer_cheat) + self.transfer_cheat_checkbox.setChecked(sc.settings.enable_transfer_cheat) self.transfer_cheat_checkbox.toggled.connect(apply_settings) self.red_ato = QLabeledWidget("Show Red ATO:", self.red_ato_checkbox) @@ -105,14 +109,22 @@ class AutoSettingsLayout(QGridLayout): self, page: str, section: str, - settings: Settings, + sc: SettingsContainer, write_full_settings: Callable[[], None], ) -> None: super().__init__() - self.settings = settings + self.page = page + self.section = section + self.sc = sc self.write_full_settings = write_full_settings + self.settings_map: Dict[str, QWidget] = {} - for row, (name, description) in enumerate(Settings.fields(page, section)): + self.init_ui() + + def init_ui(self): + for row, (name, description) in enumerate( + Settings.fields(self.page, self.section) + ): self.add_label(row, description) if isinstance(description, BooleanOption): self.add_checkbox_for(row, name, description) @@ -142,31 +154,33 @@ class AutoSettingsLayout(QGridLayout): def on_toggle(value: bool) -> None: if description.invert: value = not value - self.settings.__dict__[name] = value + self.sc.settings.__dict__[name] = value if description.causes_expensive_game_update: self.write_full_settings() checkbox = QCheckBox() - value = self.settings.__dict__[name] + value = self.sc.settings.__dict__[name] if description.invert: value = not value checkbox.setChecked(value) checkbox.toggled.connect(on_toggle) self.addWidget(checkbox, row, 1, Qt.AlignRight) + self.settings_map[name] = checkbox def add_combobox_for(self, row: int, name: str, description: ChoicesOption) -> None: combobox = QComboBox() def on_changed(index: int) -> None: - self.settings.__dict__[name] = combobox.itemData(index) + self.sc.settings.__dict__[name] = combobox.itemData(index) for text, value in description.choices.items(): combobox.addItem(text, value) combobox.setCurrentText( - description.text_for_value(self.settings.__dict__[name]) + description.text_for_value(self.sc.settings.__dict__[name]) ) combobox.currentIndexChanged.connect(on_changed) self.addWidget(combobox, row, 1, Qt.AlignRight) + self.settings_map[name] = combobox def add_float_spin_slider_for( self, row: int, name: str, description: BoundedFloatOption @@ -174,44 +188,62 @@ class AutoSettingsLayout(QGridLayout): spinner = FloatSpinSlider( description.min, description.max, - self.settings.__dict__[name], + self.sc.settings.__dict__[name], divisor=description.divisor, ) def on_changed() -> None: - self.settings.__dict__[name] = spinner.value + self.sc.settings.__dict__[name] = spinner.value spinner.spinner.valueChanged.connect(on_changed) self.addLayout(spinner, row, 1, Qt.AlignRight) + self.settings_map[name] = spinner def add_spinner_for( self, row: int, name: str, description: BoundedIntOption ) -> None: def on_changed(value: int) -> None: - self.settings.__dict__[name] = value + self.sc.settings.__dict__[name] = value if description.causes_expensive_game_update: self.write_full_settings() spinner = QSpinBox() spinner.setMinimum(description.min) spinner.setMaximum(description.max) - spinner.setValue(self.settings.__dict__[name]) + spinner.setValue(self.sc.settings.__dict__[name]) spinner.valueChanged.connect(on_changed) self.addWidget(spinner, row, 1, Qt.AlignRight) + self.settings_map[name] = spinner def add_duration_controls_for( self, row: int, name: str, description: MinutesOption ) -> None: inputs = TimeInputs( - self.settings.__dict__[name], description.min, description.max + self.sc.settings.__dict__[name], description.min, description.max ) def on_changed() -> None: - self.settings.__dict__[name] = inputs.value + self.sc.settings.__dict__[name] = inputs.value inputs.spinner.valueChanged.connect(on_changed) self.addLayout(inputs, row, 1, Qt.AlignRight) + self.settings_map[name] = inputs + + def update_from_settings(self) -> None: + for name, description in Settings.fields(self.page, self.section): + widget = self.settings_map[name] + value = self.sc.settings.__dict__[name] + if isinstance(widget, QCheckBox): + widget.setChecked(value) + elif isinstance(widget, QComboBox): + widget.setCurrentText(description.text_for_value(value)) + elif isinstance(widget, FloatSpinSlider): + widget.spinner.setValue(int(value * widget.spinner.divisor)) + elif isinstance(widget, QSpinBox): + widget.setValue(value) + elif isinstance(widget, TimeInputs): + widget.spinner.setValue(value.seconds // 60) class AutoSettingsGroup(QGroupBox): @@ -219,57 +251,80 @@ class AutoSettingsGroup(QGroupBox): self, page: str, section: str, - settings: Settings, + sc: SettingsContainer, write_full_settings: Callable[[], None], ) -> None: super().__init__(section) - self.setLayout(AutoSettingsLayout(page, section, settings, write_full_settings)) + self.layout = AutoSettingsLayout(page, section, sc, write_full_settings) + self.setLayout(self.layout) + + def update_from_settings(self) -> None: + self.layout.update_from_settings() class AutoSettingsPageLayout(QVBoxLayout): def __init__( self, page: str, - settings: Settings, + sc: SettingsContainer, write_full_settings: Callable[[], None], ) -> None: super().__init__() self.setAlignment(Qt.AlignTop) + self.widgets = [] for section in Settings.sections(page): - self.addWidget( - AutoSettingsGroup(page, section, settings, write_full_settings) + self.widgets.append( + AutoSettingsGroup(page, section, sc, write_full_settings) ) + self.addWidget(self.widgets[-1]) + + def update_from_settings(self) -> None: + for w in self.widgets: + w.update_from_settings() class AutoSettingsPage(QWidget): def __init__( self, page: str, - settings: Settings, + sc: SettingsContainer, write_full_settings: Callable[[], None], ) -> None: super().__init__() - self.setLayout(AutoSettingsPageLayout(page, settings, write_full_settings)) + self.layout = AutoSettingsPageLayout(page, sc, write_full_settings) + self.setLayout(self.layout) + + def update_from_settings(self) -> None: + self.layout.update_from_settings() class QSettingsWindow(QDialog): def __init__(self, game: Game): super().__init__() - self.game = game - self.pluginsPage = None - self.pluginsOptionsPage = None - - self.pages: dict[str, AutoSettingsPage] = {} - for page in Settings.pages(): - self.pages[page] = AutoSettingsPage(page, game.settings, self.applySettings) + self.setLayout(QSettingsWidget(game.settings, game).layout) self.setModal(True) self.setWindowTitle("Settings") self.setWindowIcon(CONST.ICONS["Settings"]) self.setMinimumSize(840, 480) + +class QSettingsWidget(QtWidgets.QWizardPage, SettingsContainer): + def __init__(self, settings: Settings, game: Optional[Game] = None): + super().__init__() + + self.settings = game.settings if game else settings + self.game = game + + self.pages: dict[str, AutoSettingsPage] = {} + for page in Settings.pages(): + self.pages[page] = AutoSettingsPage(page, self, self.applySettings) + + self.pluginsPage = PluginsPage(self) + self.pluginsOptionsPage = PluginOptionsPage(self) + self.initUi() def initUi(self): @@ -306,7 +361,6 @@ class QSettingsWindow(QDialog): self.categoryModel.appendRow(cheat) self.right_layout.addWidget(self.cheatPage) - self.pluginsPage = PluginsPage() plugins = QStandardItem("LUA Plugins") plugins.setIcon(CONST.ICONS["Plugins"]) plugins.setEditable(False) @@ -314,7 +368,6 @@ class QSettingsWindow(QDialog): self.categoryModel.appendRow(plugins) self.right_layout.addWidget(self.pluginsPage) - self.pluginsOptionsPage = PluginOptionsPage() pluginsOptions = QStandardItem("LUA Plugins Options") pluginsOptions.setIcon(CONST.ICONS["PluginsOptions"]) pluginsOptions.setEditable(False) @@ -351,10 +404,11 @@ class QSettingsWindow(QDialog): self.cheatLayout = QVBoxLayout() self.cheatPage.setLayout(self.cheatLayout) - self.cheat_options = CheatSettingsBox(self.game, self.applySettings) + self.cheat_options = CheatSettingsBox(self, self.applySettings) self.cheatLayout.addWidget(self.cheat_options) self.moneyCheatBox = QGroupBox("Money Cheat") + self.moneyCheatBox.setDisabled(self.game is None) self.moneyCheatBox.setAlignment(Qt.AlignTop) self.moneyCheatBoxLayout = QGridLayout() self.moneyCheatBox.setLayout(self.moneyCheatBoxLayout) @@ -380,26 +434,41 @@ class QSettingsWindow(QDialog): GameUpdateSignal.get_instance().updateGame(self.game) def applySettings(self): - self.game.settings.show_red_ato = self.cheat_options.show_red_ato - self.game.settings.enable_frontline_cheats = ( - self.cheat_options.show_frontline_cheat - ) - self.game.settings.enable_base_capture_cheat = ( + self.settings.show_red_ato = self.cheat_options.show_red_ato + self.settings.enable_frontline_cheats = self.cheat_options.show_frontline_cheat + self.settings.enable_base_capture_cheat = ( self.cheat_options.show_base_capture_cheat ) - self.game.settings.enable_transfer_cheat = ( - self.cheat_options.show_transfer_cheat - ) + self.settings.enable_transfer_cheat = self.cheat_options.show_transfer_cheat - events = GameUpdateEvents() - self.game.compute_unculled_zones(events) - EventStream.put_nowait(events) - GameUpdateSignal.get_instance().updateGame(self.game) + if self.game: + events = GameUpdateEvents() + self.game.compute_unculled_zones(events) + EventStream.put_nowait(events) + GameUpdateSignal.get_instance().updateGame(self.game) - def onSelectionChanged(self): + def onSelectionChanged(self) -> None: index = self.categoryList.selectionModel().currentIndex().row() self.right_layout.setCurrentIndex(index) + def update_from_settings(self) -> None: + for p in self.pages.values(): + p.update_from_settings() + + self.cheat_options.red_ato_checkbox.setChecked(self.settings.show_red_ato) + self.cheat_options.base_capture_cheat_checkbox.setChecked( + self.settings.enable_base_capture_cheat + ) + self.cheat_options.frontline_cheat_checkbox.setChecked( + self.settings.enable_frontline_cheats + ) + self.cheat_options.transfer_cheat_checkbox.setChecked( + self.settings.enable_transfer_cheat + ) + + self.pluginsPage.update_from_settings() + self.pluginsOptionsPage.update_from_settings() + def load_settings(self): sd = settings_dir() if not sd.exists(): @@ -411,12 +480,10 @@ class QSettingsWindow(QDialog): filename = zipfilename.split("/")[-1].replace(".zip", ".json") settings = json.loads( zf.read(filename).decode("utf-8"), - object_hook=self.game.settings.obj_hook, + object_hook=self.settings.obj_hook, ) - self.game.settings.__setstate__(settings) - self.close() - new = QSettingsWindow(self.game) - new.exec_() + self.settings.__setstate__(settings) + self.update_from_settings() def save_settings(self): sd = settings_dir() @@ -431,9 +498,9 @@ class QSettingsWindow(QDialog): zf.writestr( filename, json.dumps( - self.game.settings.__dict__, + self.settings.__dict__, indent=2, - default=self.game.settings.default_json, + default=self.settings.default_json, ), zipfile.ZIP_DEFLATED, ) diff --git a/qt_ui/windows/settings/plugins.py b/qt_ui/windows/settings/plugins.py index 8d12b6d6..eccec28c 100644 --- a/qt_ui/windows/settings/plugins.py +++ b/qt_ui/windows/settings/plugins.py @@ -1,3 +1,5 @@ +from typing import Dict, List + from PySide2.QtCore import Qt, QLocale from PySide2.QtWidgets import ( QCheckBox, @@ -11,6 +13,8 @@ from PySide2.QtWidgets import ( ) from game.plugins import LuaPlugin, LuaPluginManager +from game.settings import Settings +from game.settings.ISettingsContainer import SettingsContainer class PluginsBox(QGroupBox): @@ -21,6 +25,8 @@ class PluginsBox(QGroupBox): layout.setAlignment(Qt.AlignTop) self.setLayout(layout) + self.plugin_map: Dict[str, QCheckBox] = {} + for row, plugin in enumerate(LuaPluginManager.plugins()): if not plugin.show_in_ui: continue @@ -31,17 +37,29 @@ class PluginsBox(QGroupBox): checkbox.setChecked(plugin.get_value) checkbox.toggled.connect(plugin.set_value) layout.addWidget(checkbox, row, 1) + self.plugin_map[plugin.identifier] = checkbox + + def update_from_settings(self, settings: Settings): + for identifier, enabled in settings.plugins.items(): + if identifier in self.plugin_map: + self.plugin_map[identifier].setChecked(enabled) class PluginsPage(QWidget): - def __init__(self) -> None: + def __init__(self, sc: SettingsContainer) -> None: super().__init__() + self.sc = sc + layout = QVBoxLayout() layout.setAlignment(Qt.AlignTop) self.setLayout(layout) - layout.addWidget(PluginsBox()) + self.plugins_box = PluginsBox() + layout.addWidget(self.plugins_box) + + def update_from_settings(self): + self.plugins_box.update_from_settings(self.sc.settings) class PluginOptionsBox(QGroupBox): @@ -52,6 +70,8 @@ class PluginOptionsBox(QGroupBox): layout.setAlignment(Qt.AlignTop) self.setLayout(layout) + self.widgets: Dict[str, QWidget] = {} + for row, option in enumerate(plugin.options): layout.addWidget(QLabel(option.name), row, 0) @@ -61,6 +81,7 @@ class PluginOptionsBox(QGroupBox): checkbox.setChecked(val) checkbox.toggled.connect(option.set_value) layout.addWidget(checkbox, row, 1) + self.widgets[option.identifier] = checkbox elif type(val) == float or type(val) == int: if type(val) == float: spinbox = QDoubleSpinBox() @@ -73,16 +94,36 @@ class PluginOptionsBox(QGroupBox): spinbox.setValue(val) spinbox.valueChanged.connect(option.set_value) layout.addWidget(spinbox, row, 1) + self.widgets[option.identifier] = spinbox + + def update_from_settings(self, settings: Settings) -> None: + for identifier in self.widgets: + value = settings.plugin_option(identifier) + w = self.widgets[identifier] + if isinstance(w, QCheckBox): + w.setChecked(value) + elif isinstance(w, QDoubleSpinBox) or isinstance(w, QSpinBox): + w.setValue(value) class PluginOptionsPage(QWidget): - def __init__(self) -> None: + def __init__(self, sc: SettingsContainer) -> None: super().__init__() + self.sc = sc + layout = QVBoxLayout() layout.setAlignment(Qt.AlignTop) self.setLayout(layout) + self.pobs: List[PluginOptionsBox] = [] + for plugin in LuaPluginManager.plugins(): if plugin.options: - layout.addWidget(PluginOptionsBox(plugin)) + pob = PluginOptionsBox(plugin) + layout.addWidget(pob) + self.pobs.append(pob) + + def update_from_settings(self): + for pob in self.pobs: + pob.update_from_settings(self.sc.settings) diff --git a/resources/campaigns/AssaultonDamascus.yaml b/resources/campaigns/AssaultonDamascus.yaml index 6c104c25..f50ef5f4 100644 --- a/resources/campaigns/AssaultonDamascus.yaml +++ b/resources/campaigns/AssaultonDamascus.yaml @@ -17,6 +17,7 @@ recommended_enemy_income_multiplier: 1.0 advanced_iads: false settings: max_frontline_length: 30 + player_income_multiplier: 1.5 squadrons: CVN-74 John Stennis: - primary: BARCAP