1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-10 22:31:40 +02:00

Added cache for common hero-based bonuses

This commit is contained in:
Ivan Savenko
2024-12-25 23:04:15 +00:00
parent 157d6d30c8
commit 919588f7ff
6 changed files with 124 additions and 68 deletions

View File

@@ -528,14 +528,14 @@ bool CUnitState::isCaster() const
bool CUnitState::canShootBlocked() const
{
return bonusCache.cache.getBonusValue(UnitBonusValuesProxy::HAS_FREE_SHOOTING);
return bonusCache.getBonusValue(UnitBonusValuesProxy::HAS_FREE_SHOOTING);
}
bool CUnitState::canShoot() const
{
return
shots.canUse(1) &&
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::FORGETFULL) <= 1; //advanced+ level
bonusCache.getBonusValue(UnitBonusValuesProxy::FORGETFULL) <= 1; //advanced+ level
}
bool CUnitState::isShooter() const
@@ -570,9 +570,9 @@ int64_t CUnitState::getTotalHealth() const
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
@@ -700,43 +700,43 @@ BattlePhases::Type CUnitState::battleQueuePhase(int turn) const
bool CUnitState::isHypnotized() const
{
return bonusCache.cache.getBonusValue(UnitBonusValuesProxy::HYPNOTIZED);
return bonusCache.getBonusValue(UnitBonusValuesProxy::HYPNOTIZED);
}
int CUnitState::getTotalAttacks(bool ranged) const
{
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
{
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
{
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 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)
{
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;
attack += frenzyBonus;
@@ -748,7 +748,7 @@ int CUnitState::getAttack(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)
{
@@ -757,8 +757,8 @@ int CUnitState::getDefense(bool ranged) const
else
{
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);
return defence;
}

View File

@@ -125,45 +125,6 @@ private:
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
{
public:
@@ -248,7 +209,7 @@ public:
int32_t getFirstHPleft() const override;
int64_t getAvailableHealth() const override;
int64_t getTotalHealth() const override;
int64_t getMaxHealth() const override;
uint32_t getMaxHealth() const override;
BattleHex getPosition() const override;
void setPosition(BattleHex hex) override;

View File

@@ -50,6 +50,35 @@ int BonusValueCache::getValue() const
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)
:target(target)
{}

View File

@@ -48,7 +48,7 @@ public:
};
/// 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
{
public:
@@ -59,15 +59,13 @@ public:
, 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);
}
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);
}
@@ -78,6 +76,57 @@ private:
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
class PrimarySkillsCache
{
@@ -92,6 +141,20 @@ public:
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
class BonusCachePerTurn : public BonusCacheBase
{

View File

@@ -294,6 +294,8 @@ CGHeroInstance::CGHeroInstance(IGameCallback * cb)
exp(UNINITIALIZED_EXPERIENCE),
gender(EHeroGender::DEFAULT),
primarySkills(this),
magicSchoolMastery(this),
manaPerKnowledgeCached(this, Selector::type()(BonusType::MANA_PER_KNOWLEDGE_PERCENTAGE)),
lowestCreatureSpeed(0)
{
setNodeType(HERO);
@@ -789,7 +791,7 @@ int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, SpellSc
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)
{
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, 0); //in case we don't know any school
@@ -1187,8 +1189,7 @@ std::string CGHeroInstance::nodeName() 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

View File

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