浏览代码

Fix artifacts providing hero with banned spells

Tomes of X Spells and Spellbinder's Hat (and any other sources for such
bonuses from mods) will no longer provide spells that are banned on map.

Option is only active for random maps and for HotA h3m's. RoE-SoD .h3m's
work as before.

If needed, behavior can be changed in config
Ivan Savenko 2 月之前
父节点
当前提交
1f9a1dbf37

+ 15 - 9
config/gameConfig.json

@@ -154,6 +154,11 @@
 			"restorationOfErathia" : { 
 			"restorationOfErathia" : { 
 				"supported" : true,
 				"supported" : true,
 				"iconIndex" : 0,
 				"iconIndex" : 0,
+				"settings": {
+					"spells": {
+						"tomesGrantBannedSpells" : true
+					}
+				},
 				"buildingsCommon": {
 				"buildingsCommon": {
 					"townHall"       : 0,
 					"townHall"       : 0,
 					"cityHall"       : 1,
 					"cityHall"       : 1,
@@ -476,7 +481,12 @@
 				"iconIndex" : 3
 				"iconIndex" : 3
 			},
 			},
 			"hornOfTheAbyss" : { 
 			"hornOfTheAbyss" : { 
-				"supported" : false
+				"supported" : false,
+				"settings": {
+					"spells": {
+						"tomesGrantBannedSpells" : false
+					}
+				}
 			},
 			},
 			"inTheWakeOfGods" : { 
 			"inTheWakeOfGods" : { 
 				"supported" : false
 				"supported" : false
@@ -750,16 +760,12 @@
 
 
 		"spells":
 		"spells":
 		{
 		{
-			// if enabled, dimension work doesn't work into tiles under Fog of War
-			"dimensionDoorOnlyToUncoveredTiles" : false,
-			// if enabled, dimension door will hint regarding tile being incompatible terrain type, unlike H3 (water/land)
-			"dimensionDoorExposesTerrainType" : false,
-			// if enabled, attempt to use dimension door on incompatible terrain (water/land) will result in spending of mana, movement and casts per day (H3 behavior)
-			"dimensionDoorFailureSpendsPoints" : true,
 			// if enabled, dimension door will initiate a fight upon landing on tile adjacent to neutral creature
 			// if enabled, dimension door will initiate a fight upon landing on tile adjacent to neutral creature
 			"dimensionDoorTriggersGuards" : false,
 			"dimensionDoorTriggersGuards" : false,
-			// if enabled, dimension door can be used 1x per day, exception being 2x per day for XL+U or bigger maps (41472 tiles) + hero having expert air magic
-			"dimensionDoorTournamentRulesLimit" : false
+			// if enabled, SPELLS_OF_SCHOOL bonus (Tomes of Water/Air/Earth/Fire Magic) 
+			// and SPELLS_OF_LEVEL bonus (Spellbinder Hat) will grant spells that are banned on map (SoD behavior)
+			// NOTE: this value only affects random maps and .vmap's. For h3m's value is loaded from map format configuration
+			"tomesGrantBannedSpells" : false
 		},
 		},
 		
 		
 		"bonuses" : 
 		"bonuses" : 

+ 1 - 4
config/schemas/gameSettings.json

@@ -167,11 +167,8 @@
 			"type" : "object",
 			"type" : "object",
 			"additionalProperties" : false,
 			"additionalProperties" : false,
 			"properties" : {
 			"properties" : {
-				"dimensionDoorOnlyToUncoveredTiles" : { "type" : "boolean" },
-				"dimensionDoorExposesTerrainType" :   { "type" : "boolean" },
-				"dimensionDoorFailureSpendsPoints" :  { "type" : "boolean" },
 				"dimensionDoorTriggersGuards" :       { "type" : "boolean" },
 				"dimensionDoorTriggersGuards" :       { "type" : "boolean" },
-				"dimensionDoorTournamentRulesLimit" : { "type" : "boolean" }
+				"tomesGrantBannedSpells" : { "type" : "boolean" }
 			}
 			}
 		},
 		},
 		"bonuses": {
 		"bonuses": {

+ 2 - 1
lib/GameSettings.cpp

@@ -68,7 +68,6 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
 		{EGameSettings::CREATURES_JOINING_PERCENTAGE,                     "creatures", "joiningPercentage"                    },
 		{EGameSettings::CREATURES_JOINING_PERCENTAGE,                     "creatures", "joiningPercentage"                    },
 		{EGameSettings::CREATURES_WEEKLY_GROWTH_CAP,                      "creatures", "weeklyGrowthCap"                      },
 		{EGameSettings::CREATURES_WEEKLY_GROWTH_CAP,                      "creatures", "weeklyGrowthCap"                      },
 		{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT,                  "creatures", "weeklyGrowthPercent"                  },
 		{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT,                  "creatures", "weeklyGrowthPercent"                  },
-		{EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS,                   "spells",    "dimensionDoorTriggersGuards"          },
 		{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL,                "dwellings", "accumulateWhenNeutral"                },
 		{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL,                "dwellings", "accumulateWhenNeutral"                },
 		{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED,                  "dwellings", "accumulateWhenOwned"                  },
 		{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED,                  "dwellings", "accumulateWhenOwned"                  },
 		{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT,                       "dwellings", "mergeOnRecruit"                       },
 		{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT,                       "dwellings", "mergeOnRecruit"                       },
@@ -106,6 +105,8 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
 		{EGameSettings::PATHFINDER_USE_MONOLITH_TWO_WAY,                  "pathfinder", "useMonolithTwoWay"                   },
 		{EGameSettings::PATHFINDER_USE_MONOLITH_TWO_WAY,                  "pathfinder", "useMonolithTwoWay"                   },
 		{EGameSettings::PATHFINDER_USE_WHIRLPOOL,                         "pathfinder", "useWhirlpool"                        },
 		{EGameSettings::PATHFINDER_USE_WHIRLPOOL,                         "pathfinder", "useWhirlpool"                        },
 		{EGameSettings::RESOURCES_WEEKLY_BONUSES_AI,                      "resources", "weeklyBonusesAI"                      },
 		{EGameSettings::RESOURCES_WEEKLY_BONUSES_AI,                      "resources", "weeklyBonusesAI"                      },
+		{EGameSettings::SPELLS_DIMENSION_DOOR_TRIGGERS_GUARDS,            "spells",    "dimensionDoorTriggersGuards"          },
+		{EGameSettings::SPELLS_TOMES_GRANT_BANNED_SPELLS,                 "spells",    "tomesGrantBannedSpells"               },
 		{EGameSettings::TEXTS_ARTIFACT,                                   "textData",  "artifact"                             },
 		{EGameSettings::TEXTS_ARTIFACT,                                   "textData",  "artifact"                             },
 		{EGameSettings::TEXTS_CREATURE,                                   "textData",  "creature"                             },
 		{EGameSettings::TEXTS_CREATURE,                                   "textData",  "creature"                             },
 		{EGameSettings::TEXTS_FACTION,                                    "textData",  "faction"                              },
 		{EGameSettings::TEXTS_FACTION,                                    "textData",  "faction"                              },

+ 2 - 1
lib/IGameSettings.h

@@ -41,7 +41,6 @@ enum class EGameSettings
 	CREATURES_JOINING_PERCENTAGE,
 	CREATURES_JOINING_PERCENTAGE,
 	CREATURES_WEEKLY_GROWTH_CAP,
 	CREATURES_WEEKLY_GROWTH_CAP,
 	CREATURES_WEEKLY_GROWTH_PERCENT,
 	CREATURES_WEEKLY_GROWTH_PERCENT,
-	DIMENSION_DOOR_TRIGGERS_GUARDS,
 	DWELLINGS_ACCUMULATE_WHEN_NEUTRAL,
 	DWELLINGS_ACCUMULATE_WHEN_NEUTRAL,
 	DWELLINGS_ACCUMULATE_WHEN_OWNED,
 	DWELLINGS_ACCUMULATE_WHEN_OWNED,
 	DWELLINGS_MERGE_ON_RECRUIT,
 	DWELLINGS_MERGE_ON_RECRUIT,
@@ -80,6 +79,8 @@ enum class EGameSettings
 	PATHFINDER_USE_MONOLITH_TWO_WAY,
 	PATHFINDER_USE_MONOLITH_TWO_WAY,
 	PATHFINDER_USE_WHIRLPOOL,
 	PATHFINDER_USE_WHIRLPOOL,
 	RESOURCES_WEEKLY_BONUSES_AI,
 	RESOURCES_WEEKLY_BONUSES_AI,
+	SPELLS_DIMENSION_DOOR_TRIGGERS_GUARDS,
+	SPELLS_TOMES_GRANT_BANNED_SPELLS,
 	TEXTS_ARTIFACT,
 	TEXTS_ARTIFACT,
 	TEXTS_CREATURE,
 	TEXTS_CREATURE,
 	TEXTS_FACTION,
 	TEXTS_FACTION,

+ 12 - 7
lib/mapObjects/CGHeroInstance.cpp

@@ -1250,15 +1250,20 @@ std::vector<BonusSourceID> CGHeroInstance::getSourcesForSpell(const SpellID & sp
 	for(const auto & bonus : *getBonusesOfType(BonusType::SPELL, spellId))
 	for(const auto & bonus : *getBonusesOfType(BonusType::SPELL, spellId))
 		sources.emplace_back(bonus->sid);
 		sources.emplace_back(bonus->sid);
 
 
-	const auto spell = spellId.toSpell();
-	spell->forEachSchool([this, &sources](const SpellSchool & cnf, bool & stop)
+	bool tomesGrantBannedSpells = cb->getSettings().getBoolean(EGameSettings::SPELLS_TOMES_GRANT_BANNED_SPELLS);
+
+	if (tomesGrantBannedSpells || cb->isAllowed(spellId))
 	{
 	{
-		for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_SCHOOL, cnf))
-			sources.emplace_back(bonus->sid);
-	});
+		const auto spell = spellId.toSpell();
+		spell->forEachSchool([this, &sources](const SpellSchool & cnf, bool & stop)
+		{
+			for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_SCHOOL, cnf))
+				sources.emplace_back(bonus->sid);
+		});
 
 
-	for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_LEVEL, BonusCustomSubtype::spellLevel(spell->getLevel())))
-		sources.emplace_back(bonus->sid);
+		for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_LEVEL, BonusCustomSubtype::spellLevel(spell->getLevel())))
+			sources.emplace_back(bonus->sid);
+	}
 
 
 	return sources;
 	return sources;
 }
 }

