Răsfoiți Sursa

vcmi: split bonus updaters

Konstantin 2 ani în urmă
părinte
comite
e37f798a68

+ 1 - 0
AI/VCAI/VCAI.cpp

@@ -23,6 +23,7 @@
 #include "../../lib/NetPacksBase.h"
 #include "../../lib/NetPacks.h"
 #include "../../lib/bonuses/ILimiter.h"
+#include "../../lib/bonuses/IUpdater.h"
 #include "../../lib/serializer/CTypeList.h"
 #include "../../lib/serializer/BinarySerializer.h"
 #include "../../lib/serializer/BinaryDeserializer.h"

+ 1 - 0
client/CPlayerInterface.cpp

@@ -44,6 +44,7 @@
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/bonuses/ILimiter.h"
+#include "../lib/bonuses/IUpdater.h"
 #include "../lib/serializer/CTypeList.h"
 #include "../lib/serializer/BinaryDeserializer.h"
 #include "../lib/serializer/BinarySerializer.h"

+ 2 - 0
cmake_modules/VCMI_lib.cmake

@@ -29,6 +29,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 
 		${MAIN_LIB_DIR}/bonuses/HeroBonus.cpp
 		${MAIN_LIB_DIR}/bonuses/ILimiter.cpp
+		${MAIN_LIB_DIR}/bonuses/IUpdater.cpp
 
 		${MAIN_LIB_DIR}/events/ApplyDamage.cpp
 		${MAIN_LIB_DIR}/events/GameResumed.cpp
@@ -304,6 +305,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 
 		${MAIN_LIB_DIR}/bonuses/HeroBonus.h
 		${MAIN_LIB_DIR}/bonuses/ILimiter.h
+		${MAIN_LIB_DIR}/bonuses/IUpdater.h
 
 		${MAIN_LIB_DIR}/events/ApplyDamage.h
 		${MAIN_LIB_DIR}/events/GameResumed.h

+ 1 - 0
lib/CCreatureHandler.cpp

@@ -20,6 +20,7 @@
 #include "GameSettings.h"
 #include "StringConstants.h"
 #include "bonuses/ILimiter.h"
+#include "bonuses/IUpdater.h"
 #include "serializer/JsonDeserializer.h"
 #include "serializer/JsonUpdater.h"
 #include "mapObjects/CObjectClassesHandler.h"

+ 1 - 0
lib/CHeroHandler.cpp

@@ -25,6 +25,7 @@
 #include "mapObjects/CObjectClassesHandler.h"
 #include "BattleFieldHandler.h"
 #include "bonuses/ILimiter.h"
+#include "bonuses/IUpdater.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 0
lib/IGameCallback.cpp

@@ -19,6 +19,7 @@
 #include "BattleFieldHandler.h"
 #include "ObstacleHandler.h"
 #include "bonuses/ILimiter.h"
+#include "bonuses/IUpdater.h"
 
 #include "serializer/CSerializer.h" // for SAVEGAME_MAGIC
 #include "serializer/BinaryDeserializer.h"

+ 1 - 0
lib/JsonNode.cpp

@@ -15,6 +15,7 @@
 
 #include "bonuses/HeroBonus.h"
 #include "bonuses/ILimiter.h"
+#include "bonuses/IUpdater.h"
 #include "filesystem/Filesystem.h"
 #include "VCMI_Lib.h" //for identifier resolution
 #include "CModHandler.h"

+ 2 - 1
lib/battle/BattleInfo.cpp

@@ -9,7 +9,8 @@
  */
 #include "StdInc.h"
 #include "BattleInfo.h"
-#include "../bonuses/ILimiter.h"
+#include "bonuses/ILimiter.h"
+#include "bonuses/IUpdater.h"
 #include "../CStack.h"
 #include "../CHeroHandler.h"
 #include "../NetPacks.h"

+ 1 - 190
lib/bonuses/HeroBonus.cpp

@@ -11,6 +11,7 @@
 #include "StdInc.h"
 #include "HeroBonus.h"
 #include "ILimiter.h"
