Break QNewGameWizard up into pieces

This commit is contained in:
Raffson 2023-05-27 16:44:01 +02:00
parent d5e019b078
commit 0923add514
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
8 changed files with 898 additions and 868 deletions

View File

@ -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
)

View File

@ -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("<strong>Aircraft:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.aircraft, counter, grid)
if len(self.faction.awacs) > 0:
grid.addWidget(QLabel("<strong>AWACS:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.awacs, counter, grid)
if len(self.faction.tankers) > 0:
grid.addWidget(QLabel("<strong>Tankers:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.tankers, counter, grid)
if len(self.faction.frontline_units) > 0:
grid.addWidget(QLabel("<strong>Frontlines vehicles:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.frontline_units, counter, grid)
if len(self.faction.artillery_units) > 0:
grid.addWidget(QLabel("<strong>Artillery units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.artillery_units, counter, grid)
if len(self.faction.logistics_units) > 0:
grid.addWidget(QLabel("<strong>Logistics units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.logistics_units, counter, grid)
if len(self.faction.infantry_units) > 0:
grid.addWidget(QLabel("<strong>Infantry units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.infantry_units, counter, grid)
if len(self.faction.preset_groups) > 0:
grid.addWidget(QLabel("<strong>Preset groups:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.preset_groups, counter, grid)
if len(self.faction.air_defense_units) > 0:
grid.addWidget(QLabel("<strong>Air defenses:</strong>"), counter, 0)
counter = self._add_checkboxes(
self.faction.air_defense_units, counter, grid
)
if len(self.faction.naval_units) > 0:
grid.addWidget(QLabel("<strong>Naval units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.naval_units, counter, grid)
if len(self.faction.missiles) > 0:
grid.addWidget(QLabel("<strong>Missile units:</strong>"), 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("<b>Player Faction :</b>")
self.blueFactionSelect = QtWidgets.QComboBox()
blueFaction.setBuddy(self.blueFactionSelect)
redFaction = QtWidgets.QLabel("<b>Enemy Faction :</b>")
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(
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Custom-Factions"><span style="color:#FFFFFF;">How to create your own faction</span></a>'
)
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(
"<p>Want more campaigns? You can "
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Campaign-maintenance"><span style="color:#FFFFFF;">offer to help</span></a>, '
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Community-campaigns"><span style="color:#FFFFFF;">play a community campaign</span></a>, '
'or <a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Custom-Campaigns"><span style="color:#FFFFFF;">create your own</span></a>.'
"</p>"
)
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(
"<p>Select the mods you have installed. If your chosen factions support them, you'll be able to use these mods in your campaign.</p>"
)
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)

View File

@ -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

View File

@ -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("<strong>Aircraft:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.aircraft, counter, grid)
if len(self.faction.awacs) > 0:
grid.addWidget(QLabel("<strong>AWACS:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.awacs, counter, grid)
if len(self.faction.tankers) > 0:
grid.addWidget(QLabel("<strong>Tankers:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.tankers, counter, grid)
if len(self.faction.frontline_units) > 0:
grid.addWidget(QLabel("<strong>Frontlines vehicles:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.frontline_units, counter, grid)
if len(self.faction.artillery_units) > 0:
grid.addWidget(QLabel("<strong>Artillery units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.artillery_units, counter, grid)
if len(self.faction.logistics_units) > 0:
grid.addWidget(QLabel("<strong>Logistics units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.logistics_units, counter, grid)
if len(self.faction.infantry_units) > 0:
grid.addWidget(QLabel("<strong>Infantry units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.infantry_units, counter, grid)
if len(self.faction.preset_groups) > 0:
grid.addWidget(QLabel("<strong>Preset groups:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.preset_groups, counter, grid)
if len(self.faction.air_defense_units) > 0:
grid.addWidget(QLabel("<strong>Air defenses:</strong>"), counter, 0)
counter = self._add_checkboxes(
self.faction.air_defense_units, counter, grid
)
if len(self.faction.naval_units) > 0:
grid.addWidget(QLabel("<strong>Naval units:</strong>"), counter, 0)
counter = self._add_checkboxes(self.faction.naval_units, counter, grid)
if len(self.faction.missiles) > 0:
grid.addWidget(QLabel("<strong>Missile units:</strong>"), 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("<b>Player Faction :</b>")
self.blueFactionSelect = QtWidgets.QComboBox()
blueFaction.setBuddy(self.blueFactionSelect)
redFaction = QtWidgets.QLabel("<b>Enemy Faction :</b>")
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(
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Custom-Factions"><span style="color:#FFFFFF;">How to create your own faction</span></a>'
)
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)

View File

@ -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(
"<p>Select the mods you have installed. If your chosen factions support them, you'll be able to use these mods in your campaign.</p>"
)
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)
)

View File

@ -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))

View File

@ -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(
"<p>Want more campaigns? You can "
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Campaign-maintenance"><span style="color:#FFFFFF;">offer to help</span></a>, '
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Community-campaigns"><span style="color:#FFFFFF;">play a community campaign</span></a>, '
'or <a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Custom-Campaigns"><span style="color:#FFFFFF;">create your own</span></a>.'
"</p>"
)
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
)

View File

@ -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,
)