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.
This commit is contained in:
Dan Albert 2021-04-18 17:30:49 -07:00
parent 5e054cfc77
commit 39135f8c80
5 changed files with 68 additions and 10 deletions

View File

@ -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

View File

@ -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()

View File

@ -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):

View File

@ -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)

View File

@ -1,8 +1,24 @@
<strong>Author(s): {{ campaign.authors }}</strong>
<br/><br/>
<p><strong>Author(s): {{ campaign.authors }}</strong></p>
<strong>Default factions:</strong>
<span style="color:#82A466">&nbsp;{{campaign.recommended_player_faction}}</span> VS <span style="color:orange">&nbsp;{{campaign.recommended_enemy_faction}}</span>
<br/>
{% if not campaign.version %}
<p><strong style="color:orange">This campaign was created for an unknown version
of the game.</strong></p>
<p>You can still attempt to play this campaign but there may be game breaking
{% elif campaign.is_out_of_date %}
<p><strong style="color:orange">This campaign was created for an older version
of the game.</strong></p>
<p>You can still attempt to play this campaign but there may be game breaking
bugs.</p>
{% elif campaign.is_from_future %}
<p><strong style="color:orange">This campaign was created for a newer version
of the game.</strong></p>
<p>You can still attempt to play this campaign but there may be game breaking
bugs.</p>
{% else %}
<p><strong style="color:#82A466">This campaign is up to date.</strong></p>
{% endif %}
{{ campaign.description|safe }}
<p><strong>Default factions:</strong></p>
<p><span style="color:#82A466">{{campaign.recommended_player_faction}}</span> VS <span style="color:orange">&nbsp;{{campaign.recommended_enemy_faction}}</span></p>
{{ campaign.description|safe }}