+#include "IUpdater.h"
 
 #include "../VCMI_Lib.h"
 #include "../spells/CSpellHandler.h"
@@ -80,14 +81,6 @@ const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
 	{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)}
 }; //untested
 
-const std::map<std::string, TUpdaterPtr> bonusUpdaterMap =
-{
-	{"TIMES_HERO_LEVEL", std::make_shared<TimesHeroLevelUpdater>()},
-	{"TIMES_STACK_LEVEL", std::make_shared<TimesStackLevelUpdater>()},
-	{"ARMY_MOVEMENT", std::make_shared<ArmyMovementUpdater>()},
-	{"BONUS_OWNER_UPDATER", std::make_shared<OwnerUpdater>()}
-};
-
 const std::set<std::string> deprecatedBonusSet = {
 	"SECONDARY_SKILL_PREMY",
 	"SECONDARY_SKILL_VAL2",
@@ -2017,186 +2010,4 @@ std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
 	return this->shared_from_this();
 }
 
-std::shared_ptr<Bonus> IUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
-{
-	return b;
-}
-
-std::string IUpdater::toString() const
-{
-	return typeid(*this).name();
-}
-
-JsonNode IUpdater::toJsonNode() const
-{
-	return JsonNode(JsonNode::JsonType::DATA_NULL);
-}
-
-GrowsWithLevelUpdater::GrowsWithLevelUpdater(int valPer20, int stepSize) : valPer20(valPer20), stepSize(stepSize)
-{
-}
-
-std::shared_ptr<Bonus> GrowsWithLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
-{
-	if(context.getNodeType() == CBonusSystemNode::HERO)
-	{
-		int level = dynamic_cast<const CGHeroInstance &>(context).level;
-		int steps = stepSize ? level / stepSize : level;
-		//rounding follows format for HMM3 creature specialty bonus
-		int newVal = (valPer20 * steps + 19) / 20;
-		//return copy of bonus with updated val
-		std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
-		newBonus->val = newVal;
-		return newBonus;
-	}
-	return b;
-}
-
-std::string GrowsWithLevelUpdater::toString() const
-{
-	return boost::str(boost::format("GrowsWithLevelUpdater(valPer20=%d, stepSize=%d)") % valPer20 % stepSize);
-}
-
-JsonNode GrowsWithLevelUpdater::toJsonNode() const
-{
-	JsonNode root(JsonNode::JsonType::DATA_STRUCT);
-
-	root["type"].String() = "GROWS_WITH_LEVEL";
-	root["parameters"].Vector().push_back(JsonUtils::intNode(valPer20));
-	if(stepSize > 1)
-		root["parameters"].Vector().push_back(JsonUtils::intNode(stepSize));
-
-	return root;
-}
-
-std::shared_ptr<Bonus> TimesHeroLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
-{
-	if(context.getNodeType() == CBonusSystemNode::HERO)
-	{
-		int level = dynamic_cast<const CGHeroInstance &>(context).level;
-		std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
-		newBonus->val *= level;
-		return newBonus;
-	}
-	return b;
-}
-
-std::string TimesHeroLevelUpdater::toString() const
-{
-	return "TimesHeroLevelUpdater";
-}
-
-JsonNode TimesHeroLevelUpdater::toJsonNode() const
-{
-	return JsonUtils::stringNode("TIMES_HERO_LEVEL");
-}
-
-ArmyMovementUpdater::ArmyMovementUpdater():
-	base(20),
-	divider(3),
-	multiplier(10),
-	max(700)
-{
-}
-
-ArmyMovementUpdater::ArmyMovementUpdater(int base, int divider, int multiplier, int max):
-	base(base),
-	divider(divider),
-	multiplier(multiplier),
-	max(max)
-{
-}
-
-std::shared_ptr<Bonus> ArmyMovementUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
-{
-	if(b->type == Bonus::MOVEMENT && context.getNodeType() == CBonusSystemNode::HERO)
-	{
-		auto speed = static_cast<const CGHeroInstance &>(context).getLowestCreatureSpeed();
-		si32 armySpeed = speed * base / divider;
-		auto counted = armySpeed * multiplier;
-		auto newBonus = std::make_shared<Bonus>(*b);
-		newBonus->source = Bonus::ARMY;
-		newBonus->val += vstd::amin(counted, max);
-		return newBonus;
-	}
-	if(b->type != Bonus::MOVEMENT)
-		logGlobal->error("ArmyMovementUpdater should only be used for MOVEMENT bonus!");
-	return b;
-}
-
-std::string ArmyMovementUpdater::toString() const
-{
-	return "ArmyMovementUpdater";
-}
-
-JsonNode ArmyMovementUpdater::toJsonNode() const
-{
-	JsonNode root(JsonNode::JsonType::DATA_STRUCT);
-
-	root["type"].String() = "ARMY_MOVEMENT";
-	root["parameters"].Vector().push_back(JsonUtils::intNode(base));
-	root["parameters"].Vector().push_back(JsonUtils::intNode(divider));
-	root["parameters"].Vector().push_back(JsonUtils::intNode(multiplier));
-	root["parameters"].Vector().push_back(JsonUtils::intNode(max));
-
-	return root;
-}
-std::shared_ptr<Bonus> TimesStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
-{
-	if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE)
-	{
-		int level = dynamic_cast<const CStackInstance &>(context).getLevel();
-		std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
-		newBonus->val *= level;
-		return newBonus;
-	}
-	else if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
-	{
-		const auto & stack = dynamic_cast<const CStack &>(context);
-		//only update if stack doesn't have an instance (summons, war machines)
-		//otherwise we'd end up multiplying twice
-		if(stack.base == nullptr)
-		{
-			int level = stack.unitType()->getLevel();
-			std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
-			newBonus->val *= level;
-			return newBonus;
-		}
-	}
-	return b;
-}
-
-std::string TimesStackLevelUpdater::toString() const
-{
-	return "TimesStackLevelUpdater";
-}
-
-JsonNode TimesStackLevelUpdater::toJsonNode() const
-{
-	return JsonUtils::stringNode("TIMES_STACK_LEVEL");
-}
-
-std::string OwnerUpdater::toString() const
-{
-	return "OwnerUpdater";
-}
-
-JsonNode OwnerUpdater::toJsonNode() const
-{
-	return JsonUtils::stringNode("BONUS_OWNER_UPDATER");
-}
-
-std::shared_ptr<Bonus> OwnerUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
-{
-	auto owner = CBonusSystemNode::retrieveNodeOwner(&context);
-
-	if(owner == PlayerColor::UNFLAGGABLE)
-		owner = PlayerColor::NEUTRAL;
-
-	std::shared_ptr<Bonus> updated =
-		std::make_shared<Bonus>(static_cast<Bonus::BonusDuration>(b->duration), b->type, b->source, b->val, b->sid, b->subtype, b->valType);
-	updated->limiter = std::make_shared<OppositeSideLimiter>(owner);
-	return updated;
-}
-
 VCMI_LIB_NAMESPACE_END

