diff --git a/qt_ui/windows/newgame/QCampaignList.py b/qt_ui/windows/newgame/QCampaignList.py index 9329edfd..5d3de21c 100644 --- a/qt_ui/windows/newgame/QCampaignList.py +++ b/qt_ui/windows/newgame/QCampaignList.py @@ -1,46 +1,62 @@ +from __future__ import annotations + import json import logging -import os +from dataclasses import dataclass +from pathlib import Path +from typing import List from PySide2 import QtGui -from PySide2.QtCore import QSize, QItemSelectionModel -from PySide2.QtGui import QStandardItemModel, QStandardItem -from PySide2.QtWidgets import QListView, QAbstractItemView +from PySide2.QtCore import QItemSelectionModel +from PySide2.QtGui import QStandardItem, QStandardItemModel +from PySide2.QtWidgets import QAbstractItemView, QListView -from theater import caucasus, nevada, persiangulf, normandy, thechannel, syria, ConflictTheater import qt_ui.uiconstants as CONST +from theater import ConflictTheater -CAMPAIGN_DIR = ".\\resources\\campaigns" -CAMPAIGNS = [] -# Load the campaigns files from the directory -campaign_files = os.listdir(CAMPAIGN_DIR) -for f in campaign_files: - try: - ff = os.path.join(CAMPAIGN_DIR, f) - with open(ff, "r") as campaign_data: - data = json.load(campaign_data) - choice = (data["name"], ff, "Terrain_" + data["theater"].replace(" ", "")) - logging.info("Loaded campaign : " + data["name"]) - CAMPAIGNS.append(choice) - ConflictTheater.from_file(choice[1]) - logging.info("Loaded campaign :" + ff) - except Exception as e: - logging.info("Unable to load campaign :" + f) -CAMPAIGNS = sorted(CAMPAIGNS, key=lambda x: x[0]) +@dataclass(frozen=True) +class Campaign: + name: str + icon_name: str + theater: ConflictTheater + + @classmethod + def from_json(cls, path: Path) -> Campaign: + with path.open() as campaign_file: + data = json.load(campaign_file) + + sanitized_theater = data["theater"].replace(" ", "") + return cls(data["name"], f"Terrain_{sanitized_theater}", + ConflictTheater.from_json(data)) + + +def load_campaigns() -> List[Campaign]: + campaign_dir = Path("resources\\campaigns") + campaigns = [] + for path in campaign_dir.iterdir(): + try: + logging.debug(f"Loading campaign from {path}...") + campaign = Campaign.from_json(path) + campaigns.append(campaign) + except RuntimeError: + logging.exception(f"Unable to load campaign from {path}") + + return sorted(campaigns, key=lambda x: x.name) + class QCampaignItem(QStandardItem): - def __init__(self, text, filename, icon): + def __init__(self, campaign: Campaign) -> None: super(QCampaignItem, self).__init__() - self.filename = filename - self.setIcon(QtGui.QIcon(CONST.ICONS[icon])) + self.setIcon(QtGui.QIcon(CONST.ICONS[campaign.icon_name])) self.setEditable(False) - self.setText(text) + self.setText(campaign.name) + class QCampaignList(QListView): - def __init__(self): + def __init__(self, campaigns: List[Campaign]) -> None: super(QCampaignList, self).__init__() self.model = QStandardItemModel(self) self.setModel(self.model) @@ -48,12 +64,12 @@ class QCampaignList(QListView): self.setMinimumHeight(350) self.campaigns = [] self.setSelectionBehavior(QAbstractItemView.SelectItems) - self.setup_content() + self.setup_content(campaigns) - def setup_content(self): - for i, campaign in enumerate(CAMPAIGNS): + def setup_content(self, campaigns: List[Campaign]) -> None: + for campaign in campaigns: self.campaigns.append(campaign) - item = QCampaignItem(*campaign) + item = QCampaignItem(campaign) self.model.appendRow(item) self.setSelectedCampaign(0) self.repaint() diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index 9b82f14f..8a706ec7 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -2,27 +2,34 @@ from __future__ import unicode_literals import datetime import logging +from typing import List from PySide2 import QtGui, QtWidgets -from PySide2.QtCore import QPoint, QItemSelectionModel -from PySide2.QtWidgets import QHBoxLayout, QVBoxLayout +from PySide2.QtCore import QItemSelectionModel, QPoint +from PySide2.QtWidgets import QVBoxLayout from dcs.task import CAP, CAS import qt_ui.uiconstants as CONST -from game import db, Game +from game import Game, db from game.settings import Settings from gen import namegen -from qt_ui.windows.newgame.QCampaignList import QCampaignList, CAMPAIGNS -from theater import start_generator, persiangulf, nevada, caucasus, ConflictTheater, normandy, thechannel +from qt_ui.windows.newgame.QCampaignList import ( + Campaign, + QCampaignList, + load_campaigns, +) +from theater import ConflictTheater, start_generator class NewGameWizard(QtWidgets.QWizard): def __init__(self, parent=None): super(NewGameWizard, self).__init__(parent) + self.campaigns = load_campaigns() + self.addPage(IntroPage()) self.addPage(FactionSelection()) - self.addPage(TheaterConfiguration()) + self.addPage(TheaterConfiguration(self.campaigns)) self.addPage(MiscOptions()) self.addPage(ConclusionPage()) @@ -43,9 +50,9 @@ class NewGameWizard(QtWidgets.QWizard): selectedCampaign = self.field("selectedCampaign") if selectedCampaign is None: - selectedCampaign = CAMPAIGNS[0] + selectedCampaign = self.campaigns[0] - conflictTheater = ConflictTheater.from_file(selectedCampaign[1]) + conflictTheater = selectedCampaign.theater timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]] midGame = self.field("midGame") @@ -232,8 +239,8 @@ class FactionSelection(QtWidgets.QWizardPage): class TheaterConfiguration(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super(TheaterConfiguration, self).__init__(parent) + def __init__(self, campaigns: List[Campaign], parent=None) -> None: + super().__init__(parent) self.setTitle("Theater configuration") self.setSubTitle("\nChoose a terrain and time period for this game.") @@ -244,7 +251,7 @@ class TheaterConfiguration(QtWidgets.QWizardPage): QtGui.QPixmap('./resources/ui/wizard/watermark3.png')) # List of campaigns - campaignList = QCampaignList() + campaignList = QCampaignList(campaigns) self.registerField("selectedCampaign", campaignList) def on_campaign_selected(): diff --git a/theater/conflicttheater.py b/theater/conflicttheater.py index d92def90..c9013062 100644 --- a/theater/conflicttheater.py +++ b/theater/conflicttheater.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import json -from typing import Dict, Iterator, List, Optional, Tuple +from typing import Any, Dict, Iterator, List, Optional, Tuple from dcs.mapping import Point from dcs.terrain import ( @@ -163,39 +165,35 @@ class ConflictTheater: return cp @staticmethod - def from_file(filename): - with open(filename, "r") as content: - json_data = json.loads(content.read()) + def from_json(data: Dict[str, Any]) -> ConflictTheater: + theaters = { + "Caucasus": CaucasusTheater, + "Nevada": NevadaTheater, + "Persian Gulf": PersianGulfTheater, + "Normandy": NormandyTheater, + "The Channel": TheChannelTheater, + "Syria": SyriaTheater, + } + theater = theaters[data["theater"]] + t = theater() + cps = {} + for p in data["player_points"]: + cp = t.add_json_cp(theater, p) + cp.captured = True + cps[p["id"]] = cp + t.add_controlpoint(cp) - theaters = { - "Caucasus": CaucasusTheater, - "Nevada": NevadaTheater, - "Persian Gulf": PersianGulfTheater, - "Normandy": NormandyTheater, - "The Channel": TheChannelTheater, - "Syria": SyriaTheater, - } - theater = theaters[json_data["theater"]] - t = theater() - cps = {} + for p in data["enemy_points"]: + cp = t.add_json_cp(theater, p) + cps[p["id"]] = cp + t.add_controlpoint(cp) - for p in json_data["player_points"]: - cp = t.add_json_cp(theater, p) - cp.captured = True - cps[p["id"]] = cp - t.add_controlpoint(cp) + for l in data["links"]: + cps[l[0]].connect(cps[l[1]]) + cps[l[1]].connect(cps[l[0]]) - for p in json_data["enemy_points"]: - cp = t.add_json_cp(theater, p) - cps[p["id"]] = cp - t.add_controlpoint(cp) - - for l in json_data["links"]: - cps[l[0]].connect(cps[l[1]]) - cps[l[1]].connect(cps[l[0]]) - - return t + return t class CaucasusTheater(ConflictTheater):