mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
* Don't generate runway data for heliports. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2710. * Remove dead code. * Implemented support for Chiller Juice Studios F/A-18E/F/G Super Hornet mod The Chiller Juice Studios Super Hornet mod works like this: it changes the stock F/A-18C Hornet into F/A-18E/F/G Super Hornet / Growler. The exact variant is selected with argument 999 in the livery description.lua, which is why I chose to add the three variants in the FA-18C_hornet.yaml. This way, we can use the squadrons mechanism in Liberation to select the appropriate variant by specifying the correct livery for the squadron. Current properties injected are wingspan / width and the custom ordnance added by the mod. Added F/A-18E/F/G banner by Schmokedpancake and F/A-18F icon. Resolves https://github.com/dcs-liberation/dcs_liberation/issues/2681 * Added a separate loadout file for the Chiller Juice Studios F/A-18E/F/G Super Hornet mod. Currently only replaces the FPU-8A fuel tanks with FPU-12s. * Added the possibility to use the AI variant of the F/A-18C in campaigns, allowing different loadouts and in the future, the Super Hornet mod alongside legacy Hornets in the same campaign. * Updated Chiller Juice Studios F/A-18E/F/G Super Hornet mod support to version 2.0. Removed the 1.x version property and pylon injection since they are no longer necessary, since 2.0 adds the Super Hornet variants as separate aircraft. For the same reason, removed the AI-only F/A-18C from the faction files (still retained the aircraft yaml, loadout files and icon/banner in case someone still wants to use it). Includes F/A-18E/F/G banner by Schmokedpancake, loadouts by @sgtfuzzle17 and F/A-18E/F icons. * Added Super Hornet, Growler squadrons and Growler banner by @sgtfuzzle17 The squadrons include the model of the airframe in their name, so they can be referenced directly from campaign yaml files without the risk of conflicting with the same squadron of a different era, flying a different airframe. Also updated the E and G model icons. Resolves #77 * Fixed a bug with the EA-18G banner not being visible in Retribution. Also added the Super Hornet variants to factions bluefor_modern and Israel-USN_2005_Allied_Sword. * Corrected the descriptions for tandem-seat Super Hornet variants. * Updated Chiller Juice Studios F/A-18E/F/G Super Hornet mod support to version 2.1 * Anti-ship loadouts are now named properly. * Update changelog.md * Update QNewGameWizard.py --------- Co-authored-by: Dan Albert <dan@gingerhq.net> Co-authored-by: Raffson <Raffson@users.noreply.github.com>
917 lines
38 KiB
Python
917 lines
38 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import logging
|
|
import math
|
|
from datetime import datetime, timedelta
|
|
from typing import List
|
|
|
|
from PySide2 import QtGui, QtWidgets
|
|
from PySide2.QtCore import QDate, QItemSelectionModel, QPoint, Qt, Signal
|
|
from PySide2.QtWidgets import (
|
|
QCheckBox,
|
|
QLabel,
|
|
QTextEdit,
|
|
QVBoxLayout,
|
|
QTextBrowser,
|
|
QWidget,
|
|
QGridLayout,
|
|
QScrollArea,
|
|
QSizePolicy,
|
|
)
|
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
|
|
from game.campaignloader.campaign import Campaign, DEFAULT_BUDGET
|
|
from game.dcs.aircrafttype import AircraftType
|
|
from game.dcs.unittype import UnitType
|
|
from game.factions import FACTIONS, Faction
|
|
from game.settings import Settings
|
|
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
|
|
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),
|
|
}
|
|
|
|
|
|
class NewGameWizard(QtWidgets.QWizard):
|
|
def __init__(self, parent=None):
|
|
super(NewGameWizard, self).__init__(parent)
|
|
|
|
self.campaigns = list(sorted(Campaign.load_each(), key=lambda x: x.name))
|
|
|
|
self.faction_selection_page = FactionSelection()
|
|
self.addPage(IntroPage())
|
|
self.theater_page = TheaterConfiguration(
|
|
self.campaigns, self.faction_selection_page
|
|
)
|
|
self.addPage(self.theater_page)
|
|
self.addPage(self.faction_selection_page)
|
|
self.addPage(GeneratorOptions())
|
|
self.difficulty_page = DifficultyAndAutomationOptions()
|
|
self.difficulty_page.set_campaign_values(self.campaigns[0])
|
|
|
|
# Update difficulty page on campaign select
|
|
self.theater_page.campaign_selected.connect(
|
|
lambda c: self.difficulty_page.set_campaign_values(c)
|
|
)
|
|
self.addPage(self.difficulty_page)
|
|
self.addPage(ConclusionPage())
|
|
|
|
self.setPixmap(
|
|
QtWidgets.QWizard.WatermarkPixmap,
|
|
QtGui.QPixmap("./resources/ui/wizard/watermark1.png"),
|
|
)
|
|
self.setWizardStyle(QtWidgets.QWizard.ModernStyle)
|
|
|
|
self.setWindowTitle("New Game")
|
|
self.generatedGame = None
|
|
|
|
def accept(self):
|
|
logging.info("New Game Wizard accept")
|
|
logging.info("======================")
|
|
|
|
campaign = self.field("selectedCampaign")
|
|
if campaign is None:
|
|
campaign = self.theater_page.campaignList.selected_campaign
|
|
if campaign is None:
|
|
campaign = self.campaigns[0]
|
|
|
|
logging.info("New campaign selected: %s", campaign.name)
|
|
|
|
if self.field("usePreset"):
|
|
start_date = TIME_PERIODS[
|
|
list(TIME_PERIODS.keys())[self.field("timePeriod")]
|
|
]
|
|
else:
|
|
start_date = self.theater_page.calendar.selectedDate().toPython()
|
|
|
|
logging.info("New campaign start date: %s", start_date.strftime("%m/%d/%Y"))
|
|
settings = Settings(
|
|
player_income_multiplier=self.field("player_income_multiplier") / 10,
|
|
enemy_income_multiplier=self.field("enemy_income_multiplier") / 10,
|
|
automate_runway_repair=self.field("automate_runway_repairs"),
|
|
automate_front_line_reinforcements=self.field(
|
|
"automate_front_line_purchases"
|
|
),
|
|
desired_player_mission_duration=timedelta(
|
|
minutes=self.field("desired_player_mission_duration")
|
|
),
|
|
automate_aircraft_reinforcements=self.field("automate_aircraft_purchases"),
|
|
supercarrier=self.field("supercarrier"),
|
|
max_frontline_length=campaign.data.get("max_frontline_length", 80),
|
|
perf_culling=campaign.data.get("culling_exclusion_radius") is not None,
|
|
perf_culling_distance=campaign.data.get("culling_exclusion_radius", 100),
|
|
)
|
|
generator_settings = GeneratorSettings(
|
|
start_date=start_date,
|
|
start_time=campaign.recommended_start_time,
|
|
player_budget=int(self.field("starting_money")),
|
|
enemy_budget=int(self.field("enemy_starting_money")),
|
|
# QSlider forces integers, so we use 1 to 50 and divide by 10 to
|
|
# give 0.1 to 5.0.
|
|
inverted=self.field("invertMap"),
|
|
advanced_iads=self.field("advanced_iads"),
|
|
no_carrier=self.field("no_carrier"),
|
|
no_lha=self.field("no_lha"),
|
|
no_player_navy=self.field("no_player_navy"),
|
|
no_enemy_navy=self.field("no_enemy_navy"),
|
|
tgo_config=campaign.load_ground_forces_config(),
|
|
)
|
|
mod_settings = ModSettings(
|
|
a4_skyhawk=self.field("a4_skyhawk"),
|
|
a6a_intruder=self.field("a6a_intruder"),
|
|
f4bc_phantom=self.field("f4bc_phantom"),
|
|
f15d_baz=self.field("f15d_baz"),
|
|
f_16_idf=self.field("f_16_idf"),
|
|
fa_18efg=self.field("fa_18efg"),
|
|
f22_raptor=self.field("f22_raptor"),
|
|
f84g_thunderjet=self.field("f84g_thunderjet"),
|
|
f100_supersabre=self.field("f100_supersabre"),
|
|
f104_starfighter=self.field("f104_starfighter"),
|
|
f105_thunderchief=self.field("f105_thunderchief"),
|
|
hercules=self.field("hercules"),
|
|
uh_60l=self.field("uh_60l"),
|
|
jas39_gripen=self.field("jas39_gripen"),
|
|
su30_flanker_h=self.field("su30_flanker_h"),
|
|
su57_felon=self.field("su57_felon"),
|
|
ov10a_bronco=self.field("ov10a_bronco"),
|
|
frenchpack=self.field("frenchpack"),
|
|
high_digit_sams=self.field("high_digit_sams"),
|
|
swedishmilitaryassetspack=self.field("swedishmilitaryassetspack"),
|
|
)
|
|
|
|
blue_faction = self.faction_selection_page.selected_blue_faction
|
|
red_faction = self.faction_selection_page.selected_red_faction
|
|
|
|
logging.info("New campaign blue faction: %s", blue_faction.name)
|
|
logging.info("New campaign red faction: %s", red_faction.name)
|
|
|
|
theater = campaign.load_theater(generator_settings.advanced_iads)
|
|
|
|
logging.info("New campaign theater: %s", theater.terrain.name)
|
|
|
|
generator = GameGenerator(
|
|
blue_faction,
|
|
red_faction,
|
|
theater,
|
|
campaign.load_air_wing_config(theater),
|
|
settings,
|
|
generator_settings,
|
|
mod_settings,
|
|
)
|
|
self.generatedGame = generator.generate()
|
|
|
|
AirWingConfigurationDialog(self.generatedGame, self).exec_()
|
|
|
|
g = self.generatedGame
|
|
herc = AircraftType.named("C-130J-30 Super Hercules")
|
|
if herc in g.blue.air_wing.squadrons or herc in g.red.air_wing.squadrons:
|
|
g.settings.set_plugin_option("herculescargo", True)
|
|
|
|
self.generatedGame.begin_turn_0()
|
|
|
|
super(NewGameWizard, self).accept()
|
|
|
|
|
|
class IntroPage(QtWidgets.QWizardPage):
|
|
def __init__(self, parent=None):
|
|
super(IntroPage, self).__init__(parent)
|
|
|
|
self.setTitle("Introduction")
|
|
self.setPixmap(
|
|
QtWidgets.QWizard.WatermarkPixmap,
|
|
QtGui.QPixmap("./resources/ui/wizard/watermark1.png"),
|
|
)
|
|
|
|
label = QtWidgets.QLabel(
|
|
"This wizard will help you setup a new game.\n\n"
|
|
"Please make sure you saved and backed up your previous game before going through."
|
|
)
|
|
label.setWordWrap(True)
|
|
|
|
layout = QtWidgets.QVBoxLayout()
|
|
layout.addWidget(label)
|
|
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:
|
|
qfu.updateFactionUnits(qfu.faction.aircrafts)
|
|
qfu.updateFactionUnits(qfu.faction.awacs)
|
|
qfu.updateFactionUnits(qfu.faction.tankers)
|
|
qfu.updateFactionUnits(qfu.faction.frontline_units)
|
|
qfu.updateFactionUnits(qfu.faction.artillery_units)
|
|
qfu.updateFactionUnits(qfu.faction.logistics_units)
|
|
qfu.updateFactionUnits(qfu.faction.infantry_units)
|
|
qfu.updateFactionUnits(qfu.faction.preset_groups)
|
|
qfu.updateFactionUnits(qfu.faction.air_defense_units)
|
|
qfu.updateFactionUnits(qfu.faction.naval_units)
|
|
qfu.updateFactionUnits(qfu.faction.missiles)
|
|
return qfu.faction
|
|
|
|
@property
|
|
def selected_blue_faction(self) -> Faction:
|
|
return 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)
|
|
|
|
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)
|
|
runway_repairs = QtWidgets.QCheckBox()
|
|
self.registerField("automate_runway_repairs", runway_repairs)
|
|
assist_layout.addWidget(runway_repairs, 0, 1, Qt.AlignRight)
|
|
|
|
assist_layout.addWidget(QtWidgets.QLabel("Automate front-line purchases"), 1, 0)
|
|
front_line = QtWidgets.QCheckBox()
|
|
self.registerField("automate_front_line_purchases", front_line)
|
|
assist_layout.addWidget(front_line, 1, 1, Qt.AlignRight)
|
|
|
|
assist_layout.addWidget(QtWidgets.QLabel("Automate aircraft purchases"), 2, 0)
|
|
aircraft = QtWidgets.QCheckBox()
|
|
self.registerField("automate_aircraft_purchases", aircraft)
|
|
assist_layout.addWidget(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)
|
|
)
|
|
|
|
|
|
class GeneratorOptions(QtWidgets.QWizardPage):
|
|
def __init__(self, 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")
|
|
no_carrier = QtWidgets.QCheckBox()
|
|
self.registerField("no_carrier", no_carrier)
|
|
no_lha = QtWidgets.QCheckBox()
|
|
self.registerField("no_lha", no_lha)
|
|
supercarrier = QtWidgets.QCheckBox()
|
|
self.registerField("supercarrier", supercarrier)
|
|
no_player_navy = QtWidgets.QCheckBox()
|
|
self.registerField("no_player_navy", no_player_navy)
|
|
no_enemy_navy = QtWidgets.QCheckBox()
|
|
self.registerField("no_enemy_navy", no_enemy_navy)
|
|
desired_player_mission_duration = TimeInputs(
|
|
DEFAULT_MISSION_LENGTH, minimum=30, maximum=150
|
|
)
|
|
self.registerField(
|
|
"desired_player_mission_duration", desired_player_mission_duration.spinner
|
|
)
|
|
|
|
generatorLayout = QtWidgets.QGridLayout()
|
|
generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0)
|
|
generatorLayout.addWidget(no_carrier, 1, 1)
|
|
generatorLayout.addWidget(QtWidgets.QLabel("No LHA"), 2, 0)
|
|
generatorLayout.addWidget(no_lha, 2, 1)
|
|
generatorLayout.addWidget(QtWidgets.QLabel("Use Supercarrier module"), 3, 0)
|
|
generatorLayout.addWidget(supercarrier, 3, 1)
|
|
generatorLayout.addWidget(QtWidgets.QLabel("No Player Navy"), 4, 0)
|
|
generatorLayout.addWidget(no_player_navy, 4, 1)
|
|
generatorLayout.addWidget(QtWidgets.QLabel("No Enemy Navy"), 5, 0)
|
|
generatorLayout.addWidget(no_enemy_navy, 5, 1)
|
|
generatorLayout.addWidget(QtWidgets.QLabel("Desired mission duration"), 6, 0)
|
|
generatorLayout.addLayout(desired_player_mission_duration, 7, 0)
|
|
generatorSettingsGroup.setLayout(generatorLayout)
|
|
|
|
modSettingsGroup = QtWidgets.QGroupBox("Mod Settings")
|
|
a4_skyhawk = QtWidgets.QCheckBox()
|
|
self.registerField("a4_skyhawk", a4_skyhawk)
|
|
a6a_intruder = QtWidgets.QCheckBox()
|
|
self.registerField("a6a_intruder", a6a_intruder)
|
|
hercules = QtWidgets.QCheckBox()
|
|
self.registerField("hercules", hercules)
|
|
uh_60l = QtWidgets.QCheckBox()
|
|
self.registerField("uh_60l", uh_60l)
|
|
f4bc_phantom = QtWidgets.QCheckBox()
|
|
self.registerField("f4bc_phantom", f4bc_phantom)
|
|
f15d_baz = QtWidgets.QCheckBox()
|
|
self.registerField("f15d_baz", f15d_baz)
|
|
f_16_idf = QtWidgets.QCheckBox()
|
|
self.registerField("f_16_idf", f_16_idf)
|
|
fa_18efg = QtWidgets.QCheckBox()
|
|
self.registerField("fa_18efg", fa_18efg)
|
|
f22_raptor = QtWidgets.QCheckBox()
|
|
self.registerField("f22_raptor", f22_raptor)
|
|
f84g_thunderjet = QtWidgets.QCheckBox()
|
|
self.registerField("f84g_thunderjet", f84g_thunderjet)
|
|
f100_supersabre = QtWidgets.QCheckBox()
|
|
self.registerField("f100_supersabre", f100_supersabre)
|
|
f104_starfighter = QtWidgets.QCheckBox()
|
|
self.registerField("f104_starfighter", f104_starfighter)
|
|
f105_thunderchief = QtWidgets.QCheckBox()
|
|
self.registerField("f105_thunderchief", f105_thunderchief)
|
|
jas39_gripen = QtWidgets.QCheckBox()
|
|
self.registerField("jas39_gripen", jas39_gripen)
|
|
su30_flanker_h = QtWidgets.QCheckBox()
|
|
self.registerField("su30_flanker_h", su30_flanker_h)
|
|
su57_felon = QtWidgets.QCheckBox()
|
|
self.registerField("su57_felon", su57_felon)
|
|
ov10a_bronco = QtWidgets.QCheckBox()
|
|
self.registerField("ov10a_bronco", ov10a_bronco)
|
|
frenchpack = QtWidgets.QCheckBox()
|
|
self.registerField("frenchpack", frenchpack)
|
|
high_digit_sams = QtWidgets.QCheckBox()
|
|
self.registerField("high_digit_sams", high_digit_sams)
|
|
swedishmilitaryassetspack = QtWidgets.QCheckBox()
|
|
self.registerField("swedishmilitaryassetspack", swedishmilitaryassetspack)
|
|
|
|
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)", a4_skyhawk),
|
|
("A-6A Intruder (v2.7.5.01)", a6a_intruder),
|
|
("C-130J-30 Super Hercules", hercules),
|
|
("F-4B/C Phantom II (v2.8.1.01 Standalone + 29Jan23 Patch)", f4bc_phantom),
|
|
("F-15D Baz (v1.0)", f15d_baz),
|
|
("F-16I Sufa & F-16D (v3.2 by IDF Mods Project)", f_16_idf),
|
|
("F/A-18E/F/G Super Hornet (version 2.1)", fa_18efg),
|
|
("F-22A Raptor", f22_raptor),
|
|
("F-84G Thunderjet (v2.5.7.01)", f84g_thunderjet),
|
|
("F-100 Super Sabre (v2.7.18.30765 patch 20.10.22)", f100_supersabre),
|
|
("F-104 Starfighter (v2.7.11.222.01)", f104_starfighter),
|
|
("F-105 Thunderchief (v2.7.12.23x)", f105_thunderchief),
|
|
("Frenchpack", frenchpack),
|
|
("High Digit SAMs", high_digit_sams),
|
|
("Swedish Military Assets pack (1.10)", swedishmilitaryassetspack),
|
|
("JAS 39 Gripen (v1.8.0-beta)", jas39_gripen),
|
|
("OV-10A Bronco", ov10a_bronco),
|
|
("Su-30 Flanker-H (V2.01B)", su30_flanker_h),
|
|
("Su-57 Felon", su57_felon),
|
|
("UH-60L Black Hawk (v1.3.1)", uh_60l),
|
|
]
|
|
|
|
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)
|
|
|
|
|
|
class ConclusionPage(QtWidgets.QWizardPage):
|
|
def __init__(self, parent=None):
|
|
super(ConclusionPage, self).__init__(parent)
|
|
|
|
self.setTitle("Conclusion")
|
|
self.setSubTitle("\n\n")
|
|
self.setPixmap(
|
|
QtWidgets.QWizard.WatermarkPixmap,
|
|
QtGui.QPixmap("./resources/ui/wizard/watermark2.png"),
|
|
)
|
|
|
|
self.label = QtWidgets.QLabel(
|
|
"Click 'Finish' to generate and start the new game."
|
|
)
|
|
self.label.setWordWrap(True)
|
|
|
|
layout = QtWidgets.QVBoxLayout()
|
|
layout.addWidget(self.label)
|
|
self.setLayout(layout)
|