diff --git a/changelog.md b/changelog.md index 33a77730..59ee06e0 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ Saves from 2.5 are not compatible with 2.6. * **[Campaign]** Ground units can now be transferred by road. See https://github.com/Khopa/dcs_liberation/wiki/Unit-Transfers for more information. * **[Campaign]** Ground units can no longer be sold. To move units to a new location, transfer them. +* **[UI]** Campaigns generated for an older or newer version of the game will now be marked as incompatible. They can still be played, but bugs may be present. ## Fixes diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py index 80194189..07c17aa9 100644 --- a/game/theater/conflicttheater.py +++ b/game/theater/conflicttheater.py @@ -84,6 +84,15 @@ def pairwise(iterable): class MizCampaignLoader: + #: The latest version of the campaign format. Increment this version whenever all + #: existing campaigns should be flagged as incompatible in the UI. We will still + #: attempt to load old campaigns, but this provides a warning to the user that the + #: campaign may not work correctly. + #: + #: There is no verification that the campaign author updated their campaign + #: correctly, this is just a UI hint. + VERSION = 1 + BLUE_COUNTRY = CombinedJointTaskForcesBlue() RED_COUNTRY = CombinedJointTaskForcesRed() diff --git a/qt_ui/windows/newgame/QCampaignList.py b/qt_ui/windows/newgame/QCampaignList.py index b97f2ef0..44ed7175 100644 --- a/qt_ui/windows/newgame/QCampaignList.py +++ b/qt_ui/windows/newgame/QCampaignList.py @@ -4,7 +4,7 @@ import json import logging from dataclasses import dataclass from pathlib import Path -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Optional, Union from PySide2 import QtGui from PySide2.QtCore import QItemSelectionModel @@ -12,7 +12,7 @@ from PySide2.QtGui import QStandardItem, QStandardItemModel from PySide2.QtWidgets import QAbstractItemView, QListView import qt_ui.uiconstants as CONST -from game.theater import ConflictTheater +from game.theater import ConflictTheater, MizCampaignLoader PERF_FRIENDLY = 0 PERF_MEDIUM = 1 @@ -26,6 +26,12 @@ class Campaign: icon_name: str authors: str description: str + + #: The revision of the campaign format the campaign was built for. We do not attempt + #: to migrate old campaigns, but this is used to show a warning in the UI when + #: selecting a campaign that is not up to date. + version: int + recommended_player_faction: str recommended_enemy_faction: str performance: Union[PERF_FRIENDLY, PERF_MEDIUM, PERF_HARD, PERF_NASA] @@ -43,6 +49,7 @@ class Campaign: f"Terrain_{sanitized_theater}", data.get("authors", "???"), data.get("description", ""), + data.get("version", 0), data.get("recommended_player_faction", "USA 2005"), data.get("recommended_enemy_faction", "Russia 1990"), data.get("performance", 0), @@ -53,6 +60,27 @@ class Campaign: def load_theater(self) -> ConflictTheater: return ConflictTheater.from_json(self.path.parent, self.data) + @property + def is_out_of_date(self) -> bool: + """Returns True if this campaign is not up to date with the latest format.""" + return self.version < MizCampaignLoader.VERSION + + @property + def is_from_future(self) -> bool: + """Returns True if this campaign is newer than the supported format.""" + return self.version > MizCampaignLoader.VERSION + + @property + def is_compatible(self) -> bool: + """Returns True is this campaign was built for this version of the game.""" + if not self.version: + return False + if self.is_out_of_date: + return False + if self.is_from_future: + return False + return True + def load_campaigns() -> List[Campaign]: campaign_dir = Path("resources\\campaigns") @@ -73,7 +101,11 @@ class QCampaignItem(QStandardItem): super(QCampaignItem, self).__init__() self.setIcon(QtGui.QIcon(CONST.ICONS[campaign.icon_name])) self.setEditable(False) - self.setText(campaign.name) + if campaign.is_compatible: + name = campaign.name + else: + name = f"[INCOMPATIBLE] {campaign.name}" + self.setText(name) class QCampaignList(QListView): diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index 0ff28d59..c3cac473 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -321,7 +321,7 @@ class TheaterConfiguration(QtWidgets.QWizardPage): # Faction description self.campaignMapDescription = QTextEdit("") self.campaignMapDescription.setReadOnly(True) - self.campaignMapDescription.setMaximumHeight(100) + self.campaignMapDescription.setMaximumHeight(200) self.performanceText = QTextEdit("") self.performanceText.setReadOnly(True) diff --git a/resources/ui/templates/campaigntemplate_EN.j2 b/resources/ui/templates/campaigntemplate_EN.j2 index 2ec97f6e..6cd913c7 100644 --- a/resources/ui/templates/campaigntemplate_EN.j2 +++ b/resources/ui/templates/campaigntemplate_EN.j2 @@ -1,8 +1,24 @@ -Author(s): {{ campaign.authors }} -

+

Author(s): {{ campaign.authors }}

-Default factions: - {{campaign.recommended_player_faction}} VS  {{campaign.recommended_enemy_faction}} -
+{% if not campaign.version %} +

This campaign was created for an unknown version +of the game.

+

You can still attempt to play this campaign but there may be game breaking +{% elif campaign.is_out_of_date %} +

This campaign was created for an older version +of the game.

+

You can still attempt to play this campaign but there may be game breaking +bugs.

+{% elif campaign.is_from_future %} +

This campaign was created for a newer version +of the game.

+

You can still attempt to play this campaign but there may be game breaking +bugs.

+{% else %} +

This campaign is up to date.

+{% endif %} -{{ campaign.description|safe }} +

Default factions:

+

{{campaign.recommended_player_faction}} VS  {{campaign.recommended_enemy_faction}}

+ +{{ campaign.description|safe }} \ No newline at end of file