+ 3 - 0
lib/mapping/MapFormatH3M.cpp

@@ -743,6 +743,9 @@ void CMapLoaderH3M::readMapOptions()
 				logGlobal->warn("Map '%s': option to ban hero recruitment for %s is not implemented!!", mapName, PlayerColor(i).toString());
 				logGlobal->warn("Map '%s': option to ban hero recruitment for %s is not implemented!!", mapName, PlayerColor(i).toString());
 		}
 		}
 	}
 	}
+
+	const MapIdentifiersH3M & identifierMapper = LIBRARY->mapFormat->getMapping(mapHeader->version);
+	map->overrideGameSettings(identifierMapper.getFormatSettings());
 }
 }
 
 
 void CMapLoaderH3M::readAllowedArtifacts()
 void CMapLoaderH3M::readAllowedArtifacts()

+ 5 - 0
lib/mapping/MapIdentifiersH3M.cpp

@@ -15,6 +15,7 @@
 #include "../entities/faction/CFaction.h"
 #include "../entities/faction/CFaction.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../filesystem/Filesystem.h"
 #include "../filesystem/Filesystem.h"
+#include "../json/JsonUtils.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjects/ObjectTemplate.h"
 #include "../mapObjects/ObjectTemplate.h"
@@ -39,6 +40,10 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping)
 	if (!mapping["supported"].Bool())
 	if (!mapping["supported"].Bool())
 		throw std::runtime_error("Unsupported map format!");
 		throw std::runtime_error("Unsupported map format!");
 
 
