From 39135f8c8017bf41de298c8d82365c2b6675e0be Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Sun, 18 Apr 2021 17:30:49 -0700 Subject: [PATCH] Add version field to campaign descriptor file. This is used to provide a UI hint to guide players towards campaigns that have been updated to work with the current version of the game. All the campaigns we currently have were made for an unknown version of the game, so they're all flagged as incompatible. The version field is not the DCS Liberation version number because the campaign format may change multiple times during development. Instead the version number is a monotonically increasing integer that we increment whenever a game change requires campaign updates. --- changelog.md | 1 + game/theater/conflicttheater.py | 9 +++++ qt_ui/windows/newgame/QCampaignList.py | 38 +++++++++++++++++-- qt_ui/windows/newgame/QNewGameWizard.py | 2 +- resources/ui/templates/campaigntemplate_EN.j2 | 28 +++++++++++--- 5 files changed, 68 insertions(+), 10 deletions(-) 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