浏览代码

Added cache for common hero-based bonuses

Ivan Savenko 10 月之前
父节点
当前提交
919588f7ff

+ 19 - 19
lib/battle/CUnitState.cpp

@@ -528,14 +528,14 @@ bool CUnitState::isCaster() const
 
 
 bool CUnitState::canShootBlocked() const
 bool CUnitState::canShootBlocked() const
 {
 {
-	return bonusCache.cache.getBonusValue(UnitBonusValuesProxy::HAS_FREE_SHOOTING);
+	return bonusCache.getBonusValue(UnitBonusValuesProxy::HAS_FREE_SHOOTING);
 }
 }
 
 
 bool CUnitState::canShoot() const
 bool CUnitState::canShoot() const
 {
 {
 	return
 	return
 		shots.canUse(1) &&
 		shots.canUse(1) &&
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::FORGETFULL) <= 1; //advanced+ level
+		bonusCache.getBonusValue(UnitBonusValuesProxy::FORGETFULL) <= 1; //advanced+ level
 }
 }
 
 
 bool CUnitState::isShooter() const
 bool CUnitState::isShooter() const
@@ -570,9 +570,9 @@ int64_t CUnitState::getTotalHealth() const
 	return health.total();
 	return health.total();
 }
 }
 
 
-int64_t CUnitState::getMaxHealth() const
+uint32_t CUnitState::getMaxHealth() const
 {
 {
-	return std::max(1, bonusCache.cache.getBonusValue(UnitBonusValuesProxy::STACK_HEALTH));
+	return std::max(1, bonusCache.getBonusValue(UnitBonusValuesProxy::STACK_HEALTH));
 }
 }
 
 
 BattleHex CUnitState::getPosition() const
 BattleHex CUnitState::getPosition() const
