From d0dec86a6de8f1f0bbd953a2b8053adbf93a0833 Mon Sep 17 00:00:00 2001 From: Raffson Date: Fri, 29 Dec 2023 17:41:07 +0100 Subject: [PATCH] Add Load/Save for AirWingConfig #167 --- game/migrator.py | 6 ++ game/persistency.py | 4 + qt_ui/windows/AirWingConfigurationDialog.py | 106 +++++++++++++++++++- 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/game/migrator.py b/game/migrator.py index 4a0cf75f..f6899a0e 100644 --- a/game/migrator.py +++ b/game/migrator.py @@ -140,6 +140,7 @@ class Migrator: "Netherlands": "The Netherlands", "CHN": "China", } + # Squadrons for cp in self.game.theater.controlpoints: for s in cp.squadrons: preferred_task = max( @@ -165,6 +166,11 @@ class Migrator: if self.is_liberation: s.set_auto_assignable_mission_types(s.auto_assignable_mission_types) + # SquadronDefs + for coa in self.game.coalitions: + for ac, sdefs in coa.air_wing.squadron_defs.items(): + for sdef in sdefs: + try_set_attr(sdef, "radio_presets", {}) @typing.no_type_check def _update_factions(self) -> None: diff --git a/game/persistency.py b/game/persistency.py index 69d610fa..3e24e97d 100644 --- a/game/persistency.py +++ b/game/persistency.py @@ -91,6 +91,10 @@ def settings_dir() -> Path: return base_path() / "Retribution" / "Settings" +def airwing_dir() -> Path: + return base_path() / "Retribution" / "AirWing" + + def payloads_dir(backup: bool = False) -> Path: payloads = base_path() / "MissionEditor" / "UnitPayloads" if backup: diff --git a/qt_ui/windows/AirWingConfigurationDialog.py b/qt_ui/windows/AirWingConfigurationDialog.py index 87397e97..cdb04057 100644 --- a/qt_ui/windows/AirWingConfigurationDialog.py +++ b/qt_ui/windows/AirWingConfigurationDialog.py @@ -1,6 +1,7 @@ from collections import defaultdict -from typing import Iterable, Iterator, Optional +from typing import Iterable, Iterator, Optional, Any +import yaml from PySide6.QtCore import ( QItemSelection, QItemSelectionModel, @@ -29,13 +30,19 @@ from PySide6.QtWidgets import ( QMessageBox, QSpinBox, QGroupBox, + QFileDialog, ) +from dcs.mapping import Point from game import Game from game.ato.flighttype import FlightType -from game.campaignloader.campaignairwingconfig import DEFAULT_SQUADRON_SIZE +from game.campaignloader.campaignairwingconfig import ( + DEFAULT_SQUADRON_SIZE, + CampaignAirWingConfig, +) from game.coalition import Coalition from game.dcs.aircrafttype import AircraftType +from game.persistency import airwing_dir from game.squadrons import AirWing, Pilot, Squadron from game.squadrons.squadrondef import SquadronDef from game.theater import ControlPoint, ParkingType @@ -783,8 +790,19 @@ class AirWingConfigurationDialog(QDialog): self.tab_widget.addTab(coalition_tab, name) self.tabs.append(coalition_tab) + load_save_layout = QHBoxLayout() + save_button = QPushButton("Save Config") + save_button.setProperty("style", "btn-primary") + save_button.clicked.connect(lambda state: self.save_config()) + load_button = QPushButton("Load Config") + load_button.setProperty("style", "btn-primary") + load_button.clicked.connect(lambda state: self.load_config()) + load_save_layout.addWidget(load_button) + load_save_layout.addWidget(save_button) + layout.addLayout(load_save_layout) + buttons_layout = QHBoxLayout() - apply_button = QPushButton("Accept Changes && Start Campaign") + apply_button = QPushButton("Accept Changes") apply_button.setProperty("style", "btn-accept") apply_button.clicked.connect(lambda state: self.accept()) discard_button = QPushButton("Reset Changes") @@ -794,6 +812,88 @@ class AirWingConfigurationDialog(QDialog): buttons_layout.addWidget(apply_button) layout.addLayout(buttons_layout) + def save_config(self) -> None: + awd = airwing_dir() + if not awd.exists(): + awd.mkdir() + fd = QFileDialog( + caption="Save Air Wing", directory=str(awd), filter="*.yaml;*.yml" + ) + fd.setAcceptMode(QFileDialog.AcceptMode.AcceptSave) + if fd.exec_(): + airwing = self._build_air_wing() + filename = fd.selectedFiles()[0] + with open(filename, "w") as f: + f.write(yaml.dump(airwing)) + + def _build_air_wing(self) -> dict: + w = self.tab_widget.currentWidget() + assert isinstance(w, AirWingConfigurationTab) + squadrons = {} + for ac, sqs in w.coalition.air_wing.squadrons.items(): + for s in sqs: + cp = s.location.at + if isinstance(cp, Point): + key = s.location.name + else: + key = cp.id + name = ( + s.name + if s.name + in [x.name for x in w.coalition.air_wing.squadron_defs[ac]] + else s.aircraft.variant_id + ) + entry = { + "primary": s.primary_task.value, + "secondary": [ + sec.value + for sec in s.auto_assignable_mission_types + if sec.value != s.primary_task.value + ], + "aircraft": [name], + "size": s.max_size, + } + if squadrons.get(key): + squadrons[key].append(entry) + else: + squadrons[key] = [entry] + return squadrons + + def load_config(self) -> None: + result = QMessageBox.information( + None, + "Load Air Wing?", + "Revert will not be possible after loading a different Air Wing.
" + "Are you sure you want to continue?", + QMessageBox.StandardButton.Yes, + QMessageBox.StandardButton.No, + ) + if result == QMessageBox.StandardButton.No: + return + + awd = airwing_dir() + if not awd.exists(): + awd.mkdir() + fd = QFileDialog( + caption="Load Air Wing", directory=str(awd), filter="*.yaml;*.yml" + ) + if fd.exec_(): + filename = fd.selectedFiles()[0] + with open(filename, "r") as f: + airwing = yaml.safe_load(f) + self._construct_air_wing_tab(airwing) + + def _construct_air_wing_tab(self, airwing: dict[str, Any]) -> None: + w = self.tab_widget.currentWidget() + assert isinstance(w, AirWingConfigurationTab) + c = w.coalition + c.air_wing.squadrons = defaultdict(list) + config = CampaignAirWingConfig.from_campaign_data(airwing, c.game.theater) + c.configure_default_air_wing(config) + w.revert() + if c.game.turn != 0: + c.initialize_turn(False) + def revert(self) -> None: for tab in self.tabs: tab.revert()