+ 0 - 100
lib/bonuses/HeroBonus.h

@@ -976,7 +976,6 @@ extern DLL_LINKAGE const std::map<std::string, Bonus::BonusSource> bonusSourceMa
 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, TPropagatorPtr> bonusPropagatorMap;
-extern DLL_LINKAGE const std::map<std::string, TUpdaterPtr> bonusUpdaterMap;
 extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
 
 // BonusList template that requires full interface of CBonusSystemNode
@@ -987,103 +986,4 @@ void BonusList::insert(const int position, InputIterator first, InputIterator la
 	changed();
 }
 
-// observers for updating bonuses based on certain events (e.g. hero gaining level)
-
-class DLL_LINKAGE IUpdater
-{
-public:
-	virtual ~IUpdater() = default;
-
-	virtual std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const;
-	virtual std::string toString() const;
-	virtual JsonNode toJsonNode() const;
-
-	template <typename Handler> void serialize(Handler & h, const int version)
-	{
-	}
-};
-
-class DLL_LINKAGE GrowsWithLevelUpdater : public IUpdater
-{
-public:
-	int valPer20 = 0;
-	int stepSize = 1;
-
-	GrowsWithLevelUpdater() = default;
-	GrowsWithLevelUpdater(int valPer20, int stepSize = 1);
-
-	template <typename Handler> void serialize(Handler & h, const int version)
-	{
-		h & static_cast<IUpdater &>(*this);
-		h & valPer20;
-		h & stepSize;
-	}
-
-	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
-	virtual std::string toString() const override;
-	virtual JsonNode toJsonNode() const override;
-};
-
-class DLL_LINKAGE TimesHeroLevelUpdater : public IUpdater
-{
-public:
-	template <typename Handler> void serialize(Handler & h, const int version)
-	{
-		h & static_cast<IUpdater &>(*this);
-	}
-
-	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
-	virtual std::string toString() const override;
-	virtual JsonNode toJsonNode() const override;
-};
-
-class DLL_LINKAGE TimesStackLevelUpdater : public IUpdater
-{
-public:
-	template <typename Handler> void serialize(Handler & h, const int version)
-	{
-		h & static_cast<IUpdater &>(*this);
-	}
-
-	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
-	virtual std::string toString() const override;
-	virtual JsonNode toJsonNode() const override;
-};
-
-class DLL_LINKAGE ArmyMovementUpdater : public IUpdater
-{
-public:
-	si32 base;
-	si32 divider;
-	si32 multiplier;
-	si32 max;
-	ArmyMovementUpdater();
-	ArmyMovementUpdater(int base, int divider, int multiplier, int max);
-	template <typename Handler> void serialize(Handler & h, const int version)
-	{
-		h & static_cast<IUpdater &>(*this);
-		h & base;
-		h & divider;
-		h & multiplier;
-		h & max;
-	}
-
-	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
-	virtual std::string toString() const override;
-	virtual JsonNode toJsonNode() const override;
-};
-
-class DLL_LINKAGE OwnerUpdater : public IUpdater
-{
-public:
-	template <typename Handler> void serialize(Handler& h, const int version)
-	{
-		h & static_cast<IUpdater &>(*this);
-	}
-
-	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus>& b, const CBonusSystemNode& context) const override;
-	virtual std::string toString() const override;
-	virtual JsonNode toJsonNode() const override;
-};
-
 VCMI_LIB_NAMESPACE_END

