BonusCache.cpp 7.6 KB

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