Browse Source

Added mod type "Compatibility" that is hidden in launcher

Ivan Savenko 2 years ago
parent
commit
b50ebba1ba

+ 1 - 1
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" : {

+ 3 - 2
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

+ 10 - 2
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

+ 4 - 2
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;

+ 6 - 1
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;

+ 4 - 4
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;

+ 2 - 2
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())

+ 2 - 2
lib/modding/CModHandler.cpp

@@ -128,8 +128,8 @@ std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModI
 		const CModInfo & brokenMod = allMods.at(brokenModID);
 		for(const TModID & dependency : brokenMod.dependencies)
 		{
-			if(!vstd::contains(resolvedModIDs, dependency))
-				logMod->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;

+ 10 - 14
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<std::set<std::string>>()),
 	conflicts(config["conflicts"].convertTo<std::set<std::string>>()),
 	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

+ 0 - 1
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);