+ 211 - 0
lib/bonuses/IUpdater.cpp

@@ -0,0 +1,211 @@
+/*
+ * IUpdater.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 "IUpdater.h"
+#include "ILimiter.h"
+
+#include "../mapObjects/CGHeroInstance.h"
+#include "../CStack.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+const std::map<std::string, TUpdaterPtr> bonusUpdaterMap =
+{
+	{"TIMES_HERO_LEVEL", std::make_shared<TimesHeroLevelUpdater>()},
+	{"TIMES_STACK_LEVEL", std::make_shared<TimesStackLevelUpdater>()},
+	{"ARMY_MOVEMENT", std::make_shared<ArmyMovementUpdater>()},
+	{"BONUS_OWNER_UPDATER", std::make_shared<OwnerUpdater>()}
+};
+
+std::shared_ptr<Bonus> IUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
+{
+	return b;
+}
+
+std::string IUpdater::toString() const
+{
+	return typeid(*this).name();
+}
+
+JsonNode IUpdater::toJsonNode() const
+{
+	return JsonNode(JsonNode::JsonType::DATA_NULL);
+}
+
+GrowsWithLevelUpdater::GrowsWithLevelUpdater(int valPer20, int stepSize) : valPer20(valPer20), stepSize(stepSize)
+{
+}
+
+std::shared_ptr<Bonus> GrowsWithLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
+{
+	if(context.getNodeType() == CBonusSystemNode::HERO)
+	{
+		int level = dynamic_cast<const CGHeroInstance &>(context).level;
+		int steps = stepSize ? level / stepSize : level;
+		//rounding follows format for HMM3 creature specialty bonus
+		int newVal = (valPer20 * steps + 19) / 20;
+		//return copy of bonus with updated val
+		std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
+		newBonus->val = newVal;
+		return newBonus;
+	}
+	return b;
+}
+
+std::string GrowsWithLevelUpdater::toString() const
+{
+	return boost::str(boost::format("GrowsWithLevelUpdater(valPer20=%d, stepSize=%d)") % valPer20 % stepSize);
+}
+
+JsonNode GrowsWithLevelUpdater::toJsonNode() const
+{
+	JsonNode root(JsonNode::JsonType::DATA_STRUCT);
+
+	root["type"].String() = "GROWS_WITH_LEVEL";
+	root["parameters"].Vector().push_back(JsonUtils::intNode(valPer20));
+	if(stepSize > 1)
+		root["parameters"].Vector().push_back(JsonUtils::intNode(stepSize));
+
+	return root;
+}
+
+std::shared_ptr<Bonus> TimesHeroLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
+{
+	if(context.getNodeType() == CBonusSystemNode::HERO)
+	{
+		int level = dynamic_cast<const CGHeroInstance &>(context).level;
+		std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
+		newBonus->val *= level;
+		return newBonus;
+	}
+	return b;
+}
+
+std::string TimesHeroLevelUpdater::toString() const
+{
+	return "TimesHeroLevelUpdater";
+}
+
+JsonNode TimesHeroLevelUpdater::toJsonNode() const
+{
+	return JsonUtils::stringNode("TIMES_HERO_LEVEL");
+}
+
+ArmyMovementUpdater::ArmyMovementUpdater():
+	base(20),
+	divider(3),
+	multiplier(10),
+	max(700)
+{
+}
+
+ArmyMovementUpdater::ArmyMovementUpdater(int base, int divider, int multiplier, int max):
+	base(base),
+	divider(divider),
+	multiplier(multiplier),
+	max(max)
+{
+}
+
+std::shared_ptr<Bonus> ArmyMovementUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
+{
+	if(b->type == Bonus::MOVEMENT && context.getNodeType() == CBonusSystemNode::HERO)
+	{
+		auto speed = static_cast<const CGHeroInstance &>(context).getLowestCreatureSpeed();
+		si32 armySpeed = speed * base / divider;
+		auto counted = armySpeed * multiplier;
+		auto newBonus = std::make_shared<Bonus>(*b);
+		newBonus->source = Bonus::ARMY;
+		newBonus->val += vstd::amin(counted, max);
+		return newBonus;
+	}
+	if(b->type != Bonus::MOVEMENT)
+		logGlobal->error("ArmyMovementUpdater should only be used for MOVEMENT bonus!");
+	return b;
+}
+
+std::string ArmyMovementUpdater::toString() const
+{
+	return "ArmyMovementUpdater";
+}
+
+JsonNode ArmyMovementUpdater::toJsonNode() const
+{
+	JsonNode root(JsonNode::JsonType::DATA_STRUCT);
+
+	root["type"].String() = "ARMY_MOVEMENT";
+	root["parameters"].Vector().push_back(JsonUtils::intNode(base));
+	root["parameters"].Vector().push_back(JsonUtils::intNode(divider));
+	root["parameters"].Vector().push_back(JsonUtils::intNode(multiplier));
+	root["parameters"].Vector().push_back(JsonUtils::intNode(max));
+
+	return root;
+}
+std::shared_ptr<Bonus> TimesStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
+{
+	if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE)
+	{
+		int level = dynamic_cast<const CStackInstance &>(context).getLevel();
+		std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
+		newBonus->val *= level;
+		return newBonus;
+	}
+	else if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
+	{
+		const auto & stack = dynamic_cast<const CStack &>(context);
+		//only update if stack doesn't have an instance (summons, war machines)
+		//otherwise we'd end up multiplying twice
+		if(stack.base == nullptr)
+		{
+			int level = stack.unitType()->getLevel();
+			std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
+			newBonus->val *= level;
+			return newBonus;
+		}
+	}
+	return b;
+}
+
+std::string TimesStackLevelUpdater::toString() const
+{
+	return "TimesStackLevelUpdater";
+}
+
+JsonNode TimesStackLevelUpdater::toJsonNode() const
+{
+	return JsonUtils::stringNode("TIMES_STACK_LEVEL");
+}
+
+std::string OwnerUpdater::toString() const
+{
+	return "OwnerUpdater";
+}
+
+JsonNode OwnerUpdater::toJsonNode() const
+{
+	return JsonUtils::stringNode("BONUS_OWNER_UPDATER");
+}
+
+std::shared_ptr<Bonus> OwnerUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
+{
+	auto owner = CBonusSystemNode::retrieveNodeOwner(&context);
+
+	if(owner == PlayerColor::UNFLAGGABLE)
+		owner = PlayerColor::NEUTRAL;
+
+	std::shared_ptr<Bonus> updated =
+		std::make_shared<Bonus>(static_cast<Bonus::BonusDuration>(b->duration), b->type, b->source, b->val, b->sid, b->subtype, b->valType);
+	updated->limiter = std::make_shared<OppositeSideLimiter>(owner);
+	return updated;
+}
+
+VCMI_LIB_NAMESPACE_END

+ 117 - 0
lib/bonuses/IUpdater.h

@@ -0,0 +1,117 @@
+/*
+ * IUpdater.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
+
+extern DLL_LINKAGE const std::map<std::string, TUpdaterPtr> bonusUpdaterMap;
+
+// observers for updating bonuses based on certain events (e.g. hero gaining level)
+
+class DLL_LINKAGE IUpdater
+{
+public:
+	virtual ~IUpdater() = default;
+
+	virtual std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const;
+	virtual std::string toString() const;
+	virtual JsonNode toJsonNode() const;
+
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+	}
+};
+
+class DLL_LINKAGE GrowsWithLevelUpdater : public IUpdater
+{
+public:
+	int valPer20 = 0;
+	int stepSize = 1;
+
+	GrowsWithLevelUpdater() = default;
+	GrowsWithLevelUpdater(int valPer20, int stepSize = 1);
+
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & static_cast<IUpdater &>(*this);
+		h & valPer20;
+		h & stepSize;
+	}
+
+	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
+	virtual std::string toString() const override;
+	virtual JsonNode toJsonNode() const override;
+};
+
+class DLL_LINKAGE TimesHeroLevelUpdater : public IUpdater
+{
+public:
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & static_cast<IUpdater &>(*this);
+	}
+
+	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
+	virtual std::string toString() const override;
+	virtual JsonNode toJsonNode() const override;
+};
+
+class DLL_LINKAGE TimesStackLevelUpdater : public IUpdater
+{
+public:
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & static_cast<IUpdater &>(*this);
+	}
+
+	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
+	virtual std::string toString() const override;
+	virtual JsonNode toJsonNode() const override;
+};
+
+class DLL_LINKAGE ArmyMovementUpdater : public IUpdater
+{
+public:
+	si32 base;
+	si32 divider;
+	si32 multiplier;
+	si32 max;
+	ArmyMovementUpdater();
+	ArmyMovementUpdater(int base, int divider, int multiplier, int max);
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & static_cast<IUpdater &>(*this);
+		h & base;
+		h & divider;
+		h & multiplier;
+		h & max;
+	}
+
+	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
+	virtual std::string toString() const override;
+	virtual JsonNode toJsonNode() const override;
+};
+
+class DLL_LINKAGE OwnerUpdater : public IUpdater
+{
+public:
+	template <typename Handler> void serialize(Handler& h, const int version)
+	{
+		h & static_cast<IUpdater &>(*this);
+	}
+
+	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus>& b, const CBonusSystemNode& context) const override;
+	virtual std::string toString() const override;
+	virtual JsonNode toJsonNode() const override;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 1 - 0
lib/registerTypes/RegisterTypes.h

@@ -23,6 +23,7 @@
 #include "../mapObjects/MapObjects.h"
 #include "../battle/CObstacleInstance.h"
 #include "../bonuses/ILimiter.h"
+#include "../bonuses/IUpdater.h"
 #include "../CStack.h"
 
 VCMI_LIB_NAMESPACE_BEGIN