+	formatSettings.Struct(); // change type
+	if (!mapping["settings"].isNull())
+		JsonUtils::inherit(formatSettings, mapping["settings"]);
+
 	for (auto entryFaction : mapping["buildings"].Struct())
 	for (auto entryFaction : mapping["buildings"].Struct())
 	{
 	{
 		FactionID factionID (*LIBRARY->identifiers()->getIdentifier(entryFaction.second.getModScope(), "faction", entryFaction.first));
 		FactionID factionID (*LIBRARY->identifiers()->getIdentifier(entryFaction.second.getModScope(), "faction", entryFaction.first));

+ 5 - 1
lib/mapping/MapIdentifiersH3M.h

@@ -12,10 +12,10 @@
 
 
 #include "../GameConstants.h"
 #include "../GameConstants.h"
 #include "../filesystem/ResourcePath.h"
 #include "../filesystem/ResourcePath.h"
+#include "../json/JsonNode.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
-class JsonNode;
 class ObjectTemplate;
 class ObjectTemplate;
 
 
 struct ObjectTypeIdentifier
 struct ObjectTypeIdentifier
@@ -50,6 +50,8 @@ class DLL_LINKAGE MapIdentifiersH3M
 	std::map<AnimationPath, AnimationPath> mappingObjectTemplate;
 	std::map<AnimationPath, AnimationPath> mappingObjectTemplate;
 	std::map<ObjectTypeIdentifier, ObjectTypeIdentifier> mappingObjectIndex;
 	std::map<ObjectTypeIdentifier, ObjectTypeIdentifier> mappingObjectIndex;
 
 
+	JsonNode formatSettings;
+
 	template<typename IdentifierID>
 	template<typename IdentifierID>
 	void loadMapping(std::map<IdentifierID, IdentifierID> & result, const JsonNode & mapping, const std::string & identifierName);
 	void loadMapping(std::map<IdentifierID, IdentifierID> & result, const JsonNode & mapping, const std::string & identifierName);
 public:
 public:
@@ -70,6 +72,8 @@ public:
 	SecondarySkill remap(SecondarySkill input) const;
 	SecondarySkill remap(SecondarySkill input) const;
 	CampaignRegionID remap(CampaignRegionID input) const;
 	CampaignRegionID remap(CampaignRegionID input) const;
 
 
+	const JsonNode & getFormatSettings() const { return formatSettings; }
+
 };
 };
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/spells/adventure/DimensionDoorEffect.cpp

