Переглянути джерело

vcmi: add PERCENT_TO_TARGET_SOURCE value type

It will break saves!!!

Added a new value type to bonuses which allows to add percentage
only to one foreign bonus source, for example, to add percentage to all
bonuses from secondary skills from hero special.
Konstantin 2 роки тому
батько
коміт
7e9a15c20b
4 змінених файлів з 66 додано та 42 видалено
  1. 4 0
      config/schemas/bonus.json
  2. 54 42
      lib/HeroBonus.cpp
  3. 4 0
      lib/HeroBonus.h
  4. 4 0
      lib/JsonNode.cpp

+ 4 - 0
config/schemas/bonus.json

@@ -123,6 +123,10 @@
 			"type":"string",
 			"description": "sourceType"
 		},
+		"targetSourceType": {
+			"type":"string",
+			"description": "targetSourceType"
+		},
 		"stacking" : {
 			"type" : "string",
 			"description" : "stacking"

+ 54 - 42
lib/HeroBonus.cpp

@@ -452,73 +452,74 @@ void BonusList::stackBonuses()
 
 int BonusList::totalValue() const
 {
-	std::array <std::pair<int, int>, Bonus::BonusSource::NUM_BONUS_SOURCE> sources = {};
-	int base = 0;
-	int percentToBase = 0;
-	int percentToAll = 0;
-	int additive = 0;
-	int indepMax = 0;
+	struct BonusCollection
+	{
+		int base = 0;
+		int percentToBase = 0;
+		int percentToAll = 0;
+		int additive = 0;
+		int percentToSource;
+		int indepMin = std::numeric_limits<int>::max();
+		int indepMax = std::numeric_limits<int>::min();
+	};
+
+	auto percent = [](int base, int percent) -> int {return (base * (100 + percent)) / 100; };
+	std::array <BonusCollection, Bonus::BonusSource::NUM_BONUS_SOURCE> sources = {};
+	BonusCollection any;
 	bool hasIndepMax = false;
-	int indepMin = 0;
 	bool hasIndepMin = false;
-	int modifiedBase = 0;
 
 	for(std::shared_ptr<Bonus> b : bonuses)
 	{
 		switch(b->valType)
 		{
 		case Bonus::BASE_NUMBER:
-			sources[b->source].first += b->val;
+			sources[b->source].base += b->val;
 			break;
 		case Bonus::PERCENT_TO_ALL:
-			percentToAll += b->val;
+			sources[b->source].percentToAll += b->val;
 			break;
 		case Bonus::PERCENT_TO_BASE:
-			percentToBase += b->val;
+			sources[b->source].percentToBase += b->val;
 			break;
 		case Bonus::PERCENT_TO_SOURCE:
-			sources[b->source].second += b->val;
+			sources[b->source].percentToSource += b->val;
+			break;
+		case Bonus::PERCENT_TO_TARGET_TYPE:
+			sources[b->targetSourceType].percentToSource += b->val;
 			break;
 		case Bonus::ADDITIVE_VALUE:
-			additive += b->val;
+			sources[b->source].additive += b->val;
 			break;
 		case Bonus::INDEPENDENT_MAX:
-			if (!hasIndepMax)
-			{
-				indepMax = b->val;
-				hasIndepMax = true;
-			}
-			else
-			{
-				vstd::amax(indepMax, b->val);
-			}
+			hasIndepMax = true;
+			vstd::amax(sources[b->source].indepMax, b->val);
 			break;
 		case Bonus::INDEPENDENT_MIN:
-			if (!hasIndepMin)
-			{
-				indepMin = b->val;
-				hasIndepMin = true;
-			}
-			else
-			{
-				vstd::amin(indepMin, b->val);
-			}
+			hasIndepMin = true;
+			vstd::amin(sources[b->source].indepMin, b->val);
 			break;
 		}
 	}
 	for(auto src : sources)
 	{
-		base += src.first;
-		modifiedBase += src.first + (src.first * src.second) / 100;
+		any.base += percent(src.base ,src.percentToSource);
+		any.percentToBase += percent(src.percentToBase, src.percentToSource);
+		any.percentToAll += percent(src.percentToAll, src.percentToSource);
+		any.additive += percent(src.additive, src.percentToSource);
+		if(hasIndepMin)
+			vstd::amin(any.indepMin, percent(src.indepMin, src.percentToSource));
+		if(hasIndepMax)
+			vstd::amax(any.indepMax, percent(src.indepMin, src.percentToSource));
 	}
-	modifiedBase += (base * percentToBase) / 100;
-	modifiedBase += additive;
-	int valFirst = (modifiedBase * (100 + percentToAll)) / 100;
+	any.base = percent(any.base, any.percentToBase);
+	any.base += any.additive;
+	auto valFirst = percent(any.base ,any.percentToAll);
 
 	if(hasIndepMin && hasIndepMax)
-		assert(indepMin < indepMax);
+		assert(any.indepMin < any.indepMax);
 
-	const int notIndepBonuses = (int)boost::count_if(bonuses, [](const std::shared_ptr<Bonus>& b)
+	const int notIndepBonuses = (int)std::count_if(bonuses.cbegin(), bonuses.cend(), [](const std::shared_ptr<Bonus>& b)
 	{
 		return b->valType != Bonus::INDEPENDENT_MAX && b->valType != Bonus::INDEPENDENT_MIN;
 	});
@@ -526,16 +527,16 @@ int BonusList::totalValue() const
 	if (hasIndepMax)
 	{
 		if(notIndepBonuses)
-			vstd::amax(valFirst, indepMax);
+			vstd::amax(valFirst, any.indepMax);
 		else
-			valFirst = indepMax;
+			valFirst = any.indepMax;
 	}
 	if (hasIndepMin)
 	{
 		if(notIndepBonuses)
-			vstd::amin(valFirst, indepMin);
+			vstd::amin(valFirst, any.indepMin);
 		else
-			valFirst = indepMin;
+			valFirst = any.indepMin;
 	}
 
 	return valFirst;
@@ -1719,6 +1720,8 @@ JsonNode Bonus::toJsonNode() const
 		root["turns"].Integer() = turnsRemain;
 	if(source != OTHER)
 		root["sourceType"].String() = vstd::findKey(bonusSourceMap, source);
+	if(targetSourceType != OTHER)
+		root["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetSourceType);
 	if(sid != 0)
 		root["sourceID"].Integer() = sid;
 	if(val != 0)
@@ -1778,6 +1781,7 @@ Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si3
 	valType = ADDITIVE_VALUE;
 	effectRange = NO_LIMIT;
 	boost::algorithm::trim(description);
+	targetSourceType = OTHER;
 }
 
 Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, ValueType ValType)
@@ -1785,6 +1789,7 @@ Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si3
 {
 	turnsRemain = 0;
 	effectRange = NO_LIMIT;
+	targetSourceType = OTHER;
 }
 
 Bonus::Bonus()
@@ -1799,6 +1804,7 @@ Bonus::Bonus()
 	val = 0;
 	source = OTHER;
 	sid = 0;
+	targetSourceType = OTHER;
 }
 
 std::shared_ptr<Bonus> Bonus::addPropagator(TPropagatorPtr Propagator)
@@ -1833,6 +1839,12 @@ namespace Selector
 		return ssourceType;
 	}
 
