from dataclasses import dataclass from shutil import copyfile from typing import Dict, Union from PySide2.QtCore import Signal from PySide2.QtWidgets import ( QGridLayout, QGroupBox, QLabel, QSizePolicy, QVBoxLayout, QPushButton, QInputDialog, QMessageBox, QWidget, ) from dcs import lua from game import Game from game.ato.flight import Flight from game.data.weapons import Pylon from game.persistency import payloads_dir from qt_ui.windows.mission.flight.payload.QPylonEditor import QPylonEditor class QLoadoutEditor(QGroupBox): saved = Signal(str) def __init__(self, flight: Flight, game: Game) -> None: super().__init__("Use custom loadout") self.flight = flight self.game = game self.setCheckable(True) self.setChecked(flight.loadout.is_custom) vbox = QVBoxLayout(self) layout = QGridLayout(self) for i, pylon in enumerate(Pylon.iter_pylons(self.flight.unit_type)): label = QLabel(f"{pylon.number}") label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) layout.addWidget(label, i, 0) layout.addWidget(QPylonEditor(game, flight, pylon), i, 1) vbox.addLayout(layout) layout = QGridLayout(self) save_btn = QPushButton("Save Payload") save_btn.setProperty("style", "btn-danger") save_btn.setMaximumWidth(250) save_btn.clicked.connect(self._save_payload) layout.addWidget(save_btn, 0, 0) purge_btn = QPushButton("Create Backup") purge_btn.setProperty("style", "btn-success") purge_btn.setMaximumWidth(250) purge_btn.clicked.connect(self._backup_payloads) layout.addWidget(purge_btn, 0, 1) vbox.addLayout(layout) self.setLayout(vbox) for i in self.findChildren(QPylonEditor): i.set_from(self.flight.loadout) def _backup_payloads(self) -> None: ac_id = self.flight.unit_type.dcs_unit_type.id payload_file = payloads_dir() / f"{ac_id}.lua" if not payload_file.exists(): return backup_folder = payloads_dir(backup=True) backup_file = backup_folder / f"{ac_id}.lua" if not backup_folder.exists(): backup_folder.mkdir() copyfile(payload_file, backup_file) QMessageBox.information( QWidget(), "Backup Payload", f"Payload file for {self.flight.unit_type.dcs_unit_type.id} was backed up successfully.\n" f"Location: {backup_file}", ) def _save_payload(self) -> None: payload_name_input = self._create_input_dialog() if not payload_name_input.exec_(): return payload_name = payload_name_input.textValue() ac_type = self.flight.unit_type.dcs_unit_type ac_id = ac_type.id payloads_folder = payloads_dir() payload_file = payloads_folder / f"{ac_id}.lua" if not payloads_folder.exists(): payloads_folder.mkdir() ac_type.payloads[payload_name] = DcsPayload.from_flight( self.flight, payload_name ).to_dict() if payload_file.exists(): self._create_backup_if_needed(ac_id) with payload_file.open("r", encoding="utf-8") as f: payloads = lua.loads(f.read()) if payloads: pdict = payloads["unitPayloads"]["payloads"] next_key = len(pdict) + 1 for p in pdict: if pdict[p]["name"] == payload_name: next_key = p pdict[next_key] = DcsPayload.from_flight( self.flight, payload_name ).to_dict() with payload_file.open("w", encoding="utf-8") as f: f.write("local unitPayloads = ") f.write(lua.dumps(payloads["unitPayloads"], indent=1)) f.write("\nreturn unitPayloads") else: with payload_file.open("w", encoding="utf-8") as f: payloads = { "name": f"{self.flight.unit_type.dcs_unit_type.id}", "payloads": { 1: DcsPayload.from_flight(self.flight, payload_name).to_dict(), }, "unitType": f"{self.flight.unit_type.dcs_unit_type.id}", } f.write("local unitPayloads = ") f.write(lua.dumps(payloads, indent=1)) f.write("\nreturn unitPayloads") self.flight.unit_type.dcs_unit_type.add_to_payload_cache(payload_file) self.saved.emit(payload_name) QMessageBox.information( QWidget(), "Payload Saved", f"Payload for {self.flight.unit_type.dcs_unit_type.id} was successfully saved.\n" f"Location: {payload_file}", ) def _create_backup_if_needed(self, ac_id): backup_file = payloads_dir(backup=True) / f"{ac_id}.lua" if not backup_file.exists(): self._backup_payloads() def _create_input_dialog(self): payload_name_input = QInputDialog() payload_name_input.setWindowTitle("Save payload") payload_name_input.setLabelText("Enter a name for the payload to be saved:") payload_name_input.setTextValue(f"Custom {self.flight.flight_type.name}") payload_name_input.setFixedWidth(500) return payload_name_input def reset_pylons(self) -> None: self.flight.use_custom_loadout = self.isChecked() if not self.isChecked(): for i in self.findChildren(QPylonEditor): i.set_from(self.flight.loadout) @dataclass class DcsPayload: displayName: str name: str pylons: Dict[int, Dict[str, Union[str, int]]] tasks: Dict[int, int] @classmethod def from_flight(cls, flight: Flight, payload_name: str): pylons = {} for i, nr in enumerate(flight.loadout.pylons, 1): wpn = flight.loadout.pylons[nr] clsid = wpn.clsid if wpn else "" pylons[i] = { "CLSID": clsid, "num": nr, } return DcsPayload( f"{payload_name}", f"{payload_name}", pylons=pylons, tasks={1: 31}, ) def to_dict(self): return { "displayName": self.displayName, "name": self.name, "pylons": self.pylons, "tasks": self.tasks, }