mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-18 03:21:27 +02:00
vcmi: split CBonusSystemNode, BonusParams and prop
More splitting of HeroBonus.h
This commit is contained in:
parent
713e3004df
commit
1d34c73c2d
@ -22,8 +22,10 @@
|
|||||||
#include "../../lib/CGameState.h"
|
#include "../../lib/CGameState.h"
|
||||||
#include "../../lib/NetPacksBase.h"
|
#include "../../lib/NetPacksBase.h"
|
||||||
#include "../../lib/NetPacks.h"
|
#include "../../lib/NetPacks.h"
|
||||||
|
#include "../../lib/bonuses/CBonusSystemNode.h"
|
||||||
#include "../../lib/bonuses/Limiters.h"
|
#include "../../lib/bonuses/Limiters.h"
|
||||||
#include "../../lib/bonuses/Updaters.h"
|
#include "../../lib/bonuses/Updaters.h"
|
||||||
|
#include "../../lib/bonuses/Propagators.h"
|
||||||
#include "../../lib/serializer/CTypeList.h"
|
#include "../../lib/serializer/CTypeList.h"
|
||||||
#include "../../lib/serializer/BinarySerializer.h"
|
#include "../../lib/serializer/BinarySerializer.h"
|
||||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||||
|
@ -43,8 +43,10 @@
|
|||||||
#include "../lib/CArtHandler.h"
|
#include "../lib/CArtHandler.h"
|
||||||
#include "../lib/CGeneralTextHandler.h"
|
#include "../lib/CGeneralTextHandler.h"
|
||||||
#include "../lib/CHeroHandler.h"
|
#include "../lib/CHeroHandler.h"
|
||||||
|
#include "../lib/bonuses/CBonusSystemNode.h"
|
||||||
#include "../lib/bonuses/Limiters.h"
|
#include "../lib/bonuses/Limiters.h"
|
||||||
#include "../lib/bonuses/Updaters.h"
|
#include "../lib/bonuses/Updaters.h"
|
||||||
|
#include "../lib/bonuses/Propagators.h"
|
||||||
#include "../lib/serializer/CTypeList.h"
|
#include "../lib/serializer/CTypeList.h"
|
||||||
#include "../lib/serializer/BinaryDeserializer.h"
|
#include "../lib/serializer/BinaryDeserializer.h"
|
||||||
#include "../lib/serializer/BinarySerializer.h"
|
#include "../lib/serializer/BinarySerializer.h"
|
||||||
|
@ -27,9 +27,12 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
|||||||
${MAIN_LIB_DIR}/battle/SiegeInfo.cpp
|
${MAIN_LIB_DIR}/battle/SiegeInfo.cpp
|
||||||
${MAIN_LIB_DIR}/battle/Unit.cpp
|
${MAIN_LIB_DIR}/battle/Unit.cpp
|
||||||
|
|
||||||
|
${MAIN_LIB_DIR}/bonuses/BonusParams.cpp
|
||||||
${MAIN_LIB_DIR}/bonuses/CBonusProxy.cpp
|
${MAIN_LIB_DIR}/bonuses/CBonusProxy.cpp
|
||||||
|
${MAIN_LIB_DIR}/bonuses/CBonusSystemNode.cpp
|
||||||
${MAIN_LIB_DIR}/bonuses/HeroBonus.cpp
|
${MAIN_LIB_DIR}/bonuses/HeroBonus.cpp
|
||||||
${MAIN_LIB_DIR}/bonuses/Limiters.cpp
|
${MAIN_LIB_DIR}/bonuses/Limiters.cpp
|
||||||
|
${MAIN_LIB_DIR}/bonuses/Propagators.cpp
|
||||||
${MAIN_LIB_DIR}/bonuses/Updaters.cpp
|
${MAIN_LIB_DIR}/bonuses/Updaters.cpp
|
||||||
|
|
||||||
${MAIN_LIB_DIR}/events/ApplyDamage.cpp
|
${MAIN_LIB_DIR}/events/ApplyDamage.cpp
|
||||||
@ -304,9 +307,12 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
|||||||
${MAIN_LIB_DIR}/battle/SiegeInfo.h
|
${MAIN_LIB_DIR}/battle/SiegeInfo.h
|
||||||
${MAIN_LIB_DIR}/battle/Unit.h
|
${MAIN_LIB_DIR}/battle/Unit.h
|
||||||
|
|
||||||
|
${MAIN_LIB_DIR}/bonuses/BonusParams.h
|
||||||
${MAIN_LIB_DIR}/bonuses/CBonusProxy.h
|
${MAIN_LIB_DIR}/bonuses/CBonusProxy.h
|
||||||
|
${MAIN_LIB_DIR}/bonuses/CBonusSystemNode.h
|
||||||
${MAIN_LIB_DIR}/bonuses/HeroBonus.h
|
${MAIN_LIB_DIR}/bonuses/HeroBonus.h
|
||||||
${MAIN_LIB_DIR}/bonuses/Limiters.h
|
${MAIN_LIB_DIR}/bonuses/Limiters.h
|
||||||
|
${MAIN_LIB_DIR}/bonuses/Propagators.h
|
||||||
${MAIN_LIB_DIR}/bonuses/Updaters.h
|
${MAIN_LIB_DIR}/bonuses/Updaters.h
|
||||||
|
|
||||||
${MAIN_LIB_DIR}/events/ApplyDamage.h
|
${MAIN_LIB_DIR}/events/ApplyDamage.h
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <vcmi/ArtifactService.h>
|
#include <vcmi/ArtifactService.h>
|
||||||
|
|
||||||
#include "bonuses/HeroBonus.h"
|
#include "bonuses/HeroBonus.h"
|
||||||
|
#include "bonuses/CBonusSystemNode.h"
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
#include "IHandlerBase.h"
|
#include "IHandlerBase.h"
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "bonuses/HeroBonus.h"
|
#include "bonuses/HeroBonus.h"
|
||||||
|
#include "bonuses/CBonusSystemNode.h"
|
||||||
#include "ConstTransitivePtr.h"
|
#include "ConstTransitivePtr.h"
|
||||||
#include "ResourceSet.h"
|
#include "ResourceSet.h"
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "bonuses/HeroBonus.h"
|
#include "bonuses/HeroBonus.h"
|
||||||
|
#include "bonuses/CBonusSystemNode.h"
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
#include "CArtHandler.h"
|
#include "CArtHandler.h"
|
||||||
#include "CCreatureHandler.h"
|
#include "CCreatureHandler.h"
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <vcmi/Team.h>
|
#include <vcmi/Team.h>
|
||||||
|
|
||||||
#include "bonuses/HeroBonus.h"
|
#include "bonuses/HeroBonus.h"
|
||||||
|
#include "bonuses/CBonusSystemNode.h"
|
||||||
#include "ResourceSet.h"
|
#include "ResourceSet.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "JsonNode.h"
|
#include "JsonNode.h"
|
||||||
#include "bonuses/HeroBonus.h"
|
#include "bonuses/HeroBonus.h"
|
||||||
|
#include "bonuses/CBonusSystemNode.h"
|
||||||
#include "CCreatureHandler.h" //todo: remove
|
#include "CCreatureHandler.h" //todo: remove
|
||||||
#include "battle/BattleHex.h"
|
#include "battle/BattleHex.h"
|
||||||
#include "mapObjects/CGHeroInstance.h" // for commander serialization
|
#include "mapObjects/CGHeroInstance.h" // for commander serialization
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "mapObjects/CObjectClassesHandler.h"
|
#include "mapObjects/CObjectClassesHandler.h"
|
||||||
#include "mapObjects/CObjectHandler.h"
|
#include "mapObjects/CObjectHandler.h"
|
||||||
#include "bonuses/HeroBonus.h"
|
#include "bonuses/HeroBonus.h"
|
||||||
|
#include "bonuses/Propagators.h"
|
||||||
#include "ResourceSet.h"
|
#include "ResourceSet.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
#include "CModHandler.h"
|
#include "CModHandler.h"
|
||||||
#include "BattleFieldHandler.h"
|
#include "BattleFieldHandler.h"
|
||||||
#include "ObstacleHandler.h"
|
#include "ObstacleHandler.h"
|
||||||
|
#include "bonuses/CBonusSystemNode.h"
|
||||||
#include "bonuses/Limiters.h"
|
#include "bonuses/Limiters.h"
|
||||||
|
#include "bonuses/Propagators.h"
|
||||||
#include "bonuses/Updaters.h"
|
#include "bonuses/Updaters.h"
|
||||||
|
|
||||||
#include "serializer/CSerializer.h" // for SAVEGAME_MAGIC
|
#include "serializer/CSerializer.h" // for SAVEGAME_MAGIC
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
|
|
||||||
#include "ScopeGuard.h"
|
#include "ScopeGuard.h"
|
||||||
|
|
||||||
|
#include "bonuses/BonusParams.h"
|
||||||
#include "bonuses/HeroBonus.h"
|
#include "bonuses/HeroBonus.h"
|
||||||
#include "bonuses/Limiters.h"
|
#include "bonuses/Limiters.h"
|
||||||
|
#include "bonuses/Propagators.h"
|
||||||
#include "bonuses/Updaters.h"
|
#include "bonuses/Updaters.h"
|
||||||
#include "filesystem/Filesystem.h"
|
#include "filesystem/Filesystem.h"
|
||||||
#include "VCMI_Lib.h" //for identifier resolution
|
#include "VCMI_Lib.h" //for identifier resolution
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "../int3.h"
|
#include "../int3.h"
|
||||||
#include "../bonuses/HeroBonus.h"
|
#include "../bonuses/HeroBonus.h"
|
||||||
|
#include "../bonuses/CBonusSystemNode.h"
|
||||||
#include "CBattleInfoCallback.h"
|
#include "CBattleInfoCallback.h"
|
||||||
#include "IBattleState.h"
|
#include "IBattleState.h"
|
||||||
#include "SiegeInfo.h"
|
#include "SiegeInfo.h"
|
||||||
|
259
lib/bonuses/BonusParams.cpp
Normal file
259
lib/bonuses/BonusParams.cpp
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* BonusParams.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 "BonusParams.h"
|
||||||
|
|
||||||
|
#include "../ResourceSet.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
|
||||||
|
isConverted(true)
|
||||||
|
{
|
||||||
|
if(deprecatedTypeStr == "SECONDARY_SKILL_PREMY" || deprecatedTypeStr == "SPECIAL_SECONDARY_SKILL")
|
||||||
|
{
|
||||||
|
if(deprecatedSubtype == SecondarySkill::PATHFINDING || deprecatedSubtypeStr == "skill.pathfinding")
|
||||||
|
type = Bonus::ROUGH_TERRAIN_DISCOUNT;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::DIPLOMACY || deprecatedSubtypeStr == "skill.diplomacy")
|
||||||
|
type = Bonus::WANDERING_CREATURES_JOIN_BONUS;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::WISDOM || deprecatedSubtypeStr == "skill.wisdom")
|
||||||
|
type = Bonus::MAX_LEARNABLE_SPELL_LEVEL;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::MYSTICISM || deprecatedSubtypeStr == "skill.mysticism")
|
||||||
|
type = Bonus::MANA_REGENERATION;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::NECROMANCY || deprecatedSubtypeStr == "skill.necromancy")
|
||||||
|
type = Bonus::UNDEAD_RAISE_PERCENTAGE;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::LEARNING || deprecatedSubtypeStr == "skill.learning")
|
||||||
|
type = Bonus::HERO_EXPERIENCE_GAIN_PERCENT;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::RESISTANCE || deprecatedSubtypeStr == "skill.resistance")
|
||||||
|
type = Bonus::MAGIC_RESISTANCE;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
|
||||||
|
type = Bonus::LEARN_BATTLE_SPELL_CHANCE;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::SCOUTING || deprecatedSubtypeStr == "skill.scouting")
|
||||||
|
type = Bonus::SIGHT_RADIUS;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::INTELLIGENCE || deprecatedSubtypeStr == "skill.intelligence")
|
||||||
|
{
|
||||||
|
type = Bonus::MANA_PER_KNOWLEDGE;
|
||||||
|
valueType = Bonus::PERCENT_TO_BASE;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
|
||||||
|
type = Bonus::SPELL_DAMAGE;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
|
||||||
|
type = Bonus::LEARN_MEETING_SPELL_LIMIT;
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
|
||||||
|
{
|
||||||
|
subtype = 1;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
|
||||||
|
{
|
||||||
|
subtype = 0;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
|
||||||
|
{
|
||||||
|
subtype = -1;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
type = Bonus::GENERAL_DAMAGE_REDUCTION;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
|
||||||
|
{
|
||||||
|
subtype = 0;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
valueType = Bonus::PERCENT_TO_BASE;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
type = Bonus::MOVEMENT;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
|
||||||
|
{
|
||||||
|
subtype = 1;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
valueType = Bonus::PERCENT_TO_BASE;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
type = Bonus::MOVEMENT;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
|
||||||
|
{
|
||||||
|
type = Bonus::GENERATE_RESOURCE;
|
||||||
|
subtype = GameResID(EGameResID::GOLD);
|
||||||
|
subtypeRelevant = true;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
|
||||||
|
{
|
||||||
|
type = Bonus::MAGIC_SCHOOL_SKILL;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtype = 4;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
|
||||||
|
{
|
||||||
|
type = Bonus::MAGIC_SCHOOL_SKILL;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtype = 1;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
|
||||||
|
{
|
||||||
|
type = Bonus::MAGIC_SCHOOL_SKILL;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtype = 2;
|
||||||
|
}
|
||||||
|
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
|
||||||
|
{
|
||||||
|
type = Bonus::MAGIC_SCHOOL_SKILL;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtype = 8;
|
||||||
|
}
|
||||||
|
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
||||||
|
{
|
||||||
|
type = Bonus::BONUS_DAMAGE_CHANCE;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtypeStr = "core:creature.ballista";
|
||||||
|
}
|
||||||
|
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
|
||||||
|
{
|
||||||
|
type = Bonus::SPECIFIC_SPELL_POWER;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtypeStr = "core:spell.firstAid";
|
||||||
|
}
|
||||||
|
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
|
||||||
|
{
|
||||||
|
type = Bonus::CATAPULT_EXTRA_SHOTS;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtypeStr = "core:spell.catapultShot";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isConverted = false;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "SECONDARY_SKILL_VAL2")
|
||||||
|
{
|
||||||
|
if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
|
||||||
|
type = Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT;
|
||||||
|
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
||||||
|
{
|
||||||
|
type = Bonus::HERO_GRANTS_ATTACKS;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
subtypeStr = "core:creature.ballista";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isConverted = false;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "SEA_MOVEMENT")
|
||||||
|
{
|
||||||
|
subtype = 0;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
valueType = Bonus::ADDITIVE_VALUE;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
type = Bonus::MOVEMENT;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "LAND_MOVEMENT")
|
||||||
|
{
|
||||||
|
subtype = 1;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
valueType = Bonus::ADDITIVE_VALUE;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
type = Bonus::MOVEMENT;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "MAXED_SPELL")
|
||||||
|
{
|
||||||
|
type = Bonus::SPELL;
|
||||||
|
subtypeStr = deprecatedSubtypeStr;
|
||||||
|
subtypeRelevant = true;
|
||||||
|
valueType = Bonus::INDEPENDENT_MAX;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
val = 3;
|
||||||
|
valRelevant = true;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
|
||||||
|
{
|
||||||
|
type = Bonus::HP_REGENERATION;
|
||||||
|
val = 100000; //very high value to always chose stack health
|
||||||
|
valRelevant = true;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "KING1")
|
||||||
|
{
|
||||||
|
type = Bonus::KING;
|
||||||
|
val = 0;
|
||||||
|
valRelevant = true;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "KING2")
|
||||||
|
{
|
||||||
|
type = Bonus::KING;
|
||||||
|
val = 2;
|
||||||
|
valRelevant = true;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "KING3")
|
||||||
|
{
|
||||||
|
type = Bonus::KING;
|
||||||
|
val = 3;
|
||||||
|
valRelevant = true;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
|
||||||
|
type = Bonus::SIGHT_RADIUS;
|
||||||
|
else if (deprecatedTypeStr == "SELF_MORALE")
|
||||||
|
{
|
||||||
|
type = Bonus::MORALE;
|
||||||
|
val = 1;
|
||||||
|
valRelevant = true;
|
||||||
|
valueType = Bonus::INDEPENDENT_MAX;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "SELF_LUCK")
|
||||||
|
{
|
||||||
|
type = Bonus::LUCK;
|
||||||
|
val = 1;
|
||||||
|
valRelevant = true;
|
||||||
|
valueType = Bonus::INDEPENDENT_MAX;
|
||||||
|
valueTypeRelevant = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isConverted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const JsonNode & BonusParams::toJson()
|
||||||
|
{
|
||||||
|
assert(isConverted);
|
||||||
|
if(ret.isNull())
|
||||||
|
{
|
||||||
|
ret["type"].String() = vstd::findKey(bonusNameMap, type);
|
||||||
|
if(subtypeRelevant && !subtypeStr.empty())
|
||||||
|
ret["subtype"].String() = subtypeStr;
|
||||||
|
else if(subtypeRelevant)
|
||||||
|
ret["subtype"].Integer() = subtype;
|
||||||
|
if(valueTypeRelevant)
|
||||||
|
ret["valueType"].String() = vstd::findKey(bonusValueMap, valueType);
|
||||||
|
if(valRelevant)
|
||||||
|
ret["val"].Float() = val;
|
||||||
|
if(targetTypeRelevant)
|
||||||
|
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetType);
|
||||||
|
jsonCreated = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
CSelector BonusParams::toSelector()
|
||||||
|
{
|
||||||
|
assert(isConverted);
|
||||||
|
if(subtypeRelevant && !subtypeStr.empty())
|
||||||
|
JsonUtils::resolveIdentifier(subtype, toJson(), "subtype");
|
||||||
|
|
||||||
|
auto ret = Selector::type()(type);
|
||||||
|
if(subtypeRelevant)
|
||||||
|
ret = ret.And(Selector::subtype()(subtype));
|
||||||
|
if(valueTypeRelevant)
|
||||||
|
ret = ret.And(Selector::valueType(valueType));
|
||||||
|
if(targetTypeRelevant)
|
||||||
|
ret = ret.And(Selector::targetSourceType()(targetType));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
41
lib/bonuses/BonusParams.h
Normal file
41
lib/bonuses/BonusParams.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* BonusParams.h, 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "HeroBonus.h"
|
||||||
|
|
||||||
|
#include "../GameConstants.h"
|
||||||
|
#include "../JsonNode.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct DLL_LINKAGE BonusParams {
|
||||||
|
bool isConverted;
|
||||||
|
Bonus::BonusType type = Bonus::NONE;
|
||||||
|
TBonusSubtype subtype = -1;
|
||||||
|
std::string subtypeStr;
|
||||||
|
bool subtypeRelevant = false;
|
||||||
|
Bonus::ValueType valueType = Bonus::BASE_NUMBER;
|
||||||
|
bool valueTypeRelevant = false;
|
||||||
|
si32 val = 0;
|
||||||
|
bool valRelevant = false;
|
||||||
|
Bonus::BonusSource targetType = Bonus::SECONDARY_SKILL;
|
||||||
|
bool targetTypeRelevant = false;
|
||||||
|
|
||||||
|
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
|
||||||
|
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
|
||||||
|
const JsonNode & toJson();
|
||||||
|
CSelector toSelector();
|
||||||
|
private:
|
||||||
|
JsonNode ret;
|
||||||
|
bool jsonCreated = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
689
lib/bonuses/CBonusSystemNode.cpp
Normal file
689
lib/bonuses/CBonusSystemNode.cpp
Normal file
@ -0,0 +1,689 @@
|
|||||||
|
/*
|
||||||
|
* CBonusSystemNode.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 "CBonusSystemNode.h"
|
||||||
|
#include "Limiters.h"
|
||||||
|
#include "Updaters.h"
|
||||||
|
#include "Propagators.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
std::atomic<int64_t> CBonusSystemNode::treeChanged(1);
|
||||||
|
constexpr bool CBonusSystemNode::cachingEnabled = true;
|
||||||
|
|
||||||
|
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
|
||||||
|
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
|
||||||
|
|
||||||
|
PlayerColor CBonusSystemNode::retrieveNodeOwner(const CBonusSystemNode * node)
|
||||||
|
{
|
||||||
|
return node ? node->getOwner() : PlayerColor::CANNOT_DETERMINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
|
||||||
|
{
|
||||||
|
auto ret = bonuses.getFirst(selector);
|
||||||
|
if(ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
FOREACH_PARENT(pname)
|
||||||
|
{
|
||||||
|
ret = pname->getBonusLocalFirst(selector);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector) const
|
||||||
|
{
|
||||||
|
return (const_cast<CBonusSystemNode*>(this))->getBonusLocalFirst(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
|
||||||
|
{
|
||||||
|
for(const auto & elem : parents)
|
||||||
|
{
|
||||||
|
const CBonusSystemNode *parent = elem;
|
||||||
|
out.insert(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::getParents(TNodes &out)
|
||||||
|
{
|
||||||
|
for (auto & elem : parents)
|
||||||
|
{
|
||||||
|
const CBonusSystemNode *parent = elem;
|
||||||
|
out.insert(const_cast<CBonusSystemNode*>(parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::getAllParents(TCNodes & out) const //retrieves list of parent nodes (nodes to inherit bonuses from)
|
||||||
|
{
|
||||||
|
for(auto * parent : parents)
|
||||||
|
{
|
||||||
|
out.insert(parent);
|
||||||
|
parent->getAllParents(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::getAllBonusesRec(BonusList &out, const CSelector & selector) const
|
||||||
|
{
|
||||||
|
//out has been reserved sufficient capacity at getAllBonuses() call
|
||||||
|
|
||||||
|
BonusList beforeUpdate;
|
||||||
|
TCNodes lparents;
|
||||||
|
getAllParents(lparents);
|
||||||
|
|
||||||
|
if(!lparents.empty())
|
||||||
|
{
|
||||||
|
//estimate on how many bonuses are missing yet - must be positive
|
||||||
|
beforeUpdate.reserve(std::max(out.capacity() - out.size(), bonuses.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
beforeUpdate.reserve(bonuses.size()); //at most all local bonuses
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto * parent : lparents)
|
||||||
|
{
|
||||||
|
parent->getAllBonusesRec(beforeUpdate, selector);
|
||||||
|
}
|
||||||
|
bonuses.getAllBonuses(beforeUpdate);
|
||||||
|
|
||||||
|
for(const auto & b : beforeUpdate)
|
||||||
|
{
|
||||||
|
//We should not run updaters on non-selected bonuses
|
||||||
|
auto updated = selector(b.get()) && b->updater
|
||||||
|
? getUpdatedBonus(b, b->updater)
|
||||||
|
: b;
|
||||||
|
|
||||||
|
//do not add bonus with updater
|
||||||
|
bool bonusExists = false;
|
||||||
|
for(const auto & bonus : out)
|
||||||
|
{
|
||||||
|
if (bonus == updated)
|
||||||
|
bonusExists = true;
|
||||||
|
if (bonus->updater && bonus->updater == updated->updater)
|
||||||
|
bonusExists = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bonusExists)
|
||||||
|
out.push_back(updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root, const std::string &cachingStr) const
|
||||||
|
{
|
||||||
|
bool limitOnUs = (!root || root == this); //caching won't work when we want to limit bonuses against an external node
|
||||||
|
if (CBonusSystemNode::cachingEnabled && limitOnUs)
|
||||||
|
{
|
||||||
|
// Exclusive access for one thread
|
||||||
|
boost::lock_guard<boost::mutex> lock(sync);
|
||||||
|
|
||||||
|
// If the bonus system tree changes(state of a single node or the relations to each other) then
|
||||||
|
// cache all bonus objects. Selector objects doesn't matter.
|
||||||
|
if (cachedLast != treeChanged)
|
||||||
|
{
|
||||||
|
BonusList allBonuses;
|
||||||
|
allBonuses.reserve(cachedBonuses.capacity()); //we assume we'll get about the same number of bonuses
|
||||||
|
|
||||||
|
cachedBonuses.clear();
|
||||||
|
cachedRequests.clear();
|
||||||
|
|
||||||
|
getAllBonusesRec(allBonuses, Selector::all);
|
||||||
|
limitBonuses(allBonuses, cachedBonuses);
|
||||||
|
cachedBonuses.stackBonuses();
|
||||||
|
|
||||||
|
cachedLast = treeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a bonus system request comes with a caching string then look up in the map if there are any
|
||||||
|
// pre-calculated bonus results. Limiters can't be cached so they have to be calculated.
|
||||||
|
if(!cachingStr.empty())
|
||||||
|
{
|
||||||
|
auto it = cachedRequests.find(cachingStr);
|
||||||
|
if(it != cachedRequests.end())
|
||||||
|
{
|
||||||
|
//Cached list contains bonuses for our query with applied limiters
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//We still don't have the bonuses (didn't returned them from cache)
|
||||||
|
//Perform bonus selection
|
||||||
|
auto ret = std::make_shared<BonusList>();
|
||||||
|
cachedBonuses.getBonuses(*ret, selector, limit);
|
||||||
|
|
||||||
|
// Save the results in the cache
|
||||||
|
if(!cachingStr.empty())
|
||||||
|
cachedRequests[cachingStr] = ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getAllBonusesWithoutCaching(selector, limit, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root) const
|
||||||
|
{
|
||||||
|
auto ret = std::make_shared<BonusList>();
|
||||||
|
|
||||||
|
// Get bonus results without caching enabled.
|
||||||
|
BonusList beforeLimiting;
|
||||||
|
BonusList afterLimiting;
|
||||||
|
getAllBonusesRec(beforeLimiting, selector);
|
||||||
|
|
||||||
|
if(!root || root == this)
|
||||||
|
{
|
||||||
|
limitBonuses(beforeLimiting, afterLimiting);
|
||||||
|
}
|
||||||
|
else if(root)
|
||||||
|
{
|
||||||
|
//We want to limit our query against an external node. We get all its bonuses,
|
||||||
|
// add the ones we're considering and see if they're cut out by limiters
|
||||||
|
BonusList rootBonuses;
|
||||||
|
BonusList limitedRootBonuses;
|
||||||
|
getAllBonusesRec(rootBonuses, selector);
|
||||||
|
|
||||||
|
for(const auto & b : beforeLimiting)
|
||||||
|
rootBonuses.push_back(b);
|
||||||
|
|
||||||
|
root->limitBonuses(rootBonuses, limitedRootBonuses);
|
||||||
|
|
||||||
|
for(const auto & b : beforeLimiting)
|
||||||
|
if(vstd::contains(limitedRootBonuses, b))
|
||||||
|
afterLimiting.push_back(b);
|
||||||
|
|
||||||
|
}
|
||||||
|
afterLimiting.getBonuses(*ret, selector, limit);
|
||||||
|
ret->stackBonuses();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Bonus> CBonusSystemNode::getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const
|
||||||
|
{
|
||||||
|
assert(updater);
|
||||||
|
return updater->createUpdatedBonus(b, * this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::CBonusSystemNode()
|
||||||
|
:CBonusSystemNode(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
|
||||||
|
bonuses(true),
|
||||||
|
exportedBonuses(true),
|
||||||
|
nodeType(UNKNOWN),
|
||||||
|
cachedLast(0),
|
||||||
|
isHypotheticNode(isHypotetic)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType):
|
||||||
|
bonuses(true),
|
||||||
|
exportedBonuses(true),
|
||||||
|
nodeType(NodeType),
|
||||||
|
cachedLast(0),
|
||||||
|
isHypotheticNode(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other) noexcept:
|
||||||
|
bonuses(std::move(other.bonuses)),
|
||||||
|
exportedBonuses(std::move(other.exportedBonuses)),
|
||||||
|
nodeType(other.nodeType),
|
||||||
|
description(other.description),
|
||||||
|
cachedLast(0),
|
||||||
|
isHypotheticNode(other.isHypotheticNode)
|
||||||
|
{
|
||||||
|
std::swap(parents, other.parents);
|
||||||
|
std::swap(children, other.children);
|
||||||
|
|
||||||
|
//fixing bonus tree without recalculation
|
||||||
|
|
||||||
|
if(!isHypothetic())
|
||||||
|
{
|
||||||
|
for(CBonusSystemNode * n : parents)
|
||||||
|
{
|
||||||
|
n->children -= &other;
|
||||||
|
n->children.push_back(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(CBonusSystemNode * n : children)
|
||||||
|
{
|
||||||
|
n->parents -= &other;
|
||||||
|
n->parents.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//cache ignored
|
||||||
|
|
||||||
|
//cachedBonuses
|
||||||
|
//cachedRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::~CBonusSystemNode()
|
||||||
|
{
|
||||||
|
detachFromAll();
|
||||||
|
|
||||||
|
if(!children.empty())
|
||||||
|
{
|
||||||
|
while(!children.empty())
|
||||||
|
children.front()->detachFrom(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
|
||||||
|
{
|
||||||
|
assert(!vstd::contains(parents, &parent));
|
||||||
|
parents.push_back(&parent);
|
||||||
|
|
||||||
|
if(!isHypothetic())
|
||||||
|
{
|
||||||
|
if(parent.actsAsBonusSourceOnly())
|
||||||
|
parent.newRedDescendant(*this);
|
||||||
|
else
|
||||||
|
newRedDescendant(parent);
|
||||||
|
|
||||||
|
parent.newChildAttached(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::treeHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
|
||||||
|
{
|
||||||
|
assert(vstd::contains(parents, &parent));
|
||||||
|
|
||||||
|
if(!isHypothetic())
|
||||||
|
{
|
||||||
|
if(parent.actsAsBonusSourceOnly())
|
||||||
|
parent.removedRedDescendant(*this);
|
||||||
|
else
|
||||||
|
removedRedDescendant(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vstd::contains(parents, &parent))
|
||||||
|
{
|
||||||
|
parents -= &parent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
|
||||||
|
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isHypothetic())
|
||||||
|
{
|
||||||
|
parent.childDetached(*this);
|
||||||
|
}
|
||||||
|
CBonusSystemNode::treeHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::removeBonusesRecursive(const CSelector & s)
|
||||||
|
{
|
||||||
|
removeBonuses(s);
|
||||||
|
for(CBonusSystemNode * child : children)
|
||||||
|
child->removeBonusesRecursive(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
|
||||||
|
{
|
||||||
|
BonusList bl;
|
||||||
|
exportedBonuses.getBonuses(bl, s, Selector::all);
|
||||||
|
for(const auto & b : bl)
|
||||||
|
{
|
||||||
|
b->turnsRemain--;
|
||||||
|
if(b->turnsRemain <= 0)
|
||||||
|
removeBonus(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(CBonusSystemNode *child : children)
|
||||||
|
child->reduceBonusDurations(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||||
|
{
|
||||||
|
//turnsRemain shouldn't be zero for following durations
|
||||||
|
if(Bonus::NTurns(b.get()) || Bonus::NDays(b.get()) || Bonus::OneWeek(b.get()))
|
||||||
|
{
|
||||||
|
assert(b->turnsRemain);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!vstd::contains(exportedBonuses, b));
|
||||||
|
exportedBonuses.push_back(b);
|
||||||
|
exportBonus(b);
|
||||||
|
CBonusSystemNode::treeHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::accumulateBonus(const std::shared_ptr<Bonus>& b)
|
||||||
|
{
|
||||||
|
auto bonus = exportedBonuses.getFirst(Selector::typeSubtype(b->type, b->subtype)); //only local bonuses are interesting //TODO: what about value type?
|
||||||
|
if(bonus)
|
||||||
|
bonus->val += b->val;
|
||||||
|
else
|
||||||
|
addNewBonus(std::make_shared<Bonus>(*b)); //duplicate needed, original may get destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::removeBonus(const std::shared_ptr<Bonus>& b)
|
||||||
|
{
|
||||||
|
exportedBonuses -= b;
|
||||||
|
if(b->propagator)
|
||||||
|
unpropagateBonus(b);
|
||||||
|
else
|
||||||
|
bonuses -= b;
|
||||||
|
CBonusSystemNode::treeHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::removeBonuses(const CSelector & selector)
|
||||||
|
{
|
||||||
|
BonusList toRemove;
|
||||||
|
exportedBonuses.getBonuses(toRemove, selector, Selector::all);
|
||||||
|
for(const auto & bonus : toRemove)
|
||||||
|
removeBonus(bonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBonusSystemNode::actsAsBonusSourceOnly() const
|
||||||
|
{
|
||||||
|
switch(nodeType)
|
||||||
|
{
|
||||||
|
case CREATURE:
|
||||||
|
case ARTIFACT:
|
||||||
|
case ARTIFACT_INSTANCE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source)
|
||||||
|
{
|
||||||
|
if(b->propagator->shouldBeAttached(this))
|
||||||
|
{
|
||||||
|
auto propagated = b->propagationUpdater
|
||||||
|
? source.getUpdatedBonus(b, b->propagationUpdater)
|
||||||
|
: b;
|
||||||
|
bonuses.push_back(propagated);
|
||||||
|
logBonus->trace("#$# %s #propagated to# %s", propagated->Description(), nodeName());
|
||||||
|
}
|
||||||
|
|
||||||
|
FOREACH_RED_CHILD(child)
|
||||||
|
child->propagateBonus(b, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
|
||||||
|
{
|
||||||
|
if(b->propagator->shouldBeAttached(this))
|
||||||
|
{
|
||||||
|
bonuses -= b;
|
||||||
|
logBonus->trace("#$# %s #is no longer propagated to# %s", b->Description(), nodeName());
|
||||||
|
}
|
||||||
|
|
||||||
|
FOREACH_RED_CHILD(child)
|
||||||
|
child->unpropagateBonus(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::newChildAttached(CBonusSystemNode & child)
|
||||||
|
{
|
||||||
|
assert(!vstd::contains(children, &child));
|
||||||
|
children.push_back(&child);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::childDetached(CBonusSystemNode & child)
|
||||||
|
{
|
||||||
|
if(vstd::contains(children, &child))
|
||||||
|
children -= &child;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logBonus->error("Error on Detach. Node %s (nodeType=%d) is not a child of %s (nodeType=%d)"
|
||||||
|
, child.nodeShortInfo(), child.nodeType, nodeShortInfo(), nodeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::detachFromAll()
|
||||||
|
{
|
||||||
|
while(!parents.empty())
|
||||||
|
detachFrom(*parents.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBonusSystemNode::isIndependentNode() const
|
||||||
|
{
|
||||||
|
return parents.empty() && children.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CBonusSystemNode::nodeName() const
|
||||||
|
{
|
||||||
|
return !description.empty()
|
||||||
|
? description
|
||||||
|
: std::string("Bonus system node of type ") + typeid(*this).name();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CBonusSystemNode::nodeShortInfo() const
|
||||||
|
{
|
||||||
|
std::ostringstream str;
|
||||||
|
str << "'" << typeid(* this).name() << "'";
|
||||||
|
description.length() > 0
|
||||||
|
? str << " (" << description << ")"
|
||||||
|
: str << " (no description)";
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::deserializationFix()
|
||||||
|
{
|
||||||
|
exportBonuses();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::getRedParents(TNodes & out)
|
||||||
|
{
|
||||||
|
FOREACH_PARENT(pname)
|
||||||
|
{
|
||||||
|
if(pname->actsAsBonusSourceOnly())
|
||||||
|
{
|
||||||
|
out.insert(pname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!actsAsBonusSourceOnly())
|
||||||
|
{
|
||||||
|
for(CBonusSystemNode *child : children)
|
||||||
|
{
|
||||||
|
out.insert(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::getRedChildren(TNodes &out)
|
||||||
|
{
|
||||||
|
FOREACH_PARENT(pname)
|
||||||
|
{
|
||||||
|
if(!pname->actsAsBonusSourceOnly())
|
||||||
|
{
|
||||||
|
out.insert(pname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(actsAsBonusSourceOnly())
|
||||||
|
{
|
||||||
|
for(CBonusSystemNode *child : children)
|
||||||
|
{
|
||||||
|
out.insert(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant)
|
||||||
|
{
|
||||||
|
for(const auto & b : exportedBonuses)
|
||||||
|
{
|
||||||
|
if(b->propagator)
|
||||||
|
descendant.propagateBonus(b, *this);
|
||||||
|
}
|
||||||
|
TNodes redParents;
|
||||||
|
getRedAncestors(redParents); //get all red parents recursively
|
||||||
|
|
||||||
|
for(auto * parent : redParents)
|
||||||
|
{
|
||||||
|
for(const auto & b : parent->exportedBonuses)
|
||||||
|
{
|
||||||
|
if(b->propagator)
|
||||||
|
descendant.propagateBonus(b, *this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant)
|
||||||
|
{
|
||||||
|
for(const auto & b : exportedBonuses)
|
||||||
|
if(b->propagator)
|
||||||
|
descendant.unpropagateBonus(b);
|
||||||
|
|
||||||
|
TNodes redParents;
|
||||||
|
getRedAncestors(redParents); //get all red parents recursively
|
||||||
|
|
||||||
|
for(auto * parent : redParents)
|
||||||
|
{
|
||||||
|
for(const auto & b : parent->exportedBonuses)
|
||||||
|
if(b->propagator)
|
||||||
|
descendant.unpropagateBonus(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::getRedAncestors(TNodes &out)
|
||||||
|
{
|
||||||
|
getRedParents(out);
|
||||||
|
|
||||||
|
TNodes redParents;
|
||||||
|
getRedParents(redParents);
|
||||||
|
|
||||||
|
for(CBonusSystemNode * parent : redParents)
|
||||||
|
parent->getRedAncestors(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::exportBonus(const std::shared_ptr<Bonus> & b)
|
||||||
|
{
|
||||||
|
if(b->propagator)
|
||||||
|
propagateBonus(b, *this);
|
||||||
|
else
|
||||||
|
bonuses.push_back(b);
|
||||||
|
|
||||||
|
CBonusSystemNode::treeHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::exportBonuses()
|
||||||
|
{
|
||||||
|
for(const auto & b : exportedBonuses)
|
||||||
|
exportBonus(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
|
||||||
|
{
|
||||||
|
return nodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BonusList& CBonusSystemNode::getBonusList() const
|
||||||
|
{
|
||||||
|
return bonuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TNodesVector& CBonusSystemNode::getParentNodes() const
|
||||||
|
{
|
||||||
|
return parents;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TNodesVector& CBonusSystemNode::getChildrenNodes() const
|
||||||
|
{
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
|
||||||
|
{
|
||||||
|
nodeType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
BonusList & CBonusSystemNode::getExportedBonusList()
|
||||||
|
{
|
||||||
|
return exportedBonuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BonusList & CBonusSystemNode::getExportedBonusList() const
|
||||||
|
{
|
||||||
|
return exportedBonuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& CBonusSystemNode::getDescription() const
|
||||||
|
{
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::setDescription(const std::string &description)
|
||||||
|
{
|
||||||
|
this->description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out) const
|
||||||
|
{
|
||||||
|
assert(&allBonuses != &out); //todo should it work in-place?
|
||||||
|
|
||||||
|
BonusList undecided = allBonuses;
|
||||||
|
BonusList & accepted = out;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int undecidedCount = static_cast<int>(undecided.size());
|
||||||
|
for(int i = 0; i < undecided.size(); i++)
|
||||||
|
{
|
||||||
|
auto b = undecided[i];
|
||||||
|
BonusLimitationContext context = {*b, *this, out, undecided};
|
||||||
|
auto decision = b->limiter ? b->limiter->limit(context) : ILimiter::EDecision::ACCEPT; //bonuses without limiters will be accepted by default
|
||||||
|
if(decision == ILimiter::EDecision::DISCARD)
|
||||||
|
{
|
||||||
|
undecided.erase(i);
|
||||||
|
i--; continue;
|
||||||
|
}
|
||||||
|
else if(decision == ILimiter::EDecision::ACCEPT)
|
||||||
|
{
|
||||||
|
accepted.push_back(b);
|
||||||
|
undecided.erase(i);
|
||||||
|
i--; continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
assert(decision == ILimiter::EDecision::NOT_SURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(undecided.size() == undecidedCount) //we haven't moved a single bonus -> limiters reached a stable state
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TBonusListPtr CBonusSystemNode::limitBonuses(const BonusList &allBonuses) const
|
||||||
|
{
|
||||||
|
auto ret = std::make_shared<BonusList>();
|
||||||
|
limitBonuses(allBonuses, *ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBonusSystemNode::treeHasChanged()
|
||||||
|
{
|
||||||
|
treeChanged++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t CBonusSystemNode::getTreeVersion() const
|
||||||
|
{
|
||||||
|
return treeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
139
lib/bonuses/CBonusSystemNode.h
Normal file
139
lib/bonuses/CBonusSystemNode.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* CBonusSystemNode.h, 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "HeroBonus.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
using TNodes = std::set<CBonusSystemNode *>;
|
||||||
|
using TCNodes = std::set<const CBonusSystemNode *>;
|
||||||
|
using TNodesVector = std::vector<CBonusSystemNode *>;
|
||||||
|
|
||||||
|
class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ENodeTypes
|
||||||
|
{
|
||||||
|
NONE = -1,
|
||||||
|
UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALTY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM,
|
||||||
|
TOWN_AND_VISITOR, BATTLE, COMMANDER, GLOBAL_EFFECTS, ALL_CREATURES, TOWN
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
BonusList bonuses; //wielded bonuses (local or up-propagated here)
|
||||||
|
BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
|
||||||
|
|
||||||
|
TNodesVector parents; //parents -> we inherit bonuses from them, we may attach our bonuses to them
|
||||||
|
TNodesVector children;
|
||||||
|
|
||||||
|
ENodeTypes nodeType;
|
||||||
|
std::string description;
|
||||||
|
bool isHypotheticNode;
|
||||||
|
|
||||||
|
static const bool cachingEnabled;
|
||||||
|
mutable BonusList cachedBonuses;
|
||||||
|
mutable int64_t cachedLast;
|
||||||
|
static std::atomic<int64_t> treeChanged;
|
||||||
|
|
||||||
|
// Setting a value to cachingStr before getting any bonuses caches the result for later requests.
|
||||||
|
// This string needs to be unique, that's why it has to be setted in the following manner:
|
||||||
|
// [property key]_[value] => only for selector
|
||||||
|
mutable std::map<std::string, TBonusListPtr > cachedRequests;
|
||||||
|
mutable boost::mutex sync;
|
||||||
|
|
||||||
|
void getAllBonusesRec(BonusList &out, const CSelector & selector) const;
|
||||||
|
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
|
||||||
|
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CBonusSystemNode();
|
||||||
|
explicit CBonusSystemNode(bool isHypotetic);
|
||||||
|
explicit CBonusSystemNode(ENodeTypes NodeType);
|
||||||
|
CBonusSystemNode(CBonusSystemNode && other) noexcept;
|
||||||
|
virtual ~CBonusSystemNode();
|
||||||
|
|
||||||
|
void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
|
||||||
|
TBonusListPtr limitBonuses(const BonusList &allBonuses) const; //same as above, returns out by val for convienence
|
||||||
|
TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
|
||||||
|
void getParents(TCNodes &out) const; //retrieves list of parent nodes (nodes to inherit bonuses from),
|
||||||
|
std::shared_ptr<const Bonus> getBonusLocalFirst(const CSelector & selector) const;
|
||||||
|
|
||||||
|
//non-const interface
|
||||||
|
void getParents(TNodes &out); //retrieves list of parent nodes (nodes to inherit bonuses from)
|
||||||
|
|
||||||
|
void getRedParents(TNodes &out); //retrieves list of red parent nodes (nodes bonuses propagate from)
|
||||||
|
void getRedAncestors(TNodes &out);
|
||||||
|
void getRedChildren(TNodes &out);
|
||||||
|
void getAllParents(TCNodes & out) const;
|
||||||
|
static PlayerColor retrieveNodeOwner(const CBonusSystemNode * node);
|
||||||
|
std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector & selector);
|
||||||
|
|
||||||
|
void attachTo(CBonusSystemNode & parent);
|
||||||
|
void detachFrom(CBonusSystemNode & parent);
|
||||||
|
void detachFromAll();
|
||||||
|
virtual void addNewBonus(const std::shared_ptr<Bonus>& b);
|
||||||
|
void accumulateBonus(const std::shared_ptr<Bonus>& b); //add value of bonus with same type/subtype or create new
|
||||||
|
|
||||||
|
void newChildAttached(CBonusSystemNode & child);
|
||||||
|
void childDetached(CBonusSystemNode & child);
|
||||||
|
void propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source);
|
||||||
|
void unpropagateBonus(const std::shared_ptr<Bonus> & b);
|
||||||
|
void removeBonus(const std::shared_ptr<Bonus>& b);
|
||||||
|
void removeBonuses(const CSelector & selector);
|
||||||
|
void removeBonusesRecursive(const CSelector & s);
|
||||||
|
void newRedDescendant(CBonusSystemNode & descendant); //propagation needed
|
||||||
|
void removedRedDescendant(CBonusSystemNode & descendant); //de-propagation needed
|
||||||
|
|
||||||
|
bool isIndependentNode() const; //node is independent when it has no parents nor children
|
||||||
|
bool actsAsBonusSourceOnly() const;
|
||||||
|
///updates count of remaining turns and removes outdated bonuses by selector
|
||||||
|
void reduceBonusDurations(const CSelector &s);
|
||||||
|
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
||||||
|
virtual std::string nodeName() const;
|
||||||
|
virtual std::string nodeShortInfo() const;
|
||||||
|
bool isHypothetic() const { return isHypotheticNode; }
|
||||||
|
|
||||||
|
void deserializationFix();
|
||||||
|
void exportBonus(const std::shared_ptr<Bonus> & b);
|
||||||
|
void exportBonuses();
|
||||||
|
|
||||||
|
const BonusList &getBonusList() const;
|
||||||
|
BonusList & getExportedBonusList();
|
||||||
|
const BonusList & getExportedBonusList() const;
|
||||||
|
CBonusSystemNode::ENodeTypes getNodeType() const;
|
||||||
|
void setNodeType(CBonusSystemNode::ENodeTypes type);
|
||||||
|
const TNodesVector &getParentNodes() const;
|
||||||
|
const TNodesVector &getChildrenNodes() const;
|
||||||
|
const std::string &getDescription() const;
|
||||||
|
void setDescription(const std::string &description);
|
||||||
|
|
||||||
|
static void treeHasChanged();
|
||||||
|
|
||||||
|
int64_t getTreeVersion() const override;
|
||||||
|
|
||||||
|
virtual PlayerColor getOwner() const
|
||||||
|
{
|
||||||
|
return PlayerColor::NEUTRAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
// h & bonuses;
|
||||||
|
h & nodeType;
|
||||||
|
h & exportedBonuses;
|
||||||
|
h & description;
|
||||||
|
BONUS_TREE_DESERIALIZATION_FIX
|
||||||
|
//h & parents & children;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class CBonusProxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
@ -10,8 +10,10 @@
|
|||||||
|
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "HeroBonus.h"
|
#include "HeroBonus.h"
|
||||||
|
#include "CBonusSystemNode.h"
|
||||||
#include "Limiters.h"
|
#include "Limiters.h"
|
||||||
#include "Updaters.h"
|
#include "Updaters.h"
|
||||||
|
#include "Propagators.h"
|
||||||
|
|
||||||
#include "../VCMI_Lib.h"
|
#include "../VCMI_Lib.h"
|
||||||
#include "../spells/CSpellHandler.h"
|
#include "../spells/CSpellHandler.h"
|
||||||
@ -30,9 +32,6 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
|
|
||||||
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
|
|
||||||
|
|
||||||
#define BONUS_NAME(x) { #x, Bonus::x },
|
#define BONUS_NAME(x) { #x, Bonus::x },
|
||||||
const std::map<std::string, Bonus::BonusType> bonusNameMap = {
|
const std::map<std::string, Bonus::BonusType> bonusNameMap = {
|
||||||
BONUS_LIST
|
BONUS_LIST
|
||||||
@ -71,16 +70,6 @@ const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect =
|
|||||||
BONUS_ITEM(ONLY_MELEE_FIGHT)
|
BONUS_ITEM(ONLY_MELEE_FIGHT)
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
|
|
||||||
{
|
|
||||||
{"BATTLE_WIDE", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE)},
|
|
||||||
{"VISITED_TOWN_AND_VISITOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::TOWN_AND_VISITOR)},
|
|
||||||
{"PLAYER_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER)},
|
|
||||||
{"HERO", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO)},
|
|
||||||
{"TEAM_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::TEAM)}, //untested
|
|
||||||
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)}
|
|
||||||
}; //untested
|
|
||||||
|
|
||||||
const std::set<std::string> deprecatedBonusSet = {
|
const std::set<std::string> deprecatedBonusSet = {
|
||||||
"SECONDARY_SKILL_PREMY",
|
"SECONDARY_SKILL_PREMY",
|
||||||
"SECONDARY_SKILL_VAL2",
|
"SECONDARY_SKILL_VAL2",
|
||||||
@ -159,9 +148,6 @@ JsonNode CAddInfo::toJsonNode() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<int64_t> CBonusSystemNode::treeChanged(1);
|
|
||||||
constexpr bool CBonusSystemNode::cachingEnabled = true;
|
|
||||||
|
|
||||||
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
|
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -477,669 +463,6 @@ std::shared_ptr<const Bonus> IBonusBearer::getBonus(const CSelector &selector) c
|
|||||||
return bonuses->getFirst(Selector::all);
|
return bonuses->getFirst(Selector::all);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerColor CBonusSystemNode::retrieveNodeOwner(const CBonusSystemNode * node)
|
|
||||||
{
|
|
||||||
return node ? node->getOwner() : PlayerColor::CANNOT_DETERMINE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
|
|
||||||
{
|
|
||||||
auto ret = bonuses.getFirst(selector);
|
|
||||||
if(ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
FOREACH_PARENT(pname)
|
|
||||||
{
|
|
||||||
ret = pname->getBonusLocalFirst(selector);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector) const
|
|
||||||
{
|
|
||||||
return (const_cast<CBonusSystemNode*>(this))->getBonusLocalFirst(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
|
|
||||||
{
|
|
||||||
for(const auto & elem : parents)
|
|
||||||
{
|
|
||||||
const CBonusSystemNode *parent = elem;
|
|
||||||
out.insert(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::getParents(TNodes &out)
|
|
||||||
{
|
|
||||||
for (auto & elem : parents)
|
|
||||||
{
|
|
||||||
const CBonusSystemNode *parent = elem;
|
|
||||||
out.insert(const_cast<CBonusSystemNode*>(parent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::getAllParents(TCNodes & out) const //retrieves list of parent nodes (nodes to inherit bonuses from)
|
|
||||||
{
|
|
||||||
for(auto * parent : parents)
|
|
||||||
{
|
|
||||||
out.insert(parent);
|
|
||||||
parent->getAllParents(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::getAllBonusesRec(BonusList &out, const CSelector & selector) const
|
|
||||||
{
|
|
||||||
//out has been reserved sufficient capacity at getAllBonuses() call
|
|
||||||
|
|
||||||
BonusList beforeUpdate;
|
|
||||||
TCNodes lparents;
|
|
||||||
getAllParents(lparents);
|
|
||||||
|
|
||||||
if(!lparents.empty())
|
|
||||||
{
|
|
||||||
//estimate on how many bonuses are missing yet - must be positive
|
|
||||||
beforeUpdate.reserve(std::max(out.capacity() - out.size(), bonuses.size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
beforeUpdate.reserve(bonuses.size()); //at most all local bonuses
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const auto * parent : lparents)
|
|
||||||
{
|
|
||||||
parent->getAllBonusesRec(beforeUpdate, selector);
|
|
||||||
}
|
|
||||||
bonuses.getAllBonuses(beforeUpdate);
|
|
||||||
|
|
||||||
for(const auto & b : beforeUpdate)
|
|
||||||
{
|
|
||||||
//We should not run updaters on non-selected bonuses
|
|
||||||
auto updated = selector(b.get()) && b->updater
|
|
||||||
? getUpdatedBonus(b, b->updater)
|
|
||||||
: b;
|
|
||||||
|
|
||||||
//do not add bonus with updater
|
|
||||||
bool bonusExists = false;
|
|
||||||
for(const auto & bonus : out)
|
|
||||||
{
|
|
||||||
if (bonus == updated)
|
|
||||||
bonusExists = true;
|
|
||||||
if (bonus->updater && bonus->updater == updated->updater)
|
|
||||||
bonusExists = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bonusExists)
|
|
||||||
out.push_back(updated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root, const std::string &cachingStr) const
|
|
||||||
{
|
|
||||||
bool limitOnUs = (!root || root == this); //caching won't work when we want to limit bonuses against an external node
|
|
||||||
if (CBonusSystemNode::cachingEnabled && limitOnUs)
|
|
||||||
{
|
|
||||||
// Exclusive access for one thread
|
|
||||||
boost::lock_guard<boost::mutex> lock(sync);
|
|
||||||
|
|
||||||
// If the bonus system tree changes(state of a single node or the relations to each other) then
|
|
||||||
// cache all bonus objects. Selector objects doesn't matter.
|
|
||||||
if (cachedLast != treeChanged)
|
|
||||||
{
|
|
||||||
BonusList allBonuses;
|
|
||||||
allBonuses.reserve(cachedBonuses.capacity()); //we assume we'll get about the same number of bonuses
|
|
||||||
|
|
||||||
cachedBonuses.clear();
|
|
||||||
cachedRequests.clear();
|
|
||||||
|
|
||||||
getAllBonusesRec(allBonuses, Selector::all);
|
|
||||||
limitBonuses(allBonuses, cachedBonuses);
|
|
||||||
cachedBonuses.stackBonuses();
|
|
||||||
|
|
||||||
cachedLast = treeChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a bonus system request comes with a caching string then look up in the map if there are any
|
|
||||||
// pre-calculated bonus results. Limiters can't be cached so they have to be calculated.
|
|
||||||
if(!cachingStr.empty())
|
|
||||||
{
|
|
||||||
auto it = cachedRequests.find(cachingStr);
|
|
||||||
if(it != cachedRequests.end())
|
|
||||||
{
|
|
||||||
//Cached list contains bonuses for our query with applied limiters
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//We still don't have the bonuses (didn't returned them from cache)
|
|
||||||
//Perform bonus selection
|
|
||||||
auto ret = std::make_shared<BonusList>();
|
|
||||||
cachedBonuses.getBonuses(*ret, selector, limit);
|
|
||||||
|
|
||||||
// Save the results in the cache
|
|
||||||
if(!cachingStr.empty())
|
|
||||||
cachedRequests[cachingStr] = ret;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return getAllBonusesWithoutCaching(selector, limit, root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root) const
|
|
||||||
{
|
|
||||||
auto ret = std::make_shared<BonusList>();
|
|
||||||
|
|
||||||
// Get bonus results without caching enabled.
|
|
||||||
BonusList beforeLimiting;
|
|
||||||
BonusList afterLimiting;
|
|
||||||
getAllBonusesRec(beforeLimiting, selector);
|
|
||||||
|
|
||||||
if(!root || root == this)
|
|
||||||
{
|
|
||||||
limitBonuses(beforeLimiting, afterLimiting);
|
|
||||||
}
|
|
||||||
else if(root)
|
|
||||||
{
|
|
||||||
//We want to limit our query against an external node. We get all its bonuses,
|
|
||||||
// add the ones we're considering and see if they're cut out by limiters
|
|
||||||
BonusList rootBonuses;
|
|
||||||
BonusList limitedRootBonuses;
|
|
||||||
getAllBonusesRec(rootBonuses, selector);
|
|
||||||
|
|
||||||
for(const auto & b : beforeLimiting)
|
|
||||||
rootBonuses.push_back(b);
|
|
||||||
|
|
||||||
root->limitBonuses(rootBonuses, limitedRootBonuses);
|
|
||||||
|
|
||||||
for(const auto & b : beforeLimiting)
|
|
||||||
if(vstd::contains(limitedRootBonuses, b))
|
|
||||||
afterLimiting.push_back(b);
|
|
||||||
|
|
||||||
}
|
|
||||||
afterLimiting.getBonuses(*ret, selector, limit);
|
|
||||||
ret->stackBonuses();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Bonus> CBonusSystemNode::getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const
|
|
||||||
{
|
|
||||||
assert(updater);
|
|
||||||
return updater->createUpdatedBonus(b, * this);
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::CBonusSystemNode()
|
|
||||||
:CBonusSystemNode(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
|
|
||||||
bonuses(true),
|
|
||||||
exportedBonuses(true),
|
|
||||||
nodeType(UNKNOWN),
|
|
||||||
cachedLast(0),
|
|
||||||
isHypotheticNode(isHypotetic)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType):
|
|
||||||
bonuses(true),
|
|
||||||
exportedBonuses(true),
|
|
||||||
nodeType(NodeType),
|
|
||||||
cachedLast(0),
|
|
||||||
isHypotheticNode(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other) noexcept:
|
|
||||||
bonuses(std::move(other.bonuses)),
|
|
||||||
exportedBonuses(std::move(other.exportedBonuses)),
|
|
||||||
nodeType(other.nodeType),
|
|
||||||
description(other.description),
|
|
||||||
cachedLast(0),
|
|
||||||
isHypotheticNode(other.isHypotheticNode)
|
|
||||||
{
|
|
||||||
std::swap(parents, other.parents);
|
|
||||||
std::swap(children, other.children);
|
|
||||||
|
|
||||||
//fixing bonus tree without recalculation
|
|
||||||
|
|
||||||
if(!isHypothetic())
|
|
||||||
{
|
|
||||||
for(CBonusSystemNode * n : parents)
|
|
||||||
{
|
|
||||||
n->children -= &other;
|
|
||||||
n->children.push_back(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(CBonusSystemNode * n : children)
|
|
||||||
{
|
|
||||||
n->parents -= &other;
|
|
||||||
n->parents.push_back(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
//cache ignored
|
|
||||||
|
|
||||||
//cachedBonuses
|
|
||||||
//cachedRequests
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::~CBonusSystemNode()
|
|
||||||
{
|
|
||||||
detachFromAll();
|
|
||||||
|
|
||||||
if(!children.empty())
|
|
||||||
{
|
|
||||||
while(!children.empty())
|
|
||||||
children.front()->detachFrom(*this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
|
|
||||||
{
|
|
||||||
assert(!vstd::contains(parents, &parent));
|
|
||||||
parents.push_back(&parent);
|
|
||||||
|
|
||||||
if(!isHypothetic())
|
|
||||||
{
|
|
||||||
if(parent.actsAsBonusSourceOnly())
|
|
||||||
parent.newRedDescendant(*this);
|
|
||||||
else
|
|
||||||
newRedDescendant(parent);
|
|
||||||
|
|
||||||
parent.newChildAttached(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::treeHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
|
|
||||||
{
|
|
||||||
assert(vstd::contains(parents, &parent));
|
|
||||||
|
|
||||||
if(!isHypothetic())
|
|
||||||
{
|
|
||||||
if(parent.actsAsBonusSourceOnly())
|
|
||||||
parent.removedRedDescendant(*this);
|
|
||||||
else
|
|
||||||
removedRedDescendant(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vstd::contains(parents, &parent))
|
|
||||||
{
|
|
||||||
parents -= &parent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
|
|
||||||
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isHypothetic())
|
|
||||||
{
|
|
||||||
parent.childDetached(*this);
|
|
||||||
}
|
|
||||||
CBonusSystemNode::treeHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::removeBonusesRecursive(const CSelector & s)
|
|
||||||
{
|
|
||||||
removeBonuses(s);
|
|
||||||
for(CBonusSystemNode * child : children)
|
|
||||||
child->removeBonusesRecursive(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
|
|
||||||
{
|
|
||||||
BonusList bl;
|
|
||||||
exportedBonuses.getBonuses(bl, s, Selector::all);
|
|
||||||
for(const auto & b : bl)
|
|
||||||
{
|
|
||||||
b->turnsRemain--;
|
|
||||||
if(b->turnsRemain <= 0)
|
|
||||||
removeBonus(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(CBonusSystemNode *child : children)
|
|
||||||
child->reduceBonusDurations(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
|
||||||
{
|
|
||||||
//turnsRemain shouldn't be zero for following durations
|
|
||||||
if(Bonus::NTurns(b.get()) || Bonus::NDays(b.get()) || Bonus::OneWeek(b.get()))
|
|
||||||
{
|
|
||||||
assert(b->turnsRemain);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!vstd::contains(exportedBonuses, b));
|
|
||||||
exportedBonuses.push_back(b);
|
|
||||||
exportBonus(b);
|
|
||||||
CBonusSystemNode::treeHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::accumulateBonus(const std::shared_ptr<Bonus>& b)
|
|
||||||
{
|
|
||||||
auto bonus = exportedBonuses.getFirst(Selector::typeSubtype(b->type, b->subtype)); //only local bonuses are interesting //TODO: what about value type?
|
|
||||||
if(bonus)
|
|
||||||
bonus->val += b->val;
|
|
||||||
else
|
|
||||||
addNewBonus(std::make_shared<Bonus>(*b)); //duplicate needed, original may get destroyed
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::removeBonus(const std::shared_ptr<Bonus>& b)
|
|
||||||
{
|
|
||||||
exportedBonuses -= b;
|
|
||||||
if(b->propagator)
|
|
||||||
unpropagateBonus(b);
|
|
||||||
else
|
|
||||||
bonuses -= b;
|
|
||||||
CBonusSystemNode::treeHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::removeBonuses(const CSelector & selector)
|
|
||||||
{
|
|
||||||
BonusList toRemove;
|
|
||||||
exportedBonuses.getBonuses(toRemove, selector, Selector::all);
|
|
||||||
for(const auto & bonus : toRemove)
|
|
||||||
removeBonus(bonus);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CBonusSystemNode::actsAsBonusSourceOnly() const
|
|
||||||
{
|
|
||||||
switch(nodeType)
|
|
||||||
{
|
|
||||||
case CREATURE:
|
|
||||||
case ARTIFACT:
|
|
||||||
case ARTIFACT_INSTANCE:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source)
|
|
||||||
{
|
|
||||||
if(b->propagator->shouldBeAttached(this))
|
|
||||||
{
|
|
||||||
auto propagated = b->propagationUpdater
|
|
||||||
? source.getUpdatedBonus(b, b->propagationUpdater)
|
|
||||||
: b;
|
|
||||||
bonuses.push_back(propagated);
|
|
||||||
logBonus->trace("#$# %s #propagated to# %s", propagated->Description(), nodeName());
|
|
||||||
}
|
|
||||||
|
|
||||||
FOREACH_RED_CHILD(child)
|
|
||||||
child->propagateBonus(b, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
|
|
||||||
{
|
|
||||||
if(b->propagator->shouldBeAttached(this))
|
|
||||||
{
|
|
||||||
bonuses -= b;
|
|
||||||
logBonus->trace("#$# %s #is no longer propagated to# %s", b->Description(), nodeName());
|
|
||||||
}
|
|
||||||
|
|
||||||
FOREACH_RED_CHILD(child)
|
|
||||||
child->unpropagateBonus(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::newChildAttached(CBonusSystemNode & child)
|
|
||||||
{
|
|
||||||
assert(!vstd::contains(children, &child));
|
|
||||||
children.push_back(&child);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::childDetached(CBonusSystemNode & child)
|
|
||||||
{
|
|
||||||
if(vstd::contains(children, &child))
|
|
||||||
children -= &child;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) is not a child of %s (nodeType=%d)"
|
|
||||||
, child.nodeShortInfo(), child.nodeType, nodeShortInfo(), nodeType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::detachFromAll()
|
|
||||||
{
|
|
||||||
while(!parents.empty())
|
|
||||||
detachFrom(*parents.front());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CBonusSystemNode::isIndependentNode() const
|
|
||||||
{
|
|
||||||
return parents.empty() && children.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CBonusSystemNode::nodeName() const
|
|
||||||
{
|
|
||||||
return !description.empty()
|
|
||||||
? description
|
|
||||||
: std::string("Bonus system node of type ") + typeid(*this).name();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CBonusSystemNode::nodeShortInfo() const
|
|
||||||
{
|
|
||||||
std::ostringstream str;
|
|
||||||
str << "'" << typeid(* this).name() << "'";
|
|
||||||
description.length() > 0
|
|
||||||
? str << " (" << description << ")"
|
|
||||||
: str << " (no description)";
|
|
||||||
return str.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::deserializationFix()
|
|
||||||
{
|
|
||||||
exportBonuses();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::getRedParents(TNodes & out)
|
|
||||||
{
|
|
||||||
FOREACH_PARENT(pname)
|
|
||||||
{
|
|
||||||
if(pname->actsAsBonusSourceOnly())
|
|
||||||
{
|
|
||||||
out.insert(pname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!actsAsBonusSourceOnly())
|
|
||||||
{
|
|
||||||
for(CBonusSystemNode *child : children)
|
|
||||||
{
|
|
||||||
out.insert(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::getRedChildren(TNodes &out)
|
|
||||||
{
|
|
||||||
FOREACH_PARENT(pname)
|
|
||||||
{
|
|
||||||
if(!pname->actsAsBonusSourceOnly())
|
|
||||||
{
|
|
||||||
out.insert(pname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(actsAsBonusSourceOnly())
|
|
||||||
{
|
|
||||||
for(CBonusSystemNode *child : children)
|
|
||||||
{
|
|
||||||
out.insert(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant)
|
|
||||||
{
|
|
||||||
for(const auto & b : exportedBonuses)
|
|
||||||
{
|
|
||||||
if(b->propagator)
|
|
||||||
descendant.propagateBonus(b, *this);
|
|
||||||
}
|
|
||||||
TNodes redParents;
|
|
||||||
getRedAncestors(redParents); //get all red parents recursively
|
|
||||||
|
|
||||||
for(auto * parent : redParents)
|
|
||||||
{
|
|
||||||
for(const auto & b : parent->exportedBonuses)
|
|
||||||
{
|
|
||||||
if(b->propagator)
|
|
||||||
descendant.propagateBonus(b, *this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant)
|
|
||||||
{
|
|
||||||
for(const auto & b : exportedBonuses)
|
|
||||||
if(b->propagator)
|
|
||||||
descendant.unpropagateBonus(b);
|
|
||||||
|
|
||||||
TNodes redParents;
|
|
||||||
getRedAncestors(redParents); //get all red parents recursively
|
|
||||||
|
|
||||||
for(auto * parent : redParents)
|
|
||||||
{
|
|
||||||
for(const auto & b : parent->exportedBonuses)
|
|
||||||
if(b->propagator)
|
|
||||||
descendant.unpropagateBonus(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::getRedAncestors(TNodes &out)
|
|
||||||
{
|
|
||||||
getRedParents(out);
|
|
||||||
|
|
||||||
TNodes redParents;
|
|
||||||
getRedParents(redParents);
|
|
||||||
|
|
||||||
for(CBonusSystemNode * parent : redParents)
|
|
||||||
parent->getRedAncestors(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::exportBonus(const std::shared_ptr<Bonus> & b)
|
|
||||||
{
|
|
||||||
if(b->propagator)
|
|
||||||
propagateBonus(b, *this);
|
|
||||||
else
|
|
||||||
bonuses.push_back(b);
|
|
||||||
|
|
||||||
CBonusSystemNode::treeHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::exportBonuses()
|
|
||||||
{
|
|
||||||
for(const auto & b : exportedBonuses)
|
|
||||||
exportBonus(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
|
|
||||||
{
|
|
||||||
return nodeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BonusList& CBonusSystemNode::getBonusList() const
|
|
||||||
{
|
|
||||||
return bonuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TNodesVector& CBonusSystemNode::getParentNodes() const
|
|
||||||
{
|
|
||||||
return parents;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TNodesVector& CBonusSystemNode::getChildrenNodes() const
|
|
||||||
{
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
|
|
||||||
{
|
|
||||||
nodeType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
BonusList & CBonusSystemNode::getExportedBonusList()
|
|
||||||
{
|
|
||||||
return exportedBonuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BonusList & CBonusSystemNode::getExportedBonusList() const
|
|
||||||
{
|
|
||||||
return exportedBonuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& CBonusSystemNode::getDescription() const
|
|
||||||
{
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::setDescription(const std::string &description)
|
|
||||||
{
|
|
||||||
this->description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out) const
|
|
||||||
{
|
|
||||||
assert(&allBonuses != &out); //todo should it work in-place?
|
|
||||||
|
|
||||||
BonusList undecided = allBonuses;
|
|
||||||
BonusList & accepted = out;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int undecidedCount = static_cast<int>(undecided.size());
|
|
||||||
for(int i = 0; i < undecided.size(); i++)
|
|
||||||
{
|
|
||||||
auto b = undecided[i];
|
|
||||||
BonusLimitationContext context = {*b, *this, out, undecided};
|
|
||||||
auto decision = b->limiter ? b->limiter->limit(context) : ILimiter::EDecision::ACCEPT; //bonuses without limiters will be accepted by default
|
|
||||||
if(decision == ILimiter::EDecision::DISCARD)
|
|
||||||
{
|
|
||||||
undecided.erase(i);
|
|
||||||
i--; continue;
|
|
||||||
}
|
|
||||||
else if(decision == ILimiter::EDecision::ACCEPT)
|
|
||||||
{
|
|
||||||
accepted.push_back(b);
|
|
||||||
undecided.erase(i);
|
|
||||||
i--; continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
assert(decision == ILimiter::EDecision::NOT_SURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(undecided.size() == undecidedCount) //we haven't moved a single bonus -> limiters reached a stable state
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TBonusListPtr CBonusSystemNode::limitBonuses(const BonusList &allBonuses) const
|
|
||||||
{
|
|
||||||
auto ret = std::make_shared<BonusList>();
|
|
||||||
limitBonuses(allBonuses, *ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::treeHasChanged()
|
|
||||||
{
|
|
||||||
treeChanged++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t CBonusSystemNode::getTreeVersion() const
|
|
||||||
{
|
|
||||||
return treeChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Bonus::Description(std::optional<si32> customValue) const
|
std::string Bonus::Description(std::optional<si32> customValue) const
|
||||||
{
|
{
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
@ -1314,246 +637,6 @@ std::string Bonus::nameForBonus() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
|
|
||||||
isConverted(true)
|
|
||||||
{
|
|
||||||
if(deprecatedTypeStr == "SECONDARY_SKILL_PREMY" || deprecatedTypeStr == "SPECIAL_SECONDARY_SKILL")
|
|
||||||
{
|
|
||||||
if(deprecatedSubtype == SecondarySkill::PATHFINDING || deprecatedSubtypeStr == "skill.pathfinding")
|
|
||||||
type = Bonus::ROUGH_TERRAIN_DISCOUNT;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::DIPLOMACY || deprecatedSubtypeStr == "skill.diplomacy")
|
|
||||||
type = Bonus::WANDERING_CREATURES_JOIN_BONUS;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::WISDOM || deprecatedSubtypeStr == "skill.wisdom")
|
|
||||||
type = Bonus::MAX_LEARNABLE_SPELL_LEVEL;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::MYSTICISM || deprecatedSubtypeStr == "skill.mysticism")
|
|
||||||
type = Bonus::MANA_REGENERATION;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::NECROMANCY || deprecatedSubtypeStr == "skill.necromancy")
|
|
||||||
type = Bonus::UNDEAD_RAISE_PERCENTAGE;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::LEARNING || deprecatedSubtypeStr == "skill.learning")
|
|
||||||
type = Bonus::HERO_EXPERIENCE_GAIN_PERCENT;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::RESISTANCE || deprecatedSubtypeStr == "skill.resistance")
|
|
||||||
type = Bonus::MAGIC_RESISTANCE;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
|
|
||||||
type = Bonus::LEARN_BATTLE_SPELL_CHANCE;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::SCOUTING || deprecatedSubtypeStr == "skill.scouting")
|
|
||||||
type = Bonus::SIGHT_RADIUS;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::INTELLIGENCE || deprecatedSubtypeStr == "skill.intelligence")
|
|
||||||
{
|
|
||||||
type = Bonus::MANA_PER_KNOWLEDGE;
|
|
||||||
valueType = Bonus::PERCENT_TO_BASE;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
|
|
||||||
type = Bonus::SPELL_DAMAGE;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
|
|
||||||
type = Bonus::LEARN_MEETING_SPELL_LIMIT;
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
|
|
||||||
{
|
|
||||||
subtype = 1;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
|
|
||||||
{
|
|
||||||
subtype = 0;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
|
|
||||||
{
|
|
||||||
subtype = -1;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
type = Bonus::GENERAL_DAMAGE_REDUCTION;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
|
|
||||||
{
|
|
||||||
subtype = 0;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = Bonus::PERCENT_TO_BASE;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = Bonus::MOVEMENT;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
|
|
||||||
{
|
|
||||||
subtype = 1;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = Bonus::PERCENT_TO_BASE;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = Bonus::MOVEMENT;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
|
|
||||||
{
|
|
||||||
type = Bonus::GENERATE_RESOURCE;
|
|
||||||
subtype = GameResID(EGameResID::GOLD);
|
|
||||||
subtypeRelevant = true;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
|
|
||||||
{
|
|
||||||
type = Bonus::MAGIC_SCHOOL_SKILL;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtype = 4;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
|
|
||||||
{
|
|
||||||
type = Bonus::MAGIC_SCHOOL_SKILL;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtype = 1;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
|
|
||||||
{
|
|
||||||
type = Bonus::MAGIC_SCHOOL_SKILL;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtype = 2;
|
|
||||||
}
|
|
||||||
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
|
|
||||||
{
|
|
||||||
type = Bonus::MAGIC_SCHOOL_SKILL;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtype = 8;
|
|
||||||
}
|
|
||||||
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
|
||||||
{
|
|
||||||
type = Bonus::BONUS_DAMAGE_CHANCE;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:creature.ballista";
|
|
||||||
}
|
|
||||||
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
|
|
||||||
{
|
|
||||||
type = Bonus::SPECIFIC_SPELL_POWER;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:spell.firstAid";
|
|
||||||
}
|
|
||||||
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
|
|
||||||
{
|
|
||||||
type = Bonus::CATAPULT_EXTRA_SHOTS;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:spell.catapultShot";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
isConverted = false;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "SECONDARY_SKILL_VAL2")
|
|
||||||
{
|
|
||||||
if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
|
|
||||||
type = Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT;
|
|
||||||
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
|
||||||
{
|
|
||||||
type = Bonus::HERO_GRANTS_ATTACKS;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:creature.ballista";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
isConverted = false;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "SEA_MOVEMENT")
|
|
||||||
{
|
|
||||||
subtype = 0;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = Bonus::ADDITIVE_VALUE;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = Bonus::MOVEMENT;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "LAND_MOVEMENT")
|
|
||||||
{
|
|
||||||
subtype = 1;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = Bonus::ADDITIVE_VALUE;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = Bonus::MOVEMENT;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "MAXED_SPELL")
|
|
||||||
{
|
|
||||||
type = Bonus::SPELL;
|
|
||||||
subtypeStr = deprecatedSubtypeStr;
|
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = Bonus::INDEPENDENT_MAX;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
val = 3;
|
|
||||||
valRelevant = true;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
|
|
||||||
{
|
|
||||||
type = Bonus::HP_REGENERATION;
|
|
||||||
val = 100000; //very high value to always chose stack health
|
|
||||||
valRelevant = true;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "KING1")
|
|
||||||
{
|
|
||||||
type = Bonus::KING;
|
|
||||||
val = 0;
|
|
||||||
valRelevant = true;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "KING2")
|
|
||||||
{
|
|
||||||
type = Bonus::KING;
|
|
||||||
val = 2;
|
|
||||||
valRelevant = true;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "KING3")
|
|
||||||
{
|
|
||||||
type = Bonus::KING;
|
|
||||||
val = 3;
|
|
||||||
valRelevant = true;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
|
|
||||||
type = Bonus::SIGHT_RADIUS;
|
|
||||||
else if (deprecatedTypeStr == "SELF_MORALE")
|
|
||||||
{
|
|
||||||
type = Bonus::MORALE;
|
|
||||||
val = 1;
|
|
||||||
valRelevant = true;
|
|
||||||
valueType = Bonus::INDEPENDENT_MAX;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
}
|
|
||||||
else if (deprecatedTypeStr == "SELF_LUCK")
|
|
||||||
{
|
|
||||||
type = Bonus::LUCK;
|
|
||||||
val = 1;
|
|
||||||
valRelevant = true;
|
|
||||||
valueType = Bonus::INDEPENDENT_MAX;
|
|
||||||
valueTypeRelevant = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
isConverted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const JsonNode & BonusParams::toJson()
|
|
||||||
{
|
|
||||||
assert(isConverted);
|
|
||||||
if(ret.isNull())
|
|
||||||
{
|
|
||||||
ret["type"].String() = vstd::findKey(bonusNameMap, type);
|
|
||||||
if(subtypeRelevant && !subtypeStr.empty())
|
|
||||||
ret["subtype"].String() = subtypeStr;
|
|
||||||
else if(subtypeRelevant)
|
|
||||||
ret["subtype"].Integer() = subtype;
|
|
||||||
if(valueTypeRelevant)
|
|
||||||
ret["valueType"].String() = vstd::findKey(bonusValueMap, valueType);
|
|
||||||
if(valRelevant)
|
|
||||||
ret["val"].Float() = val;
|
|
||||||
if(targetTypeRelevant)
|
|
||||||
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetType);
|
|
||||||
jsonCreated = true;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
CSelector BonusParams::toSelector()
|
|
||||||
{
|
|
||||||
assert(isConverted);
|
|
||||||
if(subtypeRelevant && !subtypeStr.empty())
|
|
||||||
JsonUtils::resolveIdentifier(subtype, toJson(), "subtype");
|
|
||||||
|
|
||||||
auto ret = Selector::type()(type);
|
|
||||||
if(subtypeRelevant)
|
|
||||||
ret = ret.And(Selector::subtype()(subtype));
|
|
||||||
if(valueTypeRelevant)
|
|
||||||
ret = ret.And(Selector::valueType(valueType));
|
|
||||||
if(targetTypeRelevant)
|
|
||||||
ret = ret.And(Selector::targetSourceType()(targetType));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
|
Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
|
||||||
duration(static_cast<ui16>(Duration)),
|
duration(static_cast<ui16>(Duration)),
|
||||||
type(Type),
|
type(Type),
|
||||||
@ -1722,31 +805,6 @@ std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
|
|||||||
return this->shared_from_this();
|
return this->shared_from_this();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IPropagator::shouldBeAttached(CBonusSystemNode *dest)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::ENodeTypes IPropagator::getPropagatorType() const
|
|
||||||
{
|
|
||||||
return CBonusSystemNode::ENodeTypes::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPropagatorNodeType::CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType)
|
|
||||||
: nodeType(NodeType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::ENodeTypes CPropagatorNodeType::getPropagatorType() const
|
|
||||||
{
|
|
||||||
return nodeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
|
|
||||||
{
|
|
||||||
return nodeType == dest->getNodeType();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updaters
|
// Updaters
|
||||||
|
|
||||||
std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
|
std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
|
||||||
|
@ -27,9 +27,6 @@ using TConstBonusListPtr = std::shared_ptr<const BonusList>;
|
|||||||
using TLimiterPtr = std::shared_ptr<ILimiter>;
|
using TLimiterPtr = std::shared_ptr<ILimiter>;
|
||||||
using TPropagatorPtr = std::shared_ptr<IPropagator>;
|
using TPropagatorPtr = std::shared_ptr<IPropagator>;
|
||||||
using TUpdaterPtr = std::shared_ptr<IUpdater>;
|
using TUpdaterPtr = std::shared_ptr<IUpdater>;
|
||||||
using TNodes = std::set<CBonusSystemNode *>;
|
|
||||||
using TCNodes = std::set<const CBonusSystemNode *>;
|
|
||||||
using TNodesVector = std::vector<CBonusSystemNode *>;
|
|
||||||
|
|
||||||
class CSelector : std::function<bool(const Bonus*)>
|
class CSelector : std::function<bool(const Bonus*)>
|
||||||
{
|
{
|
||||||
@ -469,28 +466,6 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
|||||||
|
|
||||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
|
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
|
||||||
|
|
||||||
struct DLL_LINKAGE BonusParams {
|
|
||||||
bool isConverted;
|
|
||||||
Bonus::BonusType type = Bonus::NONE;
|
|
||||||
TBonusSubtype subtype = -1;
|
|
||||||
std::string subtypeStr;
|
|
||||||
bool subtypeRelevant = false;
|
|
||||||
Bonus::ValueType valueType = Bonus::BASE_NUMBER;
|
|
||||||
bool valueTypeRelevant = false;
|
|
||||||
si32 val = 0;
|
|
||||||
bool valRelevant = false;
|
|
||||||
Bonus::BonusSource targetType = Bonus::SECONDARY_SKILL;
|
|
||||||
bool targetTypeRelevant = false;
|
|
||||||
|
|
||||||
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
|
|
||||||
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
|
|
||||||
const JsonNode & toJson();
|
|
||||||
CSelector toSelector();
|
|
||||||
private:
|
|
||||||
JsonNode ret;
|
|
||||||
bool jsonCreated = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE BonusList
|
class DLL_LINKAGE BonusList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -614,151 +589,6 @@ public:
|
|||||||
virtual int64_t getTreeVersion() const = 0;
|
virtual int64_t getTreeVersion() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public boost::noncopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum ENodeTypes
|
|
||||||
{
|
|
||||||
NONE = -1,
|
|
||||||
UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALTY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM,
|
|
||||||
TOWN_AND_VISITOR, BATTLE, COMMANDER, GLOBAL_EFFECTS, ALL_CREATURES, TOWN
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
BonusList bonuses; //wielded bonuses (local or up-propagated here)
|
|
||||||
BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
|
|
||||||
|
|
||||||
TNodesVector parents; //parents -> we inherit bonuses from them, we may attach our bonuses to them
|
|
||||||
TNodesVector children;
|
|
||||||
|
|
||||||
ENodeTypes nodeType;
|
|
||||||
std::string description;
|
|
||||||
bool isHypotheticNode;
|
|
||||||
|
|
||||||
static const bool cachingEnabled;
|
|
||||||
mutable BonusList cachedBonuses;
|
|
||||||
mutable int64_t cachedLast;
|
|
||||||
static std::atomic<int64_t> treeChanged;
|
|
||||||
|
|
||||||
// Setting a value to cachingStr before getting any bonuses caches the result for later requests.
|
|
||||||
// This string needs to be unique, that's why it has to be setted in the following manner:
|
|
||||||
// [property key]_[value] => only for selector
|
|
||||||
mutable std::map<std::string, TBonusListPtr > cachedRequests;
|
|
||||||
mutable boost::mutex sync;
|
|
||||||
|
|
||||||
void getAllBonusesRec(BonusList &out, const CSelector & selector) const;
|
|
||||||
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
|
|
||||||
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CBonusSystemNode();
|
|
||||||
explicit CBonusSystemNode(bool isHypotetic);
|
|
||||||
explicit CBonusSystemNode(ENodeTypes NodeType);
|
|
||||||
CBonusSystemNode(CBonusSystemNode && other) noexcept;
|
|
||||||
virtual ~CBonusSystemNode();
|
|
||||||
|
|
||||||
void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
|
|
||||||
TBonusListPtr limitBonuses(const BonusList &allBonuses) const; //same as above, returns out by val for convienence
|
|
||||||
TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
|
|
||||||
void getParents(TCNodes &out) const; //retrieves list of parent nodes (nodes to inherit bonuses from),
|
|
||||||
std::shared_ptr<const Bonus> getBonusLocalFirst(const CSelector & selector) const;
|
|
||||||
|
|
||||||
//non-const interface
|
|
||||||
void getParents(TNodes &out); //retrieves list of parent nodes (nodes to inherit bonuses from)
|
|
||||||
|
|
||||||
void getRedParents(TNodes &out); //retrieves list of red parent nodes (nodes bonuses propagate from)
|
|
||||||
void getRedAncestors(TNodes &out);
|
|
||||||
void getRedChildren(TNodes &out);
|
|
||||||
void getAllParents(TCNodes & out) const;
|
|
||||||
static PlayerColor retrieveNodeOwner(const CBonusSystemNode * node);
|
|
||||||
std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector & selector);
|
|
||||||
|
|
||||||
void attachTo(CBonusSystemNode & parent);
|
|
||||||
void detachFrom(CBonusSystemNode & parent);
|
|
||||||
void detachFromAll();
|
|
||||||
virtual void addNewBonus(const std::shared_ptr<Bonus>& b);
|
|
||||||
void accumulateBonus(const std::shared_ptr<Bonus>& b); //add value of bonus with same type/subtype or create new
|
|
||||||
|
|
||||||
void newChildAttached(CBonusSystemNode & child);
|
|
||||||
void childDetached(CBonusSystemNode & child);
|
|
||||||
void propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source);
|
|
||||||
void unpropagateBonus(const std::shared_ptr<Bonus> & b);
|
|
||||||
void removeBonus(const std::shared_ptr<Bonus>& b);
|
|
||||||
void removeBonuses(const CSelector & selector);
|
|
||||||
void removeBonusesRecursive(const CSelector & s);
|
|
||||||
void newRedDescendant(CBonusSystemNode & descendant); //propagation needed
|
|
||||||
void removedRedDescendant(CBonusSystemNode & descendant); //de-propagation needed
|
|
||||||
|
|
||||||
bool isIndependentNode() const; //node is independent when it has no parents nor children
|
|
||||||
bool actsAsBonusSourceOnly() const;
|
|
||||||
///updates count of remaining turns and removes outdated bonuses by selector
|
|
||||||
void reduceBonusDurations(const CSelector &s);
|
|
||||||
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
|
||||||
virtual std::string nodeName() const;
|
|
||||||
virtual std::string nodeShortInfo() const;
|
|
||||||
bool isHypothetic() const { return isHypotheticNode; }
|
|
||||||
|
|
||||||
void deserializationFix();
|
|
||||||
void exportBonus(const std::shared_ptr<Bonus> & b);
|
|
||||||
void exportBonuses();
|
|
||||||
|
|
||||||
const BonusList &getBonusList() const;
|
|
||||||
BonusList & getExportedBonusList();
|
|
||||||
const BonusList & getExportedBonusList() const;
|
|
||||||
CBonusSystemNode::ENodeTypes getNodeType() const;
|
|
||||||
void setNodeType(CBonusSystemNode::ENodeTypes type);
|
|
||||||
const TNodesVector &getParentNodes() const;
|
|
||||||
const TNodesVector &getChildrenNodes() const;
|
|
||||||
const std::string &getDescription() const;
|
|
||||||
void setDescription(const std::string &description);
|
|
||||||
|
|
||||||
static void treeHasChanged();
|
|
||||||
|
|
||||||
int64_t getTreeVersion() const override;
|
|
||||||
|
|
||||||
virtual PlayerColor getOwner() const
|
|
||||||
{
|
|
||||||
return PlayerColor::NEUTRAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
// h & bonuses;
|
|
||||||
h & nodeType;
|
|
||||||
h & exportedBonuses;
|
|
||||||
h & description;
|
|
||||||
BONUS_TREE_DESERIALIZATION_FIX
|
|
||||||
//h & parents & children;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend class CBonusProxy;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE IPropagator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~IPropagator() = default;
|
|
||||||
virtual bool shouldBeAttached(CBonusSystemNode *dest);
|
|
||||||
virtual CBonusSystemNode::ENodeTypes getPropagatorType() const;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CPropagatorNodeType : public IPropagator
|
|
||||||
{
|
|
||||||
CBonusSystemNode::ENodeTypes nodeType;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType = CBonusSystemNode::ENodeTypes::UNKNOWN);
|
|
||||||
bool shouldBeAttached(CBonusSystemNode *dest) override;
|
|
||||||
CBonusSystemNode::ENodeTypes getPropagatorType() const override;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
h & nodeType;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class CSelectFieldEqual
|
class CSelectFieldEqual
|
||||||
{
|
{
|
||||||
@ -880,7 +710,6 @@ extern DLL_LINKAGE const std::map<std::string, Bonus::ValueType> bonusValueMap;
|
|||||||
extern DLL_LINKAGE const std::map<std::string, Bonus::BonusSource> bonusSourceMap;
|
extern DLL_LINKAGE const std::map<std::string, Bonus::BonusSource> bonusSourceMap;
|
||||||
extern DLL_LINKAGE const std::map<std::string, ui16> bonusDurationMap;
|
extern DLL_LINKAGE const std::map<std::string, ui16> bonusDurationMap;
|
||||||
extern DLL_LINKAGE const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect;
|
extern DLL_LINKAGE const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect;
|
||||||
extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
|
|
||||||
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
|
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
|
||||||
|
|
||||||
// BonusList template that requires full interface of CBonusSystemNode
|
// BonusList template that requires full interface of CBonusSystemNode
|
||||||
|
52
lib/bonuses/Propagators.cpp
Normal file
52
lib/bonuses/Propagators.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Propagators.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 "Propagators.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
|
||||||
|
{
|
||||||
|
{"BATTLE_WIDE", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE)},
|
||||||
|
{"VISITED_TOWN_AND_VISITOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::TOWN_AND_VISITOR)},
|
||||||
|
{"PLAYER_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER)},
|
||||||
|
{"HERO", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO)},
|
||||||
|
{"TEAM_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::TEAM)}, //untested
|
||||||
|
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)}
|
||||||
|
}; //untested
|
||||||
|
|
||||||
|
bool IPropagator::shouldBeAttached(CBonusSystemNode *dest)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::ENodeTypes IPropagator::getPropagatorType() const
|
||||||
|
{
|
||||||
|
return CBonusSystemNode::ENodeTypes::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPropagatorNodeType::CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType)
|
||||||
|
: nodeType(NodeType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::ENodeTypes CPropagatorNodeType::getPropagatorType() const
|
||||||
|
{
|
||||||
|
return nodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
|
||||||
|
{
|
||||||
|
return nodeType == dest->getNodeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
45
lib/bonuses/Propagators.h
Normal file
45
lib/bonuses/Propagators.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Propagators.h, 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "HeroBonus.h"
|
||||||
|
#include "CBonusSystemNode.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
|
||||||
|
|
||||||
|
class DLL_LINKAGE IPropagator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IPropagator() = default;
|
||||||
|
virtual bool shouldBeAttached(CBonusSystemNode *dest);
|
||||||
|
virtual CBonusSystemNode::ENodeTypes getPropagatorType() const;
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DLL_LINKAGE CPropagatorNodeType : public IPropagator
|
||||||
|
{
|
||||||
|
CBonusSystemNode::ENodeTypes nodeType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType = CBonusSystemNode::ENodeTypes::UNKNOWN);
|
||||||
|
bool shouldBeAttached(CBonusSystemNode *dest) override;
|
||||||
|
CBonusSystemNode::ENodeTypes getPropagatorType() const override;
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & nodeType;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
@ -12,6 +12,7 @@
|
|||||||
#include "CObjectHandler.h"
|
#include "CObjectHandler.h"
|
||||||
#include "../CCreatureSet.h"
|
#include "../CCreatureSet.h"
|
||||||
#include "../bonuses/CBonusProxy.h"
|
#include "../bonuses/CBonusProxy.h"
|
||||||
|
#include "../bonuses/CBonusSystemNode.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
@ -22,8 +22,10 @@
|
|||||||
#include "../mapObjects/CommonConstructors.h"
|
#include "../mapObjects/CommonConstructors.h"
|
||||||
#include "../mapObjects/MapObjects.h"
|
#include "../mapObjects/MapObjects.h"
|
||||||
#include "../battle/CObstacleInstance.h"
|
#include "../battle/CObstacleInstance.h"
|
||||||
|
#include "../bonuses/CBonusSystemNode.h"
|
||||||
#include "../bonuses/Limiters.h"
|
#include "../bonuses/Limiters.h"
|
||||||
#include "../bonuses/Updaters.h"
|
#include "../bonuses/Updaters.h"
|
||||||
|
#include "../bonuses/Propagators.h"
|
||||||
#include "../CStack.h"
|
#include "../CStack.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "../JsonNode.h"
|
#include "../JsonNode.h"
|
||||||
|
|
||||||
|
#include "../bonuses/CBonusSystemNode.h"
|
||||||
#include "../bonuses/HeroBonus.h"
|
#include "../bonuses/HeroBonus.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "../CBonusTypeHandler.h"
|
#include "../CBonusTypeHandler.h"
|
||||||
#include "../battle/CBattleInfoCallback.h"
|
#include "../battle/CBattleInfoCallback.h"
|
||||||
#include "../battle/Unit.h"
|
#include "../battle/Unit.h"
|
||||||
|
#include "../bonuses/BonusParams.h"
|
||||||
|
|
||||||
#include "../serializer/JsonSerializeFormat.h"
|
#include "../serializer/JsonSerializeFormat.h"
|
||||||
#include "../VCMI_Lib.h"
|
#include "../VCMI_Lib.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user