+	DLL_LINKAGE CSelectFieldEqual<Bonus::BonusSource> & targetSourceType()
+	{
+		static CSelectFieldEqual<Bonus::BonusSource> ssourceType(&Bonus::targetSourceType);
+		return ssourceType;
+	}
+
 	DLL_LINKAGE CSelectFieldEqual<Bonus::LimitEffect> & effectRange()
 	{
 		static CSelectFieldEqual<Bonus::LimitEffect> seffectRange(&Bonus::effectRange);

+ 4 - 0
lib/HeroBonus.h

@@ -365,6 +365,7 @@ public:
 	BONUS_VALUE(PERCENT_TO_ALL)\
 	BONUS_VALUE(PERCENT_TO_BASE)\
 	BONUS_VALUE(PERCENT_TO_SOURCE) /*Adds value only to bonuses with same source*/\
+	BONUS_VALUE(PERCENT_TO_TARGET_TYPE) /*Adds value only to bonuses with SourceType target*/\
 	BONUS_VALUE(INDEPENDENT_MAX) /*used for SPELL bonus */\
 	BONUS_VALUE(INDEPENDENT_MIN) //used for SECONDARY_SKILL_PREMY bonus
 
@@ -421,6 +422,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
 	TBonusSubtype subtype; //-1 if not applicable - 4 bytes
 
 	BonusSource source;//source type" uses BonusSource values - what gave that bonus
+	BonusSource targetSourceType;//Bonuses of what origin this amplifies, uses BonusSource values. Needed for PERCENT_TO_TARGET_TYPE.
 	si32 val;
 	ui32 sid; //source id: id of object/artifact/spell
 	ValueType valType;
@@ -458,6 +460,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
 		h & propagator;
 		h & updater;
 		h & propagationUpdater;
+		h & targetSourceType;
 	}
 
 	template <typename Ptr>
@@ -1176,6 +1179,7 @@ namespace Selector
 	extern DLL_LINKAGE CSelectFieldEqual<TBonusSubtype> & subtype();
 	extern DLL_LINKAGE CSelectFieldEqual<CAddInfo> & info();
 	extern DLL_LINKAGE CSelectFieldEqual<Bonus::BonusSource> & sourceType();
+	extern DLL_LINKAGE CSelectFieldEqual<Bonus::BonusSource> & targetSourceType();
 	extern DLL_LINKAGE CSelectFieldEqual<Bonus::LimitEffect> & effectRange();
 	extern DLL_LINKAGE CWillLastTurns turns;
 	extern DLL_LINKAGE CWillLastDays days;

+ 4 - 0
lib/JsonNode.cpp

@@ -849,6 +849,10 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
 	if (!value->isNull())
 		b->source = static_cast<Bonus::BonusSource>(parseByMap(bonusSourceMap, value, "source type "));
 
+	value = &ability["targetSourceType"];
+	if (!value->isNull())
+		b->targetSourceType = static_cast<Bonus::BonusSource>(parseByMap(bonusSourceMap, value, "target type "));
+
 	value = &ability["limiters"];
 	if (!value->isNull())
 		b->limiter = parseLimiter(*value);