浏览代码

vcmi: split CBonusSystemNode, BonusParams and prop

More splitting of HeroBonus.h
Konstantin 2 年之前
父节点
当前提交
1d34c73c2d

+ 2 - 0
AI/VCAI/VCAI.cpp

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

+ 2 - 0
client/CPlayerInterface.cpp

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

+ 6 - 0
cmake_modules/VCMI_lib.cmake

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

+ 1 - 0
lib/CArtHandler.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"
 
 

+ 1 - 0
lib/CCreatureHandler.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"

+ 1 - 0
lib/CCreatureSet.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"

+ 1 - 0
lib/CPlayerState.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

+ 1 - 0
lib/CStack.h

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

+ 1 - 0
lib/CTownHandler.cpp

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

+ 2 - 0
lib/IGameCallback.cpp

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

+ 2 - 0
lib/JsonNode.cpp

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

+ 1 - 0
lib/battle/BattleInfo.h

@@ -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 - 0
lib/bonuses/BonusParams.cpp

@@ -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 - 0
lib/bonuses/BonusParams.h

@@ -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 - 0
lib/bonuses/CBonusSystemNode.cpp

@@ -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 - 0
lib/bonuses/CBonusSystemNode.h

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

+ 2 - 944
lib/bonuses/HeroBonus.cpp

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

+ 0 - 171
lib/bonuses/HeroBonus.h

@@ -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 - 0
lib/bonuses/Propagators.cpp

@@ -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 - 0
lib/bonuses/Propagators.h

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

+ 1 - 0
lib/mapObjects/CArmedInstance.h

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

+ 2 - 0
lib/registerTypes/RegisterTypes.h

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

+ 1 - 0
lib/serializer/JsonUpdater.cpp

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

+ 1 - 0
lib/spells/TargetCondition.cpp

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