Ver código fonte

Simplified hero creature specialties logic

Ivan Savenko 4 meses atrás
pai
commit
ab0f6ed8cd

+ 12 - 4
lib/bonuses/BonusList.cpp

@@ -69,7 +69,11 @@ int BonusList::totalValue(int baseValue) const
 		int indepMax = std::numeric_limits<int>::min();
 	};
 
-	auto applyPercentage = [](int base, int percent) -> int {
+	auto applyPercentageRoundUp = [](int base, int percent) -> int {
+		return (static_cast<int64_t>(base) * (100 + percent) + 99) / 100;
+	};
+
+	auto applyPercentageRoundDown = [](int base, int percent) -> int {
 		return (static_cast<int64_t>(base) * (100 + percent)) / 100;
 	};
 
@@ -96,7 +100,11 @@ int BonusList::totalValue(int baseValue) const
 	for(const auto & b : bonuses)
 	{
 		int sourceIndex = vstd::to_underlying(b->source);
-		int valModified	= applyPercentage(b->val, percentToSource[sourceIndex]);
+		// Workaround: creature hero specialties in H3 is the only place that uses rounding up in bonuses
+		// TODO: try to find more elegant solution?
+		int valModified	= b->source == BonusSource::CREATURE_ABILITY ?
+			applyPercentageRoundUp(b->val, percentToSource[sourceIndex]):
+			applyPercentageRoundDown(b->val, percentToSource[sourceIndex]);
 
 		switch(b->valType)
 		{
@@ -123,9 +131,9 @@ int BonusList::totalValue(int baseValue) const
 		}
 	}
 
-	accumulated.base = applyPercentage(accumulated.base, accumulated.percentToBase);
+	accumulated.base = applyPercentageRoundDown(accumulated.base, accumulated.percentToBase);
 	accumulated.base += accumulated.additive;
-	auto valFirst = applyPercentage(accumulated.base ,accumulated.percentToAll);
+	auto valFirst = applyPercentageRoundDown(accumulated.base ,accumulated.percentToAll);
 
 	if(indexMinCount && indexMaxCount && accumulated.indepMin < accumulated.indepMax)
 		accumulated.indepMax = accumulated.indepMin;

+ 1 - 1
lib/bonuses/Updaters.cpp

@@ -76,7 +76,7 @@ std::shared_ptr<Bonus> TimesHeroLevelUpdater::createUpdatedBonus(const std::shar
 	{
 		int level = dynamic_cast<const CGHeroInstance &>(context).level;
 		auto newBonus = std::make_shared<Bonus>(*b);
-		newBonus->val *= level;
+		newBonus->val *= level / stepSize;
 		return newBonus;
 	}
 	return b;

+ 10 - 0
lib/bonuses/Updaters.h

@@ -64,10 +64,20 @@ public:
 
 class DLL_LINKAGE TimesHeroLevelUpdater : public IUpdater
 {
+	int stepSize = 1;
 public:
+	TimesHeroLevelUpdater() = default;
+	TimesHeroLevelUpdater(int stepSize)
+		: stepSize(stepSize)
+	{
+		assert(stepSize > 0);
+	}
+
 	template <typename Handler> void serialize(Handler & h)
 	{
 		h & static_cast<IUpdater &>(*this);
+		 if (h.hasFeature(Handler::Version::UNIVERSITY_CONFIG))
+			h & stepSize;
 	}
 
 	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;

+ 7 - 15
lib/entities/hero/CHeroHandler.cpp

@@ -146,8 +146,7 @@ std::vector<std::shared_ptr<Bonus>> CHeroHandler::createCreatureSpecialty(Creatu
 	std::vector<std::shared_ptr<Bonus>> result;
 
 	const auto & specCreature = *cid.toCreature();
-	int stepSize = specCreature.getLevel() ? specCreature.getLevel() : 5;
-
+	int level = specCreature.hasBonusOfType(BonusType::SIEGE_WEAPON) ? 5 : specCreature.getLevel();
 	{
 		auto bonus = std::make_shared<Bonus>();
 		bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, true));
@@ -156,23 +155,16 @@ std::vector<std::shared_ptr<Bonus>> CHeroHandler::createCreatureSpecialty(Creatu
 		result.push_back(bonus);
 	}
 
+	for (const auto & skill : { PrimarySkill::ATTACK, PrimarySkill::DEFENSE})
 	{
 		auto bonus = std::make_shared<Bonus>();
 		bonus->type = BonusType::PRIMARY_SKILL;
-		bonus->subtype = BonusSubtypeID(PrimarySkill::ATTACK);
-		bonus->val = 0;
-		bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, true));
-		bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getAttack(false), stepSize));
-		result.push_back(bonus);
-	}
-
-	{
-		auto bonus = std::make_shared<Bonus>();
-		bonus->type = BonusType::PRIMARY_SKILL;
-		bonus->subtype = BonusSubtypeID(PrimarySkill::DEFENSE);
-		bonus->val = 0;
+		bonus->subtype = BonusSubtypeID(skill);
+		bonus->val = 5;
+		bonus->valType = BonusValueType::PERCENT_TO_TARGET_TYPE;
+		bonus->targetSourceType = BonusSource::CREATURE_ABILITY;
 		bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, true));
-		bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize));
+		bonus->updater.reset(new TimesHeroLevelUpdater(level));
 		result.push_back(bonus);
 	}
 	return result;