1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00
Files
vcmi/lib/GameSettings.cpp
Ivan Savenko 1f9a1dbf37 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
2025-07-26 19:44:00 +03:00

198 lines
13 KiB
C++

/*
* GameSettings.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "GameSettings.h"
#include "json/JsonUtils.h"
VCMI_LIB_NAMESPACE_BEGIN
bool IGameSettings::getBoolean(EGameSettings option) const
{
return getValue(option).Bool();
}
int64_t IGameSettings::getInteger(EGameSettings option) const
{
return getValue(option).Integer();
}
double IGameSettings::getDouble(EGameSettings option) const
{
return getValue(option).Float();
}
std::vector<int> IGameSettings::getVector(EGameSettings option) const
{
return getValue(option).convertTo<std::vector<int>>();
}
int IGameSettings::getVectorValue(EGameSettings option, size_t index) const
{
return getValue(option)[index].Integer();
}
GameSettings::GameSettings() = default;
GameSettings::~GameSettings() = default;
const std::vector<GameSettings::SettingOption> GameSettings::settingProperties = {
{EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" },
{EGameSettings::BONUSES_GLOBAL, "bonuses", "global" },
{EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" },
{EGameSettings::COMBAT_ABILITY_BIAS, "combat", "abilityBias" },
{EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, "combat", "defensePointDamageFactor" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, "combat", "defensePointDamageFactorCap" },
{EGameSettings::COMBAT_GOOD_MORALE_CHANCE, "combat", "goodMoraleChance" },
{EGameSettings::COMBAT_BAD_MORALE_CHANCE, "combat", "badMoraleChance" },
{EGameSettings::COMBAT_MORALE_DICE_SIZE, "combat", "moraleDiceSize" },
{EGameSettings::COMBAT_MORALE_BIAS, "combat", "moraleBias" },
{EGameSettings::COMBAT_GOOD_LUCK_CHANCE, "combat", "goodLuckChance" },
{EGameSettings::COMBAT_BAD_LUCK_CHANCE, "combat", "badLuckChance" },
{EGameSettings::COMBAT_LUCK_DICE_SIZE, "combat", "luckDiceSize" },
{EGameSettings::COMBAT_LUCK_BIAS, "combat", "luckBias" },
{EGameSettings::COMBAT_LAYOUTS, "combat", "layouts" },
{EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, "combat", "oneHexTriggersObstacles" },
{EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, "creatures", "allowAllForDoubleMonth" },
{EGameSettings::CREATURES_ALLOW_JOINING_FOR_FREE, "creatures", "allowJoiningForFree" },
{EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS, "creatures", "allowRandomSpecialWeeks" },
{EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE, "creatures", "dailyStackExperience" },
{EGameSettings::CREATURES_JOINING_PERCENTAGE, "creatures", "joiningPercentage" },
{EGameSettings::CREATURES_WEEKLY_GROWTH_CAP, "creatures", "weeklyGrowthCap" },
{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT, "creatures", "weeklyGrowthPercent" },
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL, "dwellings", "accumulateWhenNeutral" },
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED, "dwellings", "accumulateWhenOwned" },
{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT, "dwellings", "mergeOnRecruit" },
{EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" },
{EGameSettings::HEROES_BASE_SCOUNTING_RANGE, "heroes", "baseScoutingRange" },
{EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, "heroes", "minimalPrimarySkills" },
{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP, "heroes", "perPlayerOnMapCap" },
{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP, "heroes", "perPlayerTotalCap" },
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
{EGameSettings::HEROES_MOVEMENT_COST_BASE, "heroes", "movementCostBase" },
{EGameSettings::HEROES_MOVEMENT_POINTS_LAND, "heroes", "movementPointsLand" },
{EGameSettings::HEROES_MOVEMENT_POINTS_SEA, "heroes", "movementPointsSea" },
{EGameSettings::HEROES_SKILL_PER_HERO, "heroes", "skillPerHero" },
{EGameSettings::HEROES_SPECIALTY_CREATURE_GROWTH, "heroes", "specialtyCreatureGrowth" },
{EGameSettings::HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH, "heroes", "specialtySecondarySkillGrowth" },
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
{EGameSettings::MAP_FORMAT_IN_THE_WAKE_OF_GODS, "mapFormat", "inTheWakeOfGods" },
{EGameSettings::MAP_FORMAT_JSON_VCMI, "mapFormat", "jsonVCMI" },
{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA, "mapFormat", "restorationOfErathia" },
{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH, "mapFormat", "shadowOfDeath" },
{EGameSettings::MAP_OBJECTS_H3_BUG_QUEST_TAKES_ENTIRE_ARMY, "mapObjects","h3BugQuestTakesEntireArmy" },
{EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD, "markets", "blackMarketRestockPeriod" },
{EGameSettings::MODULE_COMMANDERS, "modules", "commanders" },
{EGameSettings::MODULE_STACK_ARTIFACT, "modules", "stackArtifact" },
{EGameSettings::MODULE_STACK_EXPERIENCE, "modules", "stackExperience" },
{EGameSettings::PATHFINDER_IGNORE_GUARDS, "pathfinder", "ignoreGuards" },
{EGameSettings::PATHFINDER_ORIGINAL_FLY_RULES, "pathfinder", "originalFlyRules" },
{EGameSettings::PATHFINDER_USE_BOAT, "pathfinder", "useBoat" },
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_RANDOM, "pathfinder", "useMonolithOneWayRandom" },
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_UNIQUE, "pathfinder", "useMonolithOneWayUnique" },
{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" },
{EGameSettings::TEXTS_HERO, "textData", "hero" },
{EGameSettings::TEXTS_HERO_CLASS, "textData", "heroClass" },
{EGameSettings::TEXTS_OBJECT, "textData", "object" },
{EGameSettings::TEXTS_RIVER, "textData", "river" },
{EGameSettings::TEXTS_ROAD, "textData", "road" },
{EGameSettings::TEXTS_SPELL, "textData", "spell" },
{EGameSettings::TEXTS_TERRAIN, "textData", "terrain" },
{EGameSettings::TOWNS_BASE_SCOUNTING_RANGE, "towns", "baseScoutingRange" },
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" },
{EGameSettings::TOWNS_SPELL_RESEARCH, "towns", "spellResearch" },
{EGameSettings::TOWNS_SPELL_RESEARCH_COST, "towns", "spellResearchCost" },
{EGameSettings::TOWNS_SPELL_RESEARCH_PER_DAY, "towns", "spellResearchPerDay" },
{EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH, "towns", "spellResearchCostExponentPerResearch" },
{EGameSettings::INTERFACE_PLAYER_COLORED_BACKGROUND, "interface", "playerColoredBackground" },
};
void GameSettings::loadBase(const JsonNode & input)
{
JsonUtils::validate(input, "vcmi:gameSettings", input.getModScope());
for(const auto & option : settingProperties)
{
const JsonNode & optionValue = input[option.group][option.key];
size_t index = static_cast<size_t>(option.setting);
if(optionValue.isNull())
continue;
JsonUtils::mergeCopy(baseSettings[index], optionValue);
}
actualSettings = baseSettings;
}
void GameSettings::loadOverrides(const JsonNode & input)
{
for(const auto & option : settingProperties)
{
const JsonNode & optionValue = input[option.group][option.key];
if (!optionValue.isNull())
addOverride(option.setting, optionValue);
}
}
void GameSettings::addOverride(EGameSettings option, const JsonNode & input)
{
size_t index = static_cast<size_t>(option);
overridenSettings[index] = input;
JsonNode newValue = baseSettings[index];
JsonUtils::mergeCopy(newValue, input);
actualSettings[index] = newValue;
}
const JsonNode & GameSettings::getValue(EGameSettings option) const
{
auto index = static_cast<size_t>(option);
assert(!actualSettings.at(index).isNull());
return actualSettings.at(index);
}
JsonNode GameSettings::getFullConfig() const
{
JsonNode result;
for(const auto & option : settingProperties)
result[option.group][option.key] = getValue(option.setting);
return result;
}
JsonNode GameSettings::getAllOverrides() const
{
JsonNode result;
for(const auto & option : settingProperties)
{
const JsonNode & value = overridenSettings[static_cast<int32_t>(option.setting)];
if (!value.isNull())
result[option.group][option.key] = value;
}
return result;
}
VCMI_LIB_NAMESPACE_END