BonusCache.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * BonusCache.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "BonusCache.h"
  12. #include "IBonusBearer.h"
  13. #include "BonusSelector.h"
  14. #include "BonusList.h"
  15. #include "../GameLibrary.h"
  16. #include "../IGameSettings.h"
  17. #include "../spells/SpellSchoolHandler.h"
  18. VCMI_LIB_NAMESPACE_BEGIN
  19. int BonusCacheBase::getBonusValueImpl(BonusCacheEntry & currentValue, const CSelector & selector, BonusCacheMode mode) const
  20. {
  21. if (target->getTreeVersion() == currentValue.version)
  22. {
  23. return currentValue.value;
  24. }
  25. else
  26. {
  27. // NOTE: following code theoretically can fail if bonus tree was changed by another thread between two following lines
  28. // However, this situation should not be possible - gamestate modification should only happen in single-treaded mode with locked gamestate mutex
  29. int newValue;
  30. if (mode == BonusCacheMode::VALUE)
  31. newValue = target->valOfBonuses(selector);
  32. else
  33. newValue = target->hasBonus(selector);
  34. currentValue.value = newValue;
  35. currentValue.version = target->getTreeVersion();
  36. return newValue;
  37. }
  38. }
  39. BonusValueCache::BonusValueCache(const IBonusBearer * target, const CSelector & selector)
  40. :BonusCacheBase(target),selector(selector)
  41. {}
  42. int BonusValueCache::getValue() const
  43. {
  44. return getBonusValueImpl(value, selector, BonusCacheMode::VALUE);
  45. }
  46. bool BonusValueCache::hasBonus() const
  47. {
  48. return getBonusValueImpl(value, selector, BonusCacheMode::PRESENCE);
  49. }
  50. MagicSchoolMasteryCache::MagicSchoolMasteryCache(const IBonusBearer * target)
  51. :target(target)
  52. ,schools(LIBRARY->spellSchoolHandler->getAllObjects().size() + 1)
  53. {}
  54. void MagicSchoolMasteryCache::update() const
  55. {
  56. static const CSelector allBonusesSelector = Selector::type()(BonusType::MAGIC_SCHOOL_SKILL);
  57. auto list = target->getBonuses(allBonusesSelector);
  58. schools[0] = list->valOfBonuses(Selector::subtype()(SpellSchool::ANY));
  59. for (int i = 1; i < schools.size(); ++i)
  60. schools[i] = list->valOfBonuses(Selector::subtype()(SpellSchool(i-1)));
  61. version = target->getTreeVersion();
  62. }
  63. int32_t MagicSchoolMasteryCache::getMastery(const SpellSchool & school) const
  64. {
  65. if (target->getTreeVersion() != version)
  66. update();
  67. return schools[school.num + 1];
  68. }
  69. PrimarySkillsCache::PrimarySkillsCache(const IBonusBearer * target)
  70. :target(target)
  71. {}
  72. void PrimarySkillsCache::update() const
  73. {
  74. static const CSelector primarySkillsSelector = Selector::type()(BonusType::PRIMARY_SKILL);
  75. static const CSelector attackSelector = Selector::subtype()(PrimarySkill::ATTACK);
  76. static const CSelector defenceSelector = Selector::subtype()(PrimarySkill::DEFENSE);
  77. static const CSelector spellPowerSelector = Selector::subtype()(PrimarySkill::SPELL_POWER);
  78. static const CSelector knowledgeSelector = Selector::subtype()(PrimarySkill::KNOWLEDGE);
  79. std::array<int, 4> minValues = {
  80. LIBRARY->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::ATTACK),
  81. LIBRARY->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::DEFENSE),
  82. LIBRARY->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::SPELL_POWER),
  83. LIBRARY->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::KNOWLEDGE)
  84. };
  85. auto list = target->getBonuses(primarySkillsSelector);
  86. skills[PrimarySkill::ATTACK] = std::max(minValues[PrimarySkill::ATTACK], list->valOfBonuses(attackSelector));
  87. skills[PrimarySkill::DEFENSE] = std::max(minValues[PrimarySkill::DEFENSE], list->valOfBonuses(defenceSelector));
  88. skills[PrimarySkill::SPELL_POWER] = std::max(minValues[PrimarySkill::SPELL_POWER], list->valOfBonuses(spellPowerSelector));
  89. skills[PrimarySkill::KNOWLEDGE] = std::max(minValues[PrimarySkill::KNOWLEDGE], list->valOfBonuses(knowledgeSelector));
  90. version = target->getTreeVersion();
  91. }
  92. const std::array<std::atomic<int32_t>, 4> & PrimarySkillsCache::getSkills() const
  93. {
  94. if (target->getTreeVersion() != version)
  95. update();
  96. return skills;
  97. }
  98. int BonusCachePerTurn::getValueUncached(int turns) const
  99. {
  100. std::lock_guard lock(bonusListMutex);
  101. int nodeTreeVersion = target->getTreeVersion();
  102. if (bonusListVersion != nodeTreeVersion)
  103. {
  104. bonusList = target->getBonuses(selector);
  105. bonusListVersion = nodeTreeVersion;
  106. }
  107. if (mode == BonusCacheMode::VALUE)
  108. {
  109. if (turns != 0)
  110. return bonusList->valOfBonuses(Selector::turns(turns));
  111. else
  112. return bonusList->totalValue();
  113. }
  114. else
  115. {
  116. if (turns != 0)
  117. return bonusList->getFirst(Selector::turns(turns)) != nullptr;
  118. else
  119. return !bonusList->empty();
  120. }
  121. }
  122. int BonusCachePerTurn::getValue(int turns) const
  123. {
  124. int nodeTreeVersion = target->getTreeVersion();
  125. if (turns < cachedTurns)
  126. {
  127. auto & entry = cache[turns];
  128. if (entry.version == nodeTreeVersion)
  129. {
  130. // best case: value is in cache and up-to-date
  131. return entry.value;
  132. }
  133. else
  134. {
  135. // else - compute value and update it in the cache
  136. int newValue = getValueUncached(turns);
  137. entry.value = newValue;
  138. entry.version = nodeTreeVersion;
  139. return newValue;
  140. }
  141. }
  142. else
  143. {
  144. // non-cacheable value - compute and return (should be 0 / close to 0 calls)
  145. return getValueUncached(turns);
  146. }
  147. }
  148. const UnitBonusValuesProxy::SelectorsArray * UnitBonusValuesProxy::generateSelectors()
  149. {
  150. static const CSelector additionalAttack = Selector::type()(BonusType::ADDITIONAL_ATTACK);
  151. static const CSelector selectorMelee = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_MELEE_FIGHT));
  152. static const CSelector selectorRanged = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_DISTANCE_FIGHT));
  153. static const CSelector minDamage = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageBoth).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMin));
  154. static const CSelector maxDamage = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageBoth).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMax));
  155. static const CSelector attack = Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::ATTACK));
  156. static const CSelector defence = Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::DEFENSE));
  157. static const UnitBonusValuesProxy::SelectorsArray selectors = {
  158. additionalAttack.And(selectorMelee), //TOTAL_ATTACKS_MELEE,
  159. additionalAttack.And(selectorRanged), //TOTAL_ATTACKS_RANGED,
  160. minDamage.And(selectorMelee), //MIN_DAMAGE_MELEE,
  161. minDamage.And(selectorRanged), //MIN_DAMAGE_RANGED,
  162. maxDamage.And(selectorMelee), //MAX_DAMAGE_MELEE,
  163. maxDamage.And(selectorRanged), //MAX_DAMAGE_RANGED,
  164. attack.And(selectorMelee),//ATTACK_MELEE,
  165. attack.And(selectorRanged),//ATTACK_RANGED,
  166. defence.And(selectorMelee),//DEFENCE_MELEE,
  167. defence.And(selectorRanged),//DEFENCE_RANGED,
  168. Selector::type()(BonusType::IN_FRENZY),//IN_FRENZY,
  169. Selector::type()(BonusType::HYPNOTIZED),//HYPNOTIZED,
  170. Selector::type()(BonusType::FORGETFULL),//FORGETFULL,
  171. Selector::type()(BonusType::FREE_SHOOTING).Or(Selector::type()(BonusType::SIEGE_WEAPON)),//HAS_FREE_SHOOTING,
  172. Selector::type()(BonusType::STACK_HEALTH),//STACK_HEALTH,
  173. Selector::type()(BonusType::INVINCIBLE),//INVINCIBLE,
  174. Selector::type()(BonusType::NONE).And(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(SpellID(SpellID::CLONE))))
  175. };
  176. return &selectors;
  177. }
  178. VCMI_LIB_NAMESPACE_END