From b50ebba1bab55a41ef5de374d5fc51d072280546 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 21 Oct 2023 23:55:20 +0300 Subject: [PATCH] Added mod type "Compatibility" that is hidden in launcher --- config/schemas/mod.json | 2 +- docs/modders/Mod_File_Format.md | 5 +++-- launcher/modManager/cmodlist.cpp | 12 ++++++++++-- launcher/modManager/cmodlist.h | 6 ++++-- launcher/modManager/cmodlistmodel_moc.cpp | 7 ++++++- launcher/modManager/cmodlistview_moc.cpp | 8 ++++---- launcher/modManager/cmodmanager.cpp | 4 ++-- lib/modding/CModHandler.cpp | 4 ++-- lib/modding/CModInfo.cpp | 24 ++++++++++------------- lib/modding/CModInfo.h | 1 - 10 files changed, 42 insertions(+), 31 deletions(-) diff --git a/config/schemas/mod.json b/config/schemas/mod.json index 48b2f0680..36bc7baa8 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -55,7 +55,7 @@ }, "modType" : { "type" : "string", - "enum" : [ "Translation", "Town", "Test", "Templates", "Spells", "Music", "Sounds", "Skills", "Other", "Objects", "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Artifacts", "AI" ], + "enum" : [ "Translation", "Town", "Test", "Templates", "Spells", "Music", "Maps", "Sounds", "Skills", "Other", "Objects", "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Compatibility", "Artifacts", "AI" ], "description" : "Type of mod, e.g. Town, Artifacts, Graphical." }, "author" : { diff --git a/docs/modders/Mod_File_Format.md b/docs/modders/Mod_File_Format.md index 900980457..868003564 100644 --- a/docs/modders/Mod_File_Format.md +++ b/docs/modders/Mod_File_Format.md @@ -30,11 +30,12 @@ "version" : "1.2.3" // Type of mod, list of all possible values: - // "Translation", "Town", "Test", "Templates", "Spells", "Music", "Sounds", "Skills", "Other", "Objects", - // "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Artifacts", "AI" + // "Translation", "Town", "Test", "Templates", "Spells", "Music", "Maps", "Sounds", "Skills", "Other", "Objects", + // "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Compatibility", "Artifacts", "AI" // // Some mod types have additional effects on your mod: // Translation: mod of this type is only active if player uses base language of this mod. See "language" property. + // Compatibility: mods of this type are hidden in UI and will be automatically activated if all mod dependencies are active. Intended to be used to provide compatibility patches between mods "modType" : "Graphical", // Base language of the mod, before applying localizations. By default vcmi assumes English diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index b6cf4cc07..ee6cbda59 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -90,14 +90,22 @@ bool CModEntry::isInstalled() const return !localData.isEmpty(); } -bool CModEntry::isValid() const +bool CModEntry::isVisible() const { + if (getBaseValue("modType").toString() == "Compatibility" && isSubmod()) + return false; + return !localData.isEmpty() || !repository.isEmpty(); } bool CModEntry::isTranslation() const { - return getBaseValue("modType").toString().toLower() == "translation"; + return getBaseValue("modType").toString() == "Translation"; +} + +bool CModEntry::isSubmod() const +{ + return getName().contains('.'); } int CModEntry::getModStatus() const diff --git a/launcher/modManager/cmodlist.h b/launcher/modManager/cmodlist.h index 4143ed4b8..c82a0b88c 100644 --- a/launcher/modManager/cmodlist.h +++ b/launcher/modManager/cmodlist.h @@ -60,10 +60,12 @@ public: bool isEssential() const; // checks if verison is compatible with vcmi bool isCompatible() const; - // returns if has any data - bool isValid() const; + // returns true if mod should be visible in Launcher + bool isVisible() const; // installed and enabled bool isTranslation() const; + // returns true if this is a submod + bool isSubmod() const; // see ModStatus enum int getModStatus() const; diff --git a/launcher/modManager/cmodlistmodel_moc.cpp b/launcher/modManager/cmodlistmodel_moc.cpp index 9bde9860e..cece26b53 100644 --- a/launcher/modManager/cmodlistmodel_moc.cpp +++ b/launcher/modManager/cmodlistmodel_moc.cpp @@ -45,6 +45,7 @@ QString CModListModel::modTypeName(QString modTypeID) const {"Templates", tr("Templates") }, {"Spells", tr("Spells") }, {"Music", tr("Music") }, + {"Maps", tr("Maps") }, {"Sounds", tr("Sounds") }, {"Skills", tr("Skills") }, {"Other", tr("Other") }, @@ -58,6 +59,7 @@ QString CModListModel::modTypeName(QString modTypeID) const {"Graphical", tr("Graphical") }, {"Expansion", tr("Expansion") }, {"Creatures", tr("Creatures") }, + {"Compatibility", tr("Compatibility") }, {"Artifacts", tr("Artifacts") }, {"AI", tr("AI") }, }; @@ -257,7 +259,6 @@ bool CModFilterModel::filterMatchesThis(const QModelIndex & source) const { CModEntry mod = base->getMod(source.data(ModRoles::ModNameRole).toString()); return (mod.getModStatus() & filterMask) == filteredType && - mod.isValid() && QSortFilterProxyModel::filterAcceptsRow(source.row(), source.parent()); } @@ -265,6 +266,10 @@ bool CModFilterModel::filterAcceptsRow(int source_row, const QModelIndex & sourc { QModelIndex index = base->index(source_row, 0, source_parent); + CModEntry mod = base->getMod(index.data(ModRoles::ModNameRole).toString()); + if (!mod.isVisible()) + return false; + if(filterMatchesThis(index)) { return true; diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index f4e8c7be5..0e713bfda 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -332,7 +332,7 @@ QString CModListView::genModInfoText(CModEntry & mod) if(mod.isInstalled()) notes += replaceIfNotEmpty(getModNames(findDependentMods(mod.getName(), false)), listTemplate.arg(hasDependentMods)); - if(mod.getName().contains('.')) + if(mod.isSubmod()) notes += noteTemplate.arg(thisIsSubmod); if(notes.size()) @@ -374,8 +374,8 @@ void CModListView::selectMod(const QModelIndex & index) ui->disableButton->setVisible(mod.isEnabled()); ui->enableButton->setVisible(mod.isDisabled()); - ui->installButton->setVisible(mod.isAvailable() && !mod.getName().contains('.')); - ui->uninstallButton->setVisible(mod.isInstalled() && !mod.getName().contains('.')); + ui->installButton->setVisible(mod.isAvailable() && !mod.isSubmod()); + ui->uninstallButton->setVisible(mod.isInstalled() && !mod.isSubmod()); ui->updateButton->setVisible(mod.isUpdateable()); // Block buttons if action is not allowed at this time @@ -921,7 +921,7 @@ void CModListView::on_allModsView_doubleClicked(const QModelIndex &index) bool hasBlockingMods = !findBlockingMods(modName).empty(); bool hasDependentMods = !findDependentMods(modName, true).empty(); - if(!hasInvalidDeps && mod.isAvailable() && !mod.getName().contains('.')) + if(!hasInvalidDeps && mod.isAvailable() && !mod.isSubmod()) { on_installButton_clicked(); return; diff --git a/launcher/modManager/cmodmanager.cpp b/launcher/modManager/cmodmanager.cpp index 4e0c37e59..8e9ed4ee3 100644 --- a/launcher/modManager/cmodmanager.cpp +++ b/launcher/modManager/cmodmanager.cpp @@ -154,7 +154,7 @@ bool CModManager::canInstallMod(QString modname) { auto mod = modList->getMod(modname); - if(mod.getName().contains('.')) + if(mod.isSubmod()) return addError(modname, "Can not install submod"); if(mod.isInstalled()) @@ -169,7 +169,7 @@ bool CModManager::canUninstallMod(QString modname) { auto mod = modList->getMod(modname); - if(mod.getName().contains('.')) + if(mod.isSubmod()) return addError(modname, "Can not uninstall submod"); if(!mod.isInstalled()) diff --git a/lib/modding/CModHandler.cpp b/lib/modding/CModHandler.cpp index 1414817f5..829ce6077 100644 --- a/lib/modding/CModHandler.cpp +++ b/lib/modding/CModHandler.cpp @@ -128,8 +128,8 @@ std::vector CModHandler::validateAndSortDependencies(std::vector error("Mod '%s' will not work: it depends on mod '%s', which is not installed.", brokenMod.getVerificationInfo().name, dependency); + if(!vstd::contains(resolvedModIDs, dependency) && brokenMod.config["modType"].String() != "Compatibility") + logMod->error("Mod '%s' has been disabled: dependency '%s' is missing.", brokenMod.getVerificationInfo().name, dependency); } } return sortedValidMods; diff --git a/lib/modding/CModInfo.cpp b/lib/modding/CModInfo.cpp index d90ce08ae..4c665de1e 100644 --- a/lib/modding/CModInfo.cpp +++ b/lib/modding/CModInfo.cpp @@ -32,7 +32,6 @@ CModInfo::CModInfo(): CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config): identifier(identifier), - description(config["description"].String()), dependencies(config["depends"].convertTo>()), conflicts(config["conflicts"].convertTo>()), explicitlyEnabled(false), @@ -45,7 +44,7 @@ CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const verificationInfo.parent = identifier.substr(0, identifier.find_last_of('.')); if(verificationInfo.parent == identifier) verificationInfo.parent.clear(); - + if(!config["compatibility"].isNull()) { vcmiCompatibleMin = CModVersion::fromString(config["compatibility"]["min"].String()); @@ -98,11 +97,7 @@ void CModInfo::loadLocalData(const JsonNode & data) implicitlyEnabled = true; explicitlyEnabled = !config["keepDisabled"].Bool(); verificationInfo.checksum = 0; - if (data.getType() == JsonNode::JsonType::DATA_BOOL) - { - explicitlyEnabled = data.Bool(); - } - if (data.getType() == JsonNode::JsonType::DATA_STRUCT) + if (data.isStruct()) { explicitlyEnabled = data["active"].Bool(); validated = data["validated"].Bool(); @@ -116,7 +111,7 @@ void CModInfo::loadLocalData(const JsonNode & data) if(!implicitlyEnabled) logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", verificationInfo.name); - if (boost::iequals(config["modType"].String(), "translation")) // compatibility code - mods use "Translation" type at the moment + if (config["modType"].String() == "Translation") { if (baseLanguage != VLC->generaltexth->getPreferredLanguage()) { @@ -124,12 +119,18 @@ void CModInfo::loadLocalData(const JsonNode & data) implicitlyEnabled = false; } } + if (config["modType"].String() == "Compatibility") + { + // compatibility mods are always explicitly enabled + // however they may be implicitly disabled - if one of their dependencies is missing + explicitlyEnabled = true; + } if (isEnabled()) validation = validated ? PASSED : PENDING; else validation = validated ? PASSED : FAILED; - + verificationInfo.impactsGameplay = checkModGameplayAffecting(); } @@ -185,9 +186,4 @@ bool CModInfo::isEnabled() const return implicitlyEnabled && explicitlyEnabled; } -void CModInfo::setEnabled(bool on) -{ - explicitlyEnabled = on; -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/modding/CModInfo.h b/lib/modding/CModInfo.h index 7469e1f19..f9f227e2a 100644 --- a/lib/modding/CModInfo.h +++ b/lib/modding/CModInfo.h @@ -87,7 +87,6 @@ public: void updateChecksum(ui32 newChecksum); bool isEnabled() const; - void setEnabled(bool on); static std::string getModDir(const std::string & name); static JsonPath getModFile(const std::string & name);