@@ -34,7 +34,7 @@ DimensionDoorEffect::DimensionDoorEffect(const CSpell * s, const JsonNode & conf
 
 
 std::string DimensionDoorEffect::getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
 std::string DimensionDoorEffect::getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
 {
 {
-	if(!cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS))
+	if(!cb->getSettings().getBoolean(EGameSettings::SPELLS_DIMENSION_DOOR_TRIGGERS_GUARDS))
 		return cursor;
 		return cursor;
 
 
 	if (!exposeFow && !cb->isVisibleFor(pos, caster->getCasterOwner()))
 	if (!exposeFow && !cb->isVisibleFor(pos, caster->getCasterOwner()))

+ 1 - 3
server/CGameHandler.cpp

@@ -1021,7 +1021,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		if (blockingVisit()) // e.g. hero on the other side of teleporter
 		if (blockingVisit()) // e.g. hero on the other side of teleporter
 			return true;
 			return true;
 
 
-		EGuardLook guardsCheck = (gameInfo().getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS) && movementMode == EMovementMode::DIMENSION_DOOR)
+		EGuardLook guardsCheck = (gameInfo().getSettings().getBoolean(EGameSettings::SPELLS_DIMENSION_DOOR_TRIGGERS_GUARDS) && movementMode == EMovementMode::DIMENSION_DOOR)
 			? CHECK_FOR_GUARDS
 			? CHECK_FOR_GUARDS
 			: IGNORE_GUARDS;
 			: IGNORE_GUARDS;
 
 
@@ -1034,10 +1034,8 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 			if (const auto * town = dynamic_cast<const CGTownInstance *>(objectToVisit))
 			if (const auto * town = dynamic_cast<const CGTownInstance *>(objectToVisit))
 				objectVisited(town, h);
 				objectVisited(town, h);
 		}
 		}
-
 		return true;
 		return true;
 	}
 	}
-	
 
 
 	//still here? it is standard movement!
 	//still here? it is standard movement!
 	{
 	{