@@ -700,43 +700,43 @@ BattlePhases::Type CUnitState::battleQueuePhase(int turn) const
 
 
 bool CUnitState::isHypnotized() const
 bool CUnitState::isHypnotized() const
 {
 {
-	return bonusCache.cache.getBonusValue(UnitBonusValuesProxy::HYPNOTIZED);
+	return bonusCache.getBonusValue(UnitBonusValuesProxy::HYPNOTIZED);
 }
 }
 
 
 int CUnitState::getTotalAttacks(bool ranged) const
 int CUnitState::getTotalAttacks(bool ranged) const
 {
 {
 	return 1 + (ranged ?
 	return 1 + (ranged ?
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::TOTAL_ATTACKS_RANGED):
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::TOTAL_ATTACKS_MELEE));
+		bonusCache.getBonusValue(UnitBonusValuesProxy::TOTAL_ATTACKS_RANGED):
+		bonusCache.getBonusValue(UnitBonusValuesProxy::TOTAL_ATTACKS_MELEE));
 }
 }
 
 
 int CUnitState::getMinDamage(bool ranged) const
 int CUnitState::getMinDamage(bool ranged) const
 {
 {
 	return ranged ?
 	return ranged ?
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MIN_DAMAGE_RANGED):
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MIN_DAMAGE_MELEE);
+		bonusCache.getBonusValue(UnitBonusValuesProxy::MIN_DAMAGE_RANGED):
+		bonusCache.getBonusValue(UnitBonusValuesProxy::MIN_DAMAGE_MELEE);
 
 
 }
 }
 
 
 int CUnitState::getMaxDamage(bool ranged) const
 int CUnitState::getMaxDamage(bool ranged) const
 {
 {
 	return ranged ?
 	return ranged ?
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MAX_DAMAGE_RANGED):
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MAX_DAMAGE_MELEE);
+		bonusCache.getBonusValue(UnitBonusValuesProxy::MAX_DAMAGE_RANGED):
+		bonusCache.getBonusValue(UnitBonusValuesProxy::MAX_DAMAGE_MELEE);
 }
 }
 
 
 int CUnitState::getAttack(bool ranged) const
 int CUnitState::getAttack(bool ranged) const
 {
 {
 	int attack = ranged ?
 	int attack = ranged ?
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::ATTACK_RANGED):
-		bonusCache.cache.getBonusValue(UnitBonusValuesProxy::ATTACK_MELEE);
+		bonusCache.getBonusValue(UnitBonusValuesProxy::ATTACK_RANGED):
+		bonusCache.getBonusValue(UnitBonusValuesProxy::ATTACK_MELEE);
 
 
-	int frenzy = bonusCache.cache.getBonusValue(UnitBonusValuesProxy::IN_FRENZY);
+	int frenzy = bonusCache.getBonusValue(UnitBonusValuesProxy::IN_FRENZY);
 	if(frenzy != 0)
 	if(frenzy != 0)
 	{
 	{
 		int defence = ranged ?
 		int defence = ranged ?
-			bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_RANGED):
-			bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_MELEE);
+			bonusCache.getBonusValue(UnitBonusValuesProxy::DEFENCE_RANGED):
+			bonusCache.getBonusValue(UnitBonusValuesProxy::DEFENCE_MELEE);
 
 
 		int frenzyBonus = frenzy * defence / 100;
 		int frenzyBonus = frenzy * defence / 100;
 		attack += frenzyBonus;
 		attack += frenzyBonus;
@@ -748,7 +748,7 @@ int CUnitState::getAttack(bool ranged) const
 
 
 int CUnitState::getDefense(bool ranged) const
 int CUnitState::getDefense(bool ranged) const
 {
 {
-	int frenzy = bonusCache.cache.getBonusValue(UnitBonusValuesProxy::IN_FRENZY);
+	int frenzy = bonusCache.getBonusValue(UnitBonusValuesProxy::IN_FRENZY);
 
 
 	if(frenzy != 0)
 	if(frenzy != 0)
 	{
 	{
@@ -757,8 +757,8 @@ int CUnitState::getDefense(bool ranged) const
 	else
 	else
 	{
 	{
 		int defence = ranged ?
 		int defence = ranged ?
-						  bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_RANGED):
-						  bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_MELEE);
+						  bonusCache.getBonusValue(UnitBonusValuesProxy::DEFENCE_RANGED):
+						  bonusCache.getBonusValue(UnitBonusValuesProxy::DEFENCE_MELEE);
 		vstd::amax(defence, 0);
 		vstd::amax(defence, 0);
 		return defence;
 		return defence;
 	}
 	}

+ 1 - 40
lib/battle/CUnitState.h

@@ -125,45 +125,6 @@ private:
 	int32_t resurrected;
 	int32_t resurrected;
 };
 };
 
 
-class UnitBonusValuesProxy
-{
-public:
-	enum ECacheKeys : uint8_t
-	{
-		TOTAL_ATTACKS_MELEE,
-		TOTAL_ATTACKS_RANGED,
-
-		MIN_DAMAGE_MELEE,
-		MIN_DAMAGE_RANGED,
-		MAX_DAMAGE_MELEE,
-		MAX_DAMAGE_RANGED,
-
-		ATTACK_MELEE,
-		ATTACK_RANGED,
-
-		DEFENCE_MELEE,
-		DEFENCE_RANGED,
-
-		IN_FRENZY,
-		HYPNOTIZED,
-		FORGETFULL,
-		HAS_FREE_SHOOTING,
-		STACK_HEALTH,
-
-		TOTAL_KEYS,
-	};
-
-	static constexpr size_t KEYS_COUNT = static_cast<size_t>(ECacheKeys::TOTAL_KEYS);
-
-	BonusValuesArrayCache<ECacheKeys, KEYS_COUNT> cache;
-
-	using SelectorsArray = BonusValuesArrayCache<ECacheKeys, KEYS_COUNT>::SelectorsArray;
-
-	UnitBonusValuesProxy(const IBonusBearer * Target, const SelectorsArray * selectors):
-		cache(Target, selectors)
-	{}
-};
-
 class DLL_LINKAGE CUnitState : public Unit
 class DLL_LINKAGE CUnitState : public Unit
 {
 {
 public:
 public:
@@ -248,7 +209,7 @@ public:
 	int32_t getFirstHPleft() const override;
 	int32_t getFirstHPleft() const override;
 	int64_t getAvailableHealth() const override;
 	int64_t getAvailableHealth() const override;
 	int64_t getTotalHealth() const override;
 	int64_t getTotalHealth() const override;
-	int64_t getMaxHealth() const override;
+	uint32_t getMaxHealth() const override;
 
 
 	BattleHex getPosition() const override;
 	BattleHex getPosition() const override;
 	void setPosition(BattleHex hex) override;
 	void setPosition(BattleHex hex) override;

+ 29 - 0
lib/bonuses/BonusCache.cpp

@@ -50,6 +50,35 @@ int BonusValueCache::getValue() const
 	return getBonusValueImpl(value, selector, BonusCacheMode::VALUE);
 	return getBonusValueImpl(value, selector, BonusCacheMode::VALUE);
 }
 }
 
 
+MagicSchoolMasteryCache::MagicSchoolMasteryCache(const IBonusBearer * target)
+	:target(target)
+{}
+
+void MagicSchoolMasteryCache::update() const
+{
+	static const CSelector allBonusesSelector = Selector::type()(BonusType::MAGIC_SCHOOL_SKILL);
+	static const std::array schoolsSelector = {
+		Selector::subtype()(SpellSchool::ANY),
+		Selector::subtype()(SpellSchool::AIR),
+		Selector::subtype()(SpellSchool::FIRE),
+		Selector::subtype()(SpellSchool::WATER),
+		Selector::subtype()(SpellSchool::EARTH),
+	};
+
+	auto list = target->getBonuses(allBonusesSelector);
+	for (int i = 0; i < schoolsSelector.size(); ++i)
+		schools[i] = list->valOfBonuses(schoolsSelector[i]);
+
+	version = target->getTreeVersion();
+}
+
+int32_t MagicSchoolMasteryCache::getMastery(const SpellSchool & school) const
+{
+	if (target->getTreeVersion() != version)
+		update();
+	return schools[school.num + 1];
+}
+
 PrimarySkillsCache::PrimarySkillsCache(const IBonusBearer * target)
 PrimarySkillsCache::PrimarySkillsCache(const IBonusBearer * target)
 	:target(target)
 	:target(target)
 {}
 {}

+ 68 - 5
lib/bonuses/BonusCache.h

@@ -48,7 +48,7 @@ public:
 };
 };
 
 
 /// Cache that can track a list of queries to bonus system
 /// Cache that can track a list of queries to bonus system
