Răsfoiți Sursa

Added TIMES_ARMY_SIZE updater

Ivan Savenko 3 luni în urmă
părinte
comite
135768e763

+ 48 - 0
docs/modders/Bonus/Bonus_Updaters.md

@@ -107,6 +107,54 @@ Example of long form with custom parameters:
 }
 ```
 
+## TIMES_ARMY_SIZE
+
+Effect: Updates val to `val = clamp(val * floor(stackSize / stepSize), minimum, maximum)`, where stackSize is total number of creatures in hero army that fulful filter
+
+Parameters:
+- `minimum`: minimum possible value of the bonus value. Unlimited by default
+- `minimum`: maximum possible value of the bonus value. Unlimited by default
+- `stepSize`: number of units needed to increase updater multiplier by 1
+- `filteredCreature`: identifier of specific unit to filter
+- `filteredLevel`: level of units that need to be counted. Redundant if `filteredCreature` is used
+- `filteredFaction`: faction of units that need to be counted. Redundant if `filteredCreature` is used
+
+Filtering for specific unit:
+
+```json
+"updater" : {
+    "type" : "TIMES_ARMY_SIZE",
+    "filteredCreature" : "pikeman",
+    
+    // Optional, by default - unlimited
+    "minimum" : 0,
+    
+    // Optional, by default - unlimited
+    "maximum" : 100,
+    
+    // Optional, by default - 1
+    "stepSize" : 2
+}
+```
+
+Filtering for specific faction:
+
+```json
+"updater" : {
+    "type" : "TIMES_STACK_SIZE",
+    "filteredFaction" : "castle"
+}
+```
+
+Filtering for specific unit level:
+
+```json
+"updater" : {
+    "type" : "TIMES_STACK_SIZE",
+    "filteredLevel" : 2
+}
+```
+
 ## BONUS_OWNER_UPDATER
 
 Helper updater for proper functionality of `OPPOSITE_SIDE` limiter

+ 38 - 0
lib/bonuses/Updaters.cpp

@@ -148,6 +148,44 @@ JsonNode TimesStackSizeUpdater::toJsonNode() const
 	return JsonNode("TIMES_STACK_SIZE");
 }
 
+std::shared_ptr<Bonus> TimesArmySizeUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
+{
+	if(context.getNodeType() == BonusNodeType::ARMY || context.getNodeType() == BonusNodeType::HERO || context.getNodeType() == BonusNodeType::TOWN)
+	{
+		const auto & army = dynamic_cast<const CArmedInstance &>(context);
+		int totalSize = 0;
+		for (const auto & unit : army.Slots())
+		{
+			if (filteredCreature.hasValue() && filteredCreature != unit.second->getCreatureID())
+				continue;
+
+			if (filteredFaction.hasValue() && filteredFaction != unit.second->getFactionID())
+				continue;
+
+			if (filteredLevel != -1 && filteredLevel != unit.second->getLevel())
+				continue;
+
+			totalSize += unit.second->getCount();
+		}
+
+		auto newBonus = std::make_shared<Bonus>(*b);
+		newBonus->val *= std::clamp(totalSize / stepSize, minimum, maximum);
+		return newBonus;
+	}
+	return b;
+}
+
+std::string TimesArmySizeUpdater::toString() const
+{
+	return "TimesArmySizeUpdater";
+}
+
+JsonNode TimesArmySizeUpdater::toJsonNode() const
+{
+	return JsonNode("TIMES_ARMY_SIZE");
+}
+
+
 std::shared_ptr<Bonus> TimesStackLevelUpdater::apply(const std::shared_ptr<Bonus> & b, int level) const
 {
 	auto newBonus = std::make_shared<Bonus>(*b);

+ 24 - 0
lib/bonuses/Updaters.h

@@ -113,6 +113,30 @@ public:
 	}
 };
 
+class DLL_LINKAGE TimesArmySizeUpdater : public IUpdater
+{
+public:
+	int minimum = std::numeric_limits<int>::min();
+	int maximum = std::numeric_limits<int>::max();
+	int stepSize = 1;
+	int filteredLevel = -1;
+	CreatureID filteredCreature;
+	FactionID filteredFaction;
+	TimesArmySizeUpdater() = default;
+
+	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
+	std::string toString() const override;
+	JsonNode toJsonNode() const override;
+
+	template <typename Handler> void serialize(Handler & h)
+	{
+		h & static_cast<IUpdater &>(*this);
+		h & minimum;
+		h & maximum;
+		h & stepSize;
+	}
+};
+
 class DLL_LINKAGE TimesStackLevelUpdater : public IUpdater
 {
 	std::shared_ptr<Bonus> apply(const std::shared_ptr<Bonus> & b, int level) const;

+ 29 - 0
lib/json/JsonBonus.cpp

@@ -428,6 +428,35 @@ static TUpdaterPtr parseUpdater(const JsonNode & updaterJson)
 			}
 			return std::make_shared<TimesStackSizeUpdater>(minimum, maximum, std::max(1, stepSize));
 		}
+		if(updaterJson["type"].String() == "TIMES_ARMY_SIZE")
+		{
+			auto result = std::make_shared<TimesArmySizeUpdater>();
+
+			result->minimum = updaterJson["minimum"].isNull() ? std::numeric_limits<int>::min() : updaterJson["minimum"].Integer();
+			result->maximum = updaterJson["maximum"].isNull() ? std::numeric_limits<int>::max() : updaterJson["maximum"].Integer();
+			result->stepSize = updaterJson["stepSize"].isNull() ? 1 : updaterJson["stepSize"].Integer();
+			result->filteredLevel = updaterJson["filteredLevel"].isNull() ? -1 : updaterJson["filteredLevel"].Integer();
+			if (result->minimum > result->maximum)
+			{
+				logMod->warn("TIMES_ARMY_SIZE updater: minimum value (%d) is above maximum value(%d)!", result->minimum, result->maximum);
+				std::swap(result->minimum, result->maximum);
+			}
+			if (!updaterJson["filteredFaction"].isNull())
+			{
+				LIBRARY->identifiers()->requestIdentifier( "faction", updaterJson["filteredFaction"], [result](int32_t identifier)
+				{
+					result->filteredFaction = FactionID(identifier);
+				});
+			}
+			if (!updaterJson["filteredCreature"].isNull())
+			{
+				LIBRARY->identifiers()->requestIdentifier( "creature", updaterJson["filteredCreature"], [result](int32_t identifier)
+				{
+					result->filteredCreature = CreatureID(identifier);
+				});
+			}
+			return result;
+		}
 		else
 			logMod->warn("Unknown updater type \"%s\"", updaterJson["type"].String());
 		break;

+ 2 - 0
lib/serializer/RegisterTypes.h

@@ -298,6 +298,8 @@ void registerTypes(Serializer &s)
 	s.template registerType<DivideStackLevelUpdater>(246);
 	s.template registerType<SetHeroExperience>(247);
 	s.template registerType<GiveStackExperience>(248);
+	s.template registerType<TimesStackSizeUpdater>(249);
+	s.template registerType<TimesArmySizeUpdater>(250);
 }
 
 VCMI_LIB_NAMESPACE_END