Bläddra i källkod

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 månader sedan
förälder
incheckning
1f9a1dbf37

+ 15 - 9
config/gameConfig.json

@@ -154,6 +154,11 @@
 			"restorationOfErathia" : { 
 				"supported" : true,
 				"iconIndex" : 0,
+				"settings": {
+					"spells": {
+						"tomesGrantBannedSpells" : true
+					}
+				},
 				"buildingsCommon": {
 					"townHall"       : 0,
 					"cityHall"       : 1,
@@ -476,7 +481,12 @@
 				"iconIndex" : 3
 			},
 			"hornOfTheAbyss" : { 
-				"supported" : false
+				"supported" : false,
+				"settings": {
+					"spells": {
+						"tomesGrantBannedSpells" : false
+					}
+				}
 			},
 			"inTheWakeOfGods" : { 
 				"supported" : false
@@ -750,16 +760,12 @@
 
 		"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
 			"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" : 

+ 1 - 4
config/schemas/gameSettings.json

@@ -167,11 +167,8 @@
 			"type" : "object",
 			"additionalProperties" : false,
 			"properties" : {
-				"dimensionDoorOnlyToUncoveredTiles" : { "type" : "boolean" },
-				"dimensionDoorExposesTerrainType" :   { "type" : "boolean" },
-				"dimensionDoorFailureSpendsPoints" :  { "type" : "boolean" },
 				"dimensionDoorTriggersGuards" :       { "type" : "boolean" },
-				"dimensionDoorTournamentRulesLimit" : { "type" : "boolean" }
+				"tomesGrantBannedSpells" : { "type" : "boolean" }
 			}
 		},
 		"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_WEEKLY_GROWTH_CAP,                      "creatures", "weeklyGrowthCap"                      },
 		{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_OWNED,                  "dwellings", "accumulateWhenOwned"                  },
 		{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_WHIRLPOOL,                         "pathfinder", "useWhirlpool"                        },
 		{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_CREATURE,                                   "textData",  "creature"                             },
 		{EGameSettings::TEXTS_FACTION,                                    "textData",  "faction"                              },

+ 2 - 1
lib/IGameSettings.h

@@ -41,7 +41,6 @@ enum class EGameSettings
 	CREATURES_JOINING_PERCENTAGE,
 	CREATURES_WEEKLY_GROWTH_CAP,
 	CREATURES_WEEKLY_GROWTH_PERCENT,
-	DIMENSION_DOOR_TRIGGERS_GUARDS,
 	DWELLINGS_ACCUMULATE_WHEN_NEUTRAL,
 	DWELLINGS_ACCUMULATE_WHEN_OWNED,
 	DWELLINGS_MERGE_ON_RECRUIT,
@@ -80,6 +79,8 @@ enum class EGameSettings
 	PATHFINDER_USE_MONOLITH_TWO_WAY,
 	PATHFINDER_USE_WHIRLPOOL,
 	RESOURCES_WEEKLY_BONUSES_AI,
+	SPELLS_DIMENSION_DOOR_TRIGGERS_GUARDS,
+	SPELLS_TOMES_GRANT_BANNED_SPELLS,
 	TEXTS_ARTIFACT,
 	TEXTS_CREATURE,
 	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))
 		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;
 }

+ 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());
 		}
 	}
+
+	const MapIdentifiersH3M & identifierMapper = LIBRARY->mapFormat->getMapping(mapHeader->version);
+	map->overrideGameSettings(identifierMapper.getFormatSettings());
 }
 
 void CMapLoaderH3M::readAllowedArtifacts()

+ 5 - 0
lib/mapping/MapIdentifiersH3M.cpp

@@ -15,6 +15,7 @@
 #include "../entities/faction/CFaction.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../filesystem/Filesystem.h"
+#include "../json/JsonUtils.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjects/ObjectTemplate.h"
@@ -39,6 +40,10 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping)
 	if (!mapping["supported"].Bool())
 		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())
 	{
 		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 "../filesystem/ResourcePath.h"
+#include "../json/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class JsonNode;
 class ObjectTemplate;
 
 struct ObjectTypeIdentifier
@@ -50,6 +50,8 @@ class DLL_LINKAGE MapIdentifiersH3M
 	std::map<AnimationPath, AnimationPath> mappingObjectTemplate;
 	std::map<ObjectTypeIdentifier, ObjectTypeIdentifier> mappingObjectIndex;
 
+	JsonNode formatSettings;
+
 	template<typename IdentifierID>
 	void loadMapping(std::map<IdentifierID, IdentifierID> & result, const JsonNode & mapping, const std::string & identifierName);
 public:
@@ -70,6 +72,8 @@ public:
 	SecondarySkill remap(SecondarySkill input) const;
 	CampaignRegionID remap(CampaignRegionID input) const;
 
+	const JsonNode & getFormatSettings() const { return formatSettings; }
+
 };
 
 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
 {
-	if(!cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS))
+	if(!cb->getSettings().getBoolean(EGameSettings::SPELLS_DIMENSION_DOOR_TRIGGERS_GUARDS))
 		return cursor;
 
 	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
 			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
 			: IGNORE_GUARDS;
 
@@ -1034,10 +1034,8 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 			if (const auto * town = dynamic_cast<const CGTownInstance *>(objectToVisit))
 				objectVisited(town, h);
 		}
-
 		return true;
 	}
-	
 
 	//still here? it is standard movement!
 	{