-template<typename EnumType, size_t SIZE>
+template<size_t SIZE>
 class BonusValuesArrayCache : public BonusCacheBase
 class BonusValuesArrayCache : public BonusCacheBase
 {
 {
 public:
 public:
@@ -59,15 +59,13 @@ public:
 		, selectors(selectors)
 		, selectors(selectors)
 	{}
 	{}
 
 
-	int getBonusValue(EnumType which) const
+	int getBonusValue(int index) const
 	{
 	{
-		auto index = static_cast<size_t>(which);
 		return getBonusValueImpl(cache[index], (*selectors)[index], BonusCacheMode::VALUE);
 		return getBonusValueImpl(cache[index], (*selectors)[index], BonusCacheMode::VALUE);
 	}
 	}
 
 
-	int hasBonus(EnumType which) const
+	int hasBonus(int index) const
 	{
 	{
-		auto index = static_cast<size_t>(which);
 		return getBonusValueImpl(cache[index], (*selectors)[index], BonusCacheMode::PRESENCE);
 		return getBonusValueImpl(cache[index], (*selectors)[index], BonusCacheMode::PRESENCE);
 	}
 	}
 
 
@@ -78,6 +76,57 @@ private:
 	mutable CacheArray cache;
 	mutable CacheArray cache;
 };
 };
 
 
+class UnitBonusValuesProxy
+{
+public:
+	enum ECacheKeys : int8_t
+	{
+		TOTAL_ATTACKS_MELEE,
+		TOTAL_ATTACKS_RANGED,
+
+		MIN_DAMAGE_MELEE,
+		MIN_DAMAGE_RANGED,
+		MAX_DAMAGE_MELEE,
+		MAX_DAMAGE_RANGED,
+
+		ATTACK_MELEE,
+		ATTACK_RANGED,
+
+		DEFENCE_MELEE,
+		DEFENCE_RANGED,
+
+		IN_FRENZY,
+		HYPNOTIZED,
+		FORGETFULL,
+		HAS_FREE_SHOOTING,
+		STACK_HEALTH,
+
+		TOTAL_KEYS,
+	};
+	static constexpr size_t KEYS_COUNT = static_cast<size_t>(ECacheKeys::TOTAL_KEYS);
+
+	using SelectorsArray = BonusValuesArrayCache<KEYS_COUNT>::SelectorsArray;
+
+	UnitBonusValuesProxy(const IBonusBearer * Target, const SelectorsArray * selectors):
+		cache(Target, selectors)
+	{}
+
+	int getBonusValue(ECacheKeys which) const
+	{
+		auto index = static_cast<size_t>(which);
+		return cache.getBonusValue(index);
+	}
+
+	int hasBonus(ECacheKeys which) const
+	{
+		auto index = static_cast<size_t>(which);
+		return cache.hasBonus(index);
+	}
+
+private:
+	BonusValuesArrayCache<KEYS_COUNT> cache;
+};
+
 /// Cache that tracks values of primary skill values in bonus system
 /// Cache that tracks values of primary skill values in bonus system
 class PrimarySkillsCache
 class PrimarySkillsCache
 {
 {
@@ -92,6 +141,20 @@ public:
 	const std::array<std::atomic<int32_t>, 4> & getSkills() const;
 	const std::array<std::atomic<int32_t>, 4> & getSkills() const;
 };
 };
 
 
+/// Cache that tracks values of spell school mastery in bonus system
+class MagicSchoolMasteryCache
+{
+	const IBonusBearer * target;
+	mutable std::atomic<int64_t> version = 0;
+	mutable std::array<std::atomic<int32_t>, 4+1> schools;
+
+	void update() const;
+public:
+	MagicSchoolMasteryCache(const IBonusBearer * target);
+
+	int32_t getMastery(const SpellSchool & school) const;
+};
+
 /// Cache that tracks values for different values of bonus duration
 /// Cache that tracks values for different values of bonus duration
 class BonusCachePerTurn : public BonusCacheBase
 class BonusCachePerTurn : public BonusCacheBase
 {
 {

+ 5 - 4
lib/mapObjects/CGHeroInstance.cpp

@@ -294,6 +294,8 @@ CGHeroInstance::CGHeroInstance(IGameCallback * cb)
 	exp(UNINITIALIZED_EXPERIENCE),
 	exp(UNINITIALIZED_EXPERIENCE),
 	gender(EHeroGender::DEFAULT),
 	gender(EHeroGender::DEFAULT),
 	primarySkills(this),
 	primarySkills(this),
+	magicSchoolMastery(this),
+	manaPerKnowledgeCached(this, Selector::type()(BonusType::MANA_PER_KNOWLEDGE_PERCENTAGE)),
 	lowestCreatureSpeed(0)
 	lowestCreatureSpeed(0)
 {
 {
 	setNodeType(HERO);
 	setNodeType(HERO);
@@ -789,7 +791,7 @@ int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, SpellSc
 
 
 	spell->forEachSchool([&, this](const SpellSchool & cnf, bool & stop)
 	spell->forEachSchool([&, this](const SpellSchool & cnf, bool & stop)
 	{
 	{
-		int32_t thisSchool = valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, BonusSubtypeID(cnf)); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
+		int32_t thisSchool = magicSchoolMastery.getMastery(cnf); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
 		if(thisSchool > skill)
 		if(thisSchool > skill)
 		{
 		{
 			skill = thisSchool;
 			skill = thisSchool;
@@ -798,7 +800,7 @@ int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, SpellSc
 		}
 		}
 	});
 	});
 
 
-	vstd::amax(skill, valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, BonusSubtypeID(SpellSchool::ANY))); //any school bonus
+	vstd::amax(skill, magicSchoolMastery.getMastery(SpellSchool::ANY)); //any school bonus
 	vstd::amax(skill, valOfBonuses(BonusType::SPELL, BonusSubtypeID(spell->getId()))); //given by artifact or other effect
 	vstd::amax(skill, valOfBonuses(BonusType::SPELL, BonusSubtypeID(spell->getId()))); //given by artifact or other effect
 
 
 	vstd::amax(skill, 0); //in case we don't know any school
 	vstd::amax(skill, 0); //in case we don't know any school
@@ -1187,8 +1189,7 @@ std::string CGHeroInstance::nodeName() const
 
 
 si32 CGHeroInstance::manaLimit() const
 si32 CGHeroInstance::manaLimit() const
 {
 {
-	return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE)
-		* (valOfBonuses(BonusType::MANA_PER_KNOWLEDGE_PERCENTAGE))) / 100;
+	return getPrimSkillLevel(PrimarySkill::KNOWLEDGE) * manaPerKnowledgeCached.getValue() / 100;
 }
 }
 
 
 HeroTypeID CGHeroInstance::getPortraitSource() const
 HeroTypeID CGHeroInstance::getPortraitSource() const

+ 2 - 0
lib/mapObjects/CGHeroInstance.h

@@ -60,6 +60,8 @@ class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator,
 
 
 private:
 private:
 	PrimarySkillsCache primarySkills;
 	PrimarySkillsCache primarySkills;
+	MagicSchoolMasteryCache magicSchoolMastery;
+	BonusValueCache manaPerKnowledgeCached;
 
 
 	std::set<SpellID> spells; //known spells (spell IDs)
 	std::set<SpellID> spells; //known spells (spell IDs)
 	mutable int lowestCreatureSpeed;
 	mutable int lowestCreatureSpeed;