1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

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
This commit is contained in:
Ivan Savenko
2025-07-26 19:41:39 +03:00
parent 7fcb0246fb
commit 1f9a1dbf37
10 changed files with 48 additions and 28 deletions

View File

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

View File

@@ -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": {

View File

@@ -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" },

View File

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

View File

@@ -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)
{
for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_SCHOOL, cnf))
sources.emplace_back(bonus->sid);
});
bool tomesGrantBannedSpells = cb->getSettings().getBoolean(EGameSettings::SPELLS_TOMES_GRANT_BANNED_SPELLS);
for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_LEVEL, BonusCustomSubtype::spellLevel(spell->getLevel())))
sources.emplace_back(bonus->sid);
if (tomesGrantBannedSpells || cb->isAllowed(spellId))
{
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);
}
return sources;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,11 +1034,9 @@ 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!
{
tmh.movePoints = h->movementPointsRemaining() >= cost