Reuse the settings screen in NewGameWizard

Resolves #123
This commit is contained in:
Raffson 2023-05-27 21:48:30 +02:00
parent 0923add514
commit 4898cdb614
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
11 changed files with 231 additions and 170 deletions

View File

@ -14,6 +14,7 @@
* **[Modding]** Support for SW mod v2.55 * **[Modding]** Support for SW mod v2.55
* **[Modding]** Support for Spanish & Australian Naval Assets v3.2.0 by desdemicabina * **[Modding]** Support for Spanish & Australian Naval Assets v3.2.0 by desdemicabina
* **[Modding]** Support for Iron Dome v1.2 by IDF Mods Project * **[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 ## Fixes
* **[New Game Wizard]** Settings would not persist when going back to a previous page. * **[New Game Wizard]** Settings would not persist when going back to a previous page.

View File

@ -0,0 +1,5 @@
from game.settings import Settings
class SettingsContainer:
settings: Settings

View File

@ -257,7 +257,10 @@ class Settings:
CAMPAIGN_MANAGEMENT_PAGE, CAMPAIGN_MANAGEMENT_PAGE,
PILOTS_AND_SQUADRONS_SECTION, PILOTS_AND_SQUADRONS_SECTION,
default=False, 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 # HQ Automation
@ -718,6 +721,9 @@ class Settings:
new_state = Settings().__dict__ new_state = Settings().__dict__
new_state.update(state) new_state.update(state)
self.__dict__.update(new_state) self.__dict__.update(new_state)
from game.plugins import LuaPluginManager
LuaPluginManager().load_settings(self)
@classmethod @classmethod
def _field_description(cls, settings_field: Field[Any]) -> OptionDescription: def _field_description(cls, settings_field: Field[Any]) -> OptionDescription:

View File

@ -1,20 +1,20 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import logging import logging
from datetime import timedelta
from PySide2 import QtGui, QtWidgets from PySide2 import QtGui, QtWidgets
from game.campaignloader.campaign import Campaign from game.campaignloader.campaign import Campaign
from game.dcs.aircrafttype import AircraftType from game.dcs.aircrafttype import AircraftType
from game.settings import Settings
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
from qt_ui.windows.AirWingConfigurationDialog import AirWingConfigurationDialog 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.QFactionSelection import FactionSelection
from qt_ui.windows.newgame.WizardPages.QGeneratorSettings import GeneratorOptions from qt_ui.windows.newgame.WizardPages.QGeneratorSettings import GeneratorOptions
from qt_ui.windows.newgame.WizardPages.QNewGameSettings import NewGameSettings 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): class NewGameWizard(QtWidgets.QWizard):
@ -33,8 +33,7 @@ class NewGameWizard(QtWidgets.QWizard):
self.addPage(self.faction_selection_page) self.addPage(self.faction_selection_page)
self.go_page = GeneratorOptions(self.campaigns[0], self) self.go_page = GeneratorOptions(self.campaigns[0], self)
self.addPage(self.go_page) self.addPage(self.go_page)
self.settings_page = NewGameSettings(self) self.settings_page = NewGameSettings(self.campaigns[0], self)
self.settings_page.set_campaign_values(self.campaigns[0])
# Update difficulty page on campaign select # Update difficulty page on campaign select
self.theater_page.campaign_selected.connect(lambda c: self.update_settings(c)) 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() start_date = self.theater_page.calendar.selectedDate().toPython()
logging.info("New campaign start date: %s", start_date.strftime("%m/%d/%Y")) 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( generator_settings = GeneratorSettings(
start_date=start_date, start_date=start_date,
@ -138,6 +122,8 @@ class NewGameWizard(QtWidgets.QWizard):
logging.info("New campaign theater: %s", theater.terrain.name) logging.info("New campaign theater: %s", theater.terrain.name)
settings = self.settings_page.settings_widget.settings
generator = GameGenerator( generator = GameGenerator(
blue_faction, blue_faction,
red_faction, red_faction,

View File

@ -4,7 +4,14 @@ from copy import deepcopy
from PySide2 import QtWidgets, QtGui from PySide2 import QtWidgets, QtGui
from PySide2.QtCore import Qt 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.campaignloader import Campaign
from game.factions import Faction, FACTIONS from game.factions import Faction, FACTIONS

View File

@ -8,8 +8,7 @@ from PySide2.QtWidgets import QLabel, QVBoxLayout
from game.campaignloader import Campaign from game.campaignloader import Campaign
from game.campaignloader.campaign import DEFAULT_BUDGET from game.campaignloader.campaign import DEFAULT_BUDGET
from qt_ui.widgets.spinsliders import CurrencySpinner, TimeInputs from qt_ui.widgets.spinsliders import CurrencySpinner
from qt_ui.windows.newgame.SettingNames import MISSION_LENGTH, SUPER_CARRIER
DEFAULT_MISSION_LENGTH: timedelta = timedelta(minutes=60) DEFAULT_MISSION_LENGTH: timedelta = timedelta(minutes=60)
@ -51,38 +50,28 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.registerField("no_carrier", self.no_carrier) self.registerField("no_carrier", self.no_carrier)
self.no_lha = QtWidgets.QCheckBox() self.no_lha = QtWidgets.QCheckBox()
self.registerField("no_lha", self.no_lha) self.registerField("no_lha", self.no_lha)
self.supercarrier = QtWidgets.QCheckBox()
self.registerField(SUPER_CARRIER, self.supercarrier)
self.no_player_navy = QtWidgets.QCheckBox() self.no_player_navy = QtWidgets.QCheckBox()
self.registerField("no_player_navy", self.no_player_navy) self.registerField("no_player_navy", self.no_player_navy)
self.no_enemy_navy = QtWidgets.QCheckBox() self.no_enemy_navy = QtWidgets.QCheckBox()
self.registerField("no_enemy_navy", self.no_enemy_navy) 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 = QtWidgets.QGridLayout()
generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0) generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0)
generatorLayout.addWidget(self.no_carrier, 1, 1) generatorLayout.addWidget(self.no_carrier, 1, 1)
generatorLayout.addWidget(QtWidgets.QLabel("No LHA"), 2, 0) generatorLayout.addWidget(QtWidgets.QLabel("No LHA"), 2, 0)
generatorLayout.addWidget(self.no_lha, 2, 1) generatorLayout.addWidget(self.no_lha, 2, 1)
generatorLayout.addWidget(QtWidgets.QLabel("Use Supercarrier module"), 3, 0) generatorLayout.addWidget(QtWidgets.QLabel("No Player Navy"), 3, 0)
generatorLayout.addWidget(self.supercarrier, 3, 1) generatorLayout.addWidget(self.no_player_navy, 3, 1)
generatorLayout.addWidget(QtWidgets.QLabel("No Player Navy"), 4, 0) generatorLayout.addWidget(QtWidgets.QLabel("No Enemy Navy"), 4, 0)
generatorLayout.addWidget(self.no_player_navy, 4, 1) generatorLayout.addWidget(self.no_enemy_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.player_budget = BudgetInputs("Player starting budget", DEFAULT_BUDGET)
self.registerField("starting_money", self.player_budget.starting_money) 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.enemy_budget = BudgetInputs("Enemy starting budget", DEFAULT_BUDGET)
self.registerField("enemy_starting_money", self.enemy_budget.starting_money) 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) generatorSettingsGroup.setLayout(generatorLayout)
@ -204,10 +193,8 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.no_carrier.setChecked(s.get("no_carrier", False)) self.no_carrier.setChecked(s.get("no_carrier", False))
self.no_lha.setChecked(s.get("no_lha", 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_player_navy.setChecked(s.get("no_player_navy", False))
self.no_enemy_navy.setChecked(s.get("no_enemy_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.a4_skyhawk.setChecked(s.get("a4_skyhawk", False))
self.a6a_intruder.setChecked(s.get("a6a_intruder", False)) self.a6a_intruder.setChecked(s.get("a6a_intruder", False))

View File

@ -1,84 +1,37 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from PySide2 import QtWidgets, QtGui from PySide2 import QtWidgets, QtGui
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QLabel
from game.campaignloader import Campaign from game.campaignloader import Campaign
from qt_ui.widgets.spinsliders import FloatSpinSlider from game.settings import Settings
from qt_ui.windows.newgame.SettingNames import RUNWAY_REPAIR, FRONTLINE, AIRCRAFT from qt_ui.windows.settings.QSettingsWindow import QSettingsWidget
class NewGameSettings(QtWidgets.QWizardPage): class NewGameSettings(QtWidgets.QWizardPage):
def __init__(self, parent=None) -> None: def __init__(self, campaign: Campaign, parent=None) -> None:
super().__init__(parent) super().__init__(parent)
self.setTitle("Campaign options") self.setTitle("Campaign options")
self.setSubTitle( self.setSubTitle("\nAll other options unrelated to campaign generation.")
"\nAll other options unrelated to campaign generation."
)
self.setPixmap( self.setPixmap(
QtWidgets.QWizard.LogoPixmap, QtWidgets.QWizard.LogoPixmap,
QtGui.QPixmap("./resources/ui/wizard/logo1.png"), QtGui.QPixmap("./resources/ui/wizard/logo1.png"),
) )
layout = QtWidgets.QVBoxLayout() settings = Settings()
settings.__setstate__(campaign.settings)
economy_group = QtWidgets.QGroupBox("Economy options") settings.player_income_multiplier = (
layout.addWidget(economy_group) campaign.recommended_player_income_multiplier
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.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") def set_campaign_values(self, c: Campaign):
layout.addWidget(assist_group) sw = self.settings_widget
assist_layout = QtWidgets.QGridLayout() sw.settings.player_income_multiplier = c.recommended_player_income_multiplier
assist_group.setLayout(assist_layout) sw.settings.enemy_income_multiplier = c.recommended_enemy_income_multiplier
sw.settings.__dict__.update(c.settings)
assist_layout.addWidget(QtWidgets.QLabel("Automate runway repairs"), 0, 0) print(sw.settings.__dict__)
self.runway_repairs = QtWidgets.QCheckBox() sw.update_from_settings()
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))

View File

@ -6,7 +6,14 @@ from typing import List, Optional
from PySide2 import QtWidgets, QtGui from PySide2 import QtWidgets, QtGui
from PySide2.QtCore import Signal, QDate, QPoint, QItemSelectionModel, Qt, QModelIndex from PySide2.QtCore import Signal, QDate, QPoint, QItemSelectionModel, Qt, QModelIndex
from PySide2.QtGui import QStandardItem, QPixmap, QStandardItemModel 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 game.campaignloader import Campaign
from qt_ui.liberation_install import get_dcs_install_directory from qt_ui.liberation_install import get_dcs_install_directory

View File

@ -1,9 +1,10 @@
import json import json
import logging import logging
import textwrap import textwrap
from typing import Callable
import zipfile import zipfile
from typing import Callable, Optional, Dict
from PySide2 import QtWidgets
from PySide2.QtCore import QItemSelectionModel, QPoint, QSize, Qt from PySide2.QtCore import QItemSelectionModel, QPoint, QSize, Qt
from PySide2.QtGui import QStandardItem, QStandardItemModel from PySide2.QtGui import QStandardItem, QStandardItemModel
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
@ -37,6 +38,7 @@ from game.settings import (
OptionDescription, OptionDescription,
Settings, Settings,
) )
from game.settings.ISettingsContainer import SettingsContainer
from game.sim import GameUpdateEvents from game.sim import GameUpdateEvents
from qt_ui.widgets.QLabeledWidget import QLabeledWidget from qt_ui.widgets.QLabeledWidget import QLabeledWidget
from qt_ui.widgets.spinsliders import FloatSpinSlider, TimeInputs from qt_ui.widgets.spinsliders import FloatSpinSlider, TimeInputs
@ -45,27 +47,29 @@ from qt_ui.windows.settings.plugins import PluginOptionsPage, PluginsPage
class CheatSettingsBox(QGroupBox): 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") super().__init__("Cheat Settings")
self.main_layout = QVBoxLayout() self.main_layout = QVBoxLayout()
self.setLayout(self.main_layout) self.setLayout(self.main_layout)
self.red_ato_checkbox = QCheckBox() 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.red_ato_checkbox.toggled.connect(apply_settings)
self.frontline_cheat_checkbox = QCheckBox() 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.frontline_cheat_checkbox.toggled.connect(apply_settings)
self.base_capture_cheat_checkbox = QCheckBox() self.base_capture_cheat_checkbox = QCheckBox()
self.base_capture_cheat_checkbox.setChecked( 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.base_capture_cheat_checkbox.toggled.connect(apply_settings)
self.transfer_cheat_checkbox = QCheckBox() 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.transfer_cheat_checkbox.toggled.connect(apply_settings)
self.red_ato = QLabeledWidget("Show Red ATO:", self.red_ato_checkbox) self.red_ato = QLabeledWidget("Show Red ATO:", self.red_ato_checkbox)
@ -105,14 +109,22 @@ class AutoSettingsLayout(QGridLayout):
self, self,
page: str, page: str,
section: str, section: str,
settings: Settings, sc: SettingsContainer,
write_full_settings: Callable[[], None], write_full_settings: Callable[[], None],
) -> None: ) -> None:
super().__init__() super().__init__()
self.settings = settings self.page = page
self.section = section
self.sc = sc
self.write_full_settings = write_full_settings 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) self.add_label(row, description)
if isinstance(description, BooleanOption): if isinstance(description, BooleanOption):
self.add_checkbox_for(row, name, description) self.add_checkbox_for(row, name, description)
@ -142,31 +154,33 @@ class AutoSettingsLayout(QGridLayout):
def on_toggle(value: bool) -> None: def on_toggle(value: bool) -> None:
if description.invert: if description.invert:
value = not value value = not value
self.settings.__dict__[name] = value self.sc.settings.__dict__[name] = value
if description.causes_expensive_game_update: if description.causes_expensive_game_update:
self.write_full_settings() self.write_full_settings()
checkbox = QCheckBox() checkbox = QCheckBox()
value = self.settings.__dict__[name] value = self.sc.settings.__dict__[name]
if description.invert: if description.invert:
value = not value value = not value
checkbox.setChecked(value) checkbox.setChecked(value)
checkbox.toggled.connect(on_toggle) checkbox.toggled.connect(on_toggle)
self.addWidget(checkbox, row, 1, Qt.AlignRight) self.addWidget(checkbox, row, 1, Qt.AlignRight)
self.settings_map[name] = checkbox
def add_combobox_for(self, row: int, name: str, description: ChoicesOption) -> None: def add_combobox_for(self, row: int, name: str, description: ChoicesOption) -> None:
combobox = QComboBox() combobox = QComboBox()
def on_changed(index: int) -> None: 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(): for text, value in description.choices.items():
combobox.addItem(text, value) combobox.addItem(text, value)
combobox.setCurrentText( combobox.setCurrentText(
description.text_for_value(self.settings.__dict__[name]) description.text_for_value(self.sc.settings.__dict__[name])
) )
combobox.currentIndexChanged.connect(on_changed) combobox.currentIndexChanged.connect(on_changed)
self.addWidget(combobox, row, 1, Qt.AlignRight) self.addWidget(combobox, row, 1, Qt.AlignRight)
self.settings_map[name] = combobox
def add_float_spin_slider_for( def add_float_spin_slider_for(
self, row: int, name: str, description: BoundedFloatOption self, row: int, name: str, description: BoundedFloatOption
@ -174,44 +188,62 @@ class AutoSettingsLayout(QGridLayout):
spinner = FloatSpinSlider( spinner = FloatSpinSlider(
description.min, description.min,
description.max, description.max,
self.settings.__dict__[name], self.sc.settings.__dict__[name],
divisor=description.divisor, divisor=description.divisor,
) )
def on_changed() -> None: def on_changed() -> None:
self.settings.__dict__[name] = spinner.value self.sc.settings.__dict__[name] = spinner.value
spinner.spinner.valueChanged.connect(on_changed) spinner.spinner.valueChanged.connect(on_changed)
self.addLayout(spinner, row, 1, Qt.AlignRight) self.addLayout(spinner, row, 1, Qt.AlignRight)
self.settings_map[name] = spinner
def add_spinner_for( def add_spinner_for(
self, row: int, name: str, description: BoundedIntOption self, row: int, name: str, description: BoundedIntOption
) -> None: ) -> None:
def on_changed(value: int) -> None: def on_changed(value: int) -> None:
self.settings.__dict__[name] = value self.sc.settings.__dict__[name] = value
if description.causes_expensive_game_update: if description.causes_expensive_game_update:
self.write_full_settings() self.write_full_settings()
spinner = QSpinBox() spinner = QSpinBox()
spinner.setMinimum(description.min) spinner.setMinimum(description.min)
spinner.setMaximum(description.max) spinner.setMaximum(description.max)
spinner.setValue(self.settings.__dict__[name]) spinner.setValue(self.sc.settings.__dict__[name])
spinner.valueChanged.connect(on_changed) spinner.valueChanged.connect(on_changed)
self.addWidget(spinner, row, 1, Qt.AlignRight) self.addWidget(spinner, row, 1, Qt.AlignRight)
self.settings_map[name] = spinner
def add_duration_controls_for( def add_duration_controls_for(
self, row: int, name: str, description: MinutesOption self, row: int, name: str, description: MinutesOption
) -> None: ) -> None:
inputs = TimeInputs( inputs = TimeInputs(
self.settings.__dict__[name], description.min, description.max self.sc.settings.__dict__[name], description.min, description.max
) )
def on_changed() -> None: def on_changed() -> None:
self.settings.__dict__[name] = inputs.value self.sc.settings.__dict__[name] = inputs.value
inputs.spinner.valueChanged.connect(on_changed) inputs.spinner.valueChanged.connect(on_changed)
self.addLayout(inputs, row, 1, Qt.AlignRight) 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): class AutoSettingsGroup(QGroupBox):
@ -219,57 +251,80 @@ class AutoSettingsGroup(QGroupBox):
self, self,
page: str, page: str,
section: str, section: str,
settings: Settings, sc: SettingsContainer,
write_full_settings: Callable[[], None], write_full_settings: Callable[[], None],
) -> None: ) -> None:
super().__init__(section) 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): class AutoSettingsPageLayout(QVBoxLayout):
def __init__( def __init__(
self, self,
page: str, page: str,
settings: Settings, sc: SettingsContainer,
write_full_settings: Callable[[], None], write_full_settings: Callable[[], None],
) -> None: ) -> None:
super().__init__() super().__init__()
self.setAlignment(Qt.AlignTop) self.setAlignment(Qt.AlignTop)
self.widgets = []
for section in Settings.sections(page): for section in Settings.sections(page):
self.addWidget( self.widgets.append(
AutoSettingsGroup(page, section, settings, write_full_settings) 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): class AutoSettingsPage(QWidget):
def __init__( def __init__(
self, self,
page: str, page: str,
settings: Settings, sc: SettingsContainer,
write_full_settings: Callable[[], None], write_full_settings: Callable[[], None],
) -> None: ) -> None:
super().__init__() 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): class QSettingsWindow(QDialog):
def __init__(self, game: Game): def __init__(self, game: Game):
super().__init__() super().__init__()
self.game = game self.game = game
self.pluginsPage = None self.setLayout(QSettingsWidget(game.settings, game).layout)
self.pluginsOptionsPage = None
self.pages: dict[str, AutoSettingsPage] = {}
for page in Settings.pages():
self.pages[page] = AutoSettingsPage(page, game.settings, self.applySettings)
self.setModal(True) self.setModal(True)
self.setWindowTitle("Settings") self.setWindowTitle("Settings")
self.setWindowIcon(CONST.ICONS["Settings"]) self.setWindowIcon(CONST.ICONS["Settings"])
self.setMinimumSize(840, 480) 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() self.initUi()
def initUi(self): def initUi(self):
@ -306,7 +361,6 @@ class QSettingsWindow(QDialog):
self.categoryModel.appendRow(cheat) self.categoryModel.appendRow(cheat)
self.right_layout.addWidget(self.cheatPage) self.right_layout.addWidget(self.cheatPage)
self.pluginsPage = PluginsPage()
plugins = QStandardItem("LUA Plugins") plugins = QStandardItem("LUA Plugins")
plugins.setIcon(CONST.ICONS["Plugins"]) plugins.setIcon(CONST.ICONS["Plugins"])
plugins.setEditable(False) plugins.setEditable(False)
@ -314,7 +368,6 @@ class QSettingsWindow(QDialog):
self.categoryModel.appendRow(plugins) self.categoryModel.appendRow(plugins)
self.right_layout.addWidget(self.pluginsPage) self.right_layout.addWidget(self.pluginsPage)
self.pluginsOptionsPage = PluginOptionsPage()
pluginsOptions = QStandardItem("LUA Plugins Options") pluginsOptions = QStandardItem("LUA Plugins Options")
pluginsOptions.setIcon(CONST.ICONS["PluginsOptions"]) pluginsOptions.setIcon(CONST.ICONS["PluginsOptions"])
pluginsOptions.setEditable(False) pluginsOptions.setEditable(False)
@ -351,10 +404,11 @@ class QSettingsWindow(QDialog):
self.cheatLayout = QVBoxLayout() self.cheatLayout = QVBoxLayout()
self.cheatPage.setLayout(self.cheatLayout) 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.cheatLayout.addWidget(self.cheat_options)
self.moneyCheatBox = QGroupBox("Money Cheat") self.moneyCheatBox = QGroupBox("Money Cheat")
self.moneyCheatBox.setDisabled(self.game is None)
self.moneyCheatBox.setAlignment(Qt.AlignTop) self.moneyCheatBox.setAlignment(Qt.AlignTop)
self.moneyCheatBoxLayout = QGridLayout() self.moneyCheatBoxLayout = QGridLayout()
self.moneyCheatBox.setLayout(self.moneyCheatBoxLayout) self.moneyCheatBox.setLayout(self.moneyCheatBoxLayout)
@ -380,26 +434,41 @@ class QSettingsWindow(QDialog):
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)
def applySettings(self): def applySettings(self):
self.game.settings.show_red_ato = self.cheat_options.show_red_ato self.settings.show_red_ato = self.cheat_options.show_red_ato
self.game.settings.enable_frontline_cheats = ( self.settings.enable_frontline_cheats = self.cheat_options.show_frontline_cheat
self.cheat_options.show_frontline_cheat self.settings.enable_base_capture_cheat = (
)
self.game.settings.enable_base_capture_cheat = (
self.cheat_options.show_base_capture_cheat self.cheat_options.show_base_capture_cheat
) )
self.game.settings.enable_transfer_cheat = ( self.settings.enable_transfer_cheat = self.cheat_options.show_transfer_cheat
self.cheat_options.show_transfer_cheat
)
events = GameUpdateEvents() if self.game:
self.game.compute_unculled_zones(events) events = GameUpdateEvents()
EventStream.put_nowait(events) self.game.compute_unculled_zones(events)
GameUpdateSignal.get_instance().updateGame(self.game) EventStream.put_nowait(events)
GameUpdateSignal.get_instance().updateGame(self.game)
def onSelectionChanged(self): def onSelectionChanged(self) -> None:
index = self.categoryList.selectionModel().currentIndex().row() index = self.categoryList.selectionModel().currentIndex().row()
self.right_layout.setCurrentIndex(index) 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): def load_settings(self):
sd = settings_dir() sd = settings_dir()
if not sd.exists(): if not sd.exists():
@ -411,12 +480,10 @@ class QSettingsWindow(QDialog):
filename = zipfilename.split("/")[-1].replace(".zip", ".json") filename = zipfilename.split("/")[-1].replace(".zip", ".json")
settings = json.loads( settings = json.loads(
zf.read(filename).decode("utf-8"), 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.settings.__setstate__(settings)
self.close() self.update_from_settings()
new = QSettingsWindow(self.game)
new.exec_()
def save_settings(self): def save_settings(self):
sd = settings_dir() sd = settings_dir()
@ -431,9 +498,9 @@ class QSettingsWindow(QDialog):
zf.writestr( zf.writestr(
filename, filename,
json.dumps( json.dumps(
self.game.settings.__dict__, self.settings.__dict__,
indent=2, indent=2,
default=self.game.settings.default_json, default=self.settings.default_json,
), ),
zipfile.ZIP_DEFLATED, zipfile.ZIP_DEFLATED,
) )

View File

@ -1,3 +1,5 @@
from typing import Dict, List
from PySide2.QtCore import Qt, QLocale from PySide2.QtCore import Qt, QLocale
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
QCheckBox, QCheckBox,
@ -11,6 +13,8 @@ from PySide2.QtWidgets import (
) )
from game.plugins import LuaPlugin, LuaPluginManager from game.plugins import LuaPlugin, LuaPluginManager
from game.settings import Settings
from game.settings.ISettingsContainer import SettingsContainer
class PluginsBox(QGroupBox): class PluginsBox(QGroupBox):
@ -21,6 +25,8 @@ class PluginsBox(QGroupBox):
layout.setAlignment(Qt.AlignTop) layout.setAlignment(Qt.AlignTop)
self.setLayout(layout) self.setLayout(layout)
self.plugin_map: Dict[str, QCheckBox] = {}
for row, plugin in enumerate(LuaPluginManager.plugins()): for row, plugin in enumerate(LuaPluginManager.plugins()):
if not plugin.show_in_ui: if not plugin.show_in_ui:
continue continue
@ -31,17 +37,29 @@ class PluginsBox(QGroupBox):
checkbox.setChecked(plugin.get_value) checkbox.setChecked(plugin.get_value)
checkbox.toggled.connect(plugin.set_value) checkbox.toggled.connect(plugin.set_value)
layout.addWidget(checkbox, row, 1) 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): class PluginsPage(QWidget):
def __init__(self) -> None: def __init__(self, sc: SettingsContainer) -> None:
super().__init__() super().__init__()
self.sc = sc
layout = QVBoxLayout() layout = QVBoxLayout()
layout.setAlignment(Qt.AlignTop) layout.setAlignment(Qt.AlignTop)
self.setLayout(layout) 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): class PluginOptionsBox(QGroupBox):
@ -52,6 +70,8 @@ class PluginOptionsBox(QGroupBox):
layout.setAlignment(Qt.AlignTop) layout.setAlignment(Qt.AlignTop)
self.setLayout(layout) self.setLayout(layout)
self.widgets: Dict[str, QWidget] = {}
for row, option in enumerate(plugin.options): for row, option in enumerate(plugin.options):
layout.addWidget(QLabel(option.name), row, 0) layout.addWidget(QLabel(option.name), row, 0)
@ -61,6 +81,7 @@ class PluginOptionsBox(QGroupBox):
checkbox.setChecked(val) checkbox.setChecked(val)
checkbox.toggled.connect(option.set_value) checkbox.toggled.connect(option.set_value)
layout.addWidget(checkbox, row, 1) layout.addWidget(checkbox, row, 1)
self.widgets[option.identifier] = checkbox
elif type(val) == float or type(val) == int: elif type(val) == float or type(val) == int:
if type(val) == float: if type(val) == float:
spinbox = QDoubleSpinBox() spinbox = QDoubleSpinBox()
@ -73,16 +94,36 @@ class PluginOptionsBox(QGroupBox):
spinbox.setValue(val) spinbox.setValue(val)
spinbox.valueChanged.connect(option.set_value) spinbox.valueChanged.connect(option.set_value)
layout.addWidget(spinbox, row, 1) 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): class PluginOptionsPage(QWidget):
def __init__(self) -> None: def __init__(self, sc: SettingsContainer) -> None:
super().__init__() super().__init__()
self.sc = sc
layout = QVBoxLayout() layout = QVBoxLayout()
layout.setAlignment(Qt.AlignTop) layout.setAlignment(Qt.AlignTop)
self.setLayout(layout) self.setLayout(layout)
self.pobs: List[PluginOptionsBox] = []
for plugin in LuaPluginManager.plugins(): for plugin in LuaPluginManager.plugins():
if plugin.options: 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)

View File

@ -17,6 +17,7 @@ recommended_enemy_income_multiplier: 1.0
advanced_iads: false advanced_iads: false
settings: settings:
max_frontline_length: 30 max_frontline_length: 30
player_income_multiplier: 1.5
squadrons: squadrons:
CVN-74 John Stennis: CVN-74 John Stennis:
- primary: BARCAP - primary: BARCAP