diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index e8a8c9ab4..62e991931 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -152,6 +152,120 @@ const BonusList * CBonusProxy::operator->() const return get().get(); } +CTotalsProxy::CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue) + : target(Target), + selector(Selector), + initialValue(InitialValue), + meleeCachedLast(0), + meleeValue(0), + rangedCachedLast(0), + rangedValue(0), + value(0), + cachedLast(0) +{ +} + +CTotalsProxy::CTotalsProxy(const CTotalsProxy & other) + : target(other.target), + selector(other.selector), + initialValue(other.initialValue), + meleeCachedLast(other.meleeCachedLast), + meleeValue(other.meleeValue), + rangedCachedLast(other.rangedCachedLast), + rangedValue(other.rangedValue) +{ +} + +CTotalsProxy & CTotalsProxy::operator=(const CTotalsProxy & other) +{ + initialValue = other.initialValue; + meleeCachedLast = other.meleeCachedLast; + meleeValue = other.meleeValue; + rangedCachedLast = other.rangedCachedLast; + rangedValue = other.rangedValue; + value = other.value; + cachedLast = other.cachedLast; + + return *this; +} + +int CTotalsProxy::getValue() const +{ + const auto treeVersion = target->getTreeVersion(); + + if(treeVersion != cachedLast) + { + auto bonuses = target->getBonuses(selector); + + value = initialValue + bonuses->totalValue(); + cachedLast = treeVersion; + } + + return value; +} + +int CTotalsProxy::getMeleeValue() const +{ + static const auto limit = Selector::effectRange(Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_MELEE_FIGHT)); + + const auto treeVersion = target->getTreeVersion(); + + if(treeVersion != meleeCachedLast) + { + auto bonuses = target->getBonuses(selector, limit); + meleeValue = initialValue + bonuses->totalValue(); + meleeCachedLast = treeVersion; + } + + return meleeValue; +} + +int CTotalsProxy::getRangedValue() const +{ + static const auto limit = Selector::effectRange(Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)); + + const auto treeVersion = target->getTreeVersion(); + + if(treeVersion != rangedCachedLast) + { + auto bonuses = target->getBonuses(selector, limit); + rangedValue = initialValue + bonuses->totalValue(); + rangedCachedLast = treeVersion; + } + + return rangedValue; +} + +///CCheckProxy +CCheckProxy::CCheckProxy(const IBonusBearer * Target, CSelector Selector) + : target(Target), + selector(Selector), + cachedLast(0), + hasBonus(false) +{ +} + +CCheckProxy::CCheckProxy(const CCheckProxy & other) + : target(other.target), + selector(other.selector), + cachedLast(other.cachedLast), + hasBonus(other.hasBonus) +{ +} + +bool CCheckProxy::getHasBonus() const +{ + const auto treeVersion = target->getTreeVersion(); + + if(treeVersion != cachedLast) + { + hasBonus = target->hasBonus(selector); + cachedLast = treeVersion; + } + + return hasBonus; +} + CAddInfo::CAddInfo() { } @@ -467,6 +581,22 @@ void BonusList::insert(BonusList::TInternalContainer::iterator position, BonusLi changed(); } +CSelector IBonusBearer::anaffectedByMoraleSelector + = Selector::type(Bonus::NON_LIVING) + .Or(Selector::type(Bonus::UNDEAD)) + .Or(Selector::type(Bonus::NO_MORALE)) + .Or(Selector::type(Bonus::SIEGE_WEAPON)); + +CSelector IBonusBearer::moraleSelector = Selector::type(Bonus::MORALE); +CSelector IBonusBearer::selfMoraleSelector = Selector::type(Bonus::SELF_MORALE); + +IBonusBearer::IBonusBearer() + :anaffectedByMorale(this, anaffectedByMoraleSelector), + moraleValue(this, moraleSelector, 0), + selfMorale(this, selfMoraleSelector) +{ +} + int IBonusBearer::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const { return valOfBonuses(Selector::type(type).And(selector)); @@ -532,13 +662,12 @@ bool IBonusBearer::hasBonusFrom(Bonus::BonusSource source, ui32 sourceID) const int IBonusBearer::MoraleVal() const { - if(hasBonusOfType(Bonus::NON_LIVING) || hasBonusOfType(Bonus::UNDEAD) || - hasBonusOfType(Bonus::NO_MORALE) || hasBonusOfType(Bonus::SIEGE_WEAPON)) + if(anaffectedByMorale.getHasBonus()) return 0; - int ret = valOfBonuses(Bonus::MORALE); + int ret = moraleValue.getValue(); - if(hasBonusOfType(Bonus::SELF_MORALE)) //eg. minotaur + if(selfMorale.getHasBonus()) //eg. minotaur vstd::amax(ret, +1); return vstd::abetween(ret, -3, +3); diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 0c713885c..279ce4123 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -87,6 +87,51 @@ private: mutable TBonusListPtr data; }; +class DLL_LINKAGE CTotalsProxy +{ +public: + CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue); + CTotalsProxy(const CTotalsProxy & other); + CTotalsProxy(CTotalsProxy && other) = delete; + + CTotalsProxy & operator=(const CTotalsProxy & other); + CTotalsProxy & operator=(CTotalsProxy && other) = delete; + + int getMeleeValue() const; + int getRangedValue() const; + int getValue() const; + +private: + const IBonusBearer * target; + CSelector selector; + int initialValue; + + mutable int64_t cachedLast; + mutable int value; + + mutable int64_t meleeCachedLast; + mutable int meleeValue; + + mutable int64_t rangedCachedLast; + mutable int rangedValue; +}; + +class DLL_LINKAGE CCheckProxy +{ +public: + CCheckProxy(const IBonusBearer * Target, CSelector Selector); + CCheckProxy(const CCheckProxy & other); + + bool getHasBonus() const; + +private: + const IBonusBearer * target; + CSelector selector; + + mutable int64_t cachedLast; + mutable bool hasBonus; +}; + class DLL_LINKAGE CAddInfo : public std::vector { public: @@ -641,11 +686,20 @@ public: class DLL_LINKAGE IBonusBearer { +private: + static CSelector anaffectedByMoraleSelector; + CCheckProxy anaffectedByMorale; + static CSelector moraleSelector; + CTotalsProxy moraleValue; + static CSelector selfMoraleSelector; + CCheckProxy selfMorale; + public: //new bonusing node interface // * selector is predicate that tests if HeroBonus matches our criteria // * root is node on which call was made (nullptr will be replaced with this) //interface + IBonusBearer(); virtual const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const = 0; int valOfBonuses(const CSelector &selector, const std::string &cachingStr = "") const; bool hasBonus(const CSelector &selector, const std::string &cachingStr = "") const; diff --git a/lib/battle/CUnitState.cpp b/lib/battle/CUnitState.cpp index 2d2ecbe3e..828816d3b 100644 --- a/lib/battle/CUnitState.cpp +++ b/lib/battle/CUnitState.cpp @@ -19,99 +19,6 @@ namespace battle { - -CTotalsProxy::CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue) - : target(Target), - selector(Selector), - initialValue(InitialValue), - meleeCachedLast(0), - meleeValue(0), - rangedCachedLast(0), - rangedValue(0) -{ -} - -CTotalsProxy::CTotalsProxy(const CTotalsProxy & other) - : target(other.target), - selector(other.selector), - initialValue(other.initialValue), - meleeCachedLast(other.meleeCachedLast), - meleeValue(other.meleeValue), - rangedCachedLast(other.rangedCachedLast), - rangedValue(other.rangedValue) -{ -} - -CTotalsProxy & CTotalsProxy::operator=(const CTotalsProxy & other) -{ - initialValue = other.initialValue; - meleeCachedLast = other.meleeCachedLast; - meleeValue = other.meleeValue; - rangedCachedLast = other.rangedCachedLast; - rangedValue = other.rangedValue; - return *this; -} - -int CTotalsProxy::getMeleeValue() const -{ - static const auto limit = Selector::effectRange(Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_MELEE_FIGHT)); - - const auto treeVersion = target->getTreeVersion(); - - if(treeVersion != meleeCachedLast) - { - auto bonuses = target->getBonuses(selector, limit); - meleeValue = initialValue + bonuses->totalValue(); - meleeCachedLast = treeVersion; - } - return meleeValue; -} - -int CTotalsProxy::getRangedValue() const -{ - static const auto limit = Selector::effectRange(Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)); - - const auto treeVersion = target->getTreeVersion(); - - if(treeVersion != rangedCachedLast) - { - auto bonuses = target->getBonuses(selector, limit); - rangedValue = initialValue + bonuses->totalValue(); - rangedCachedLast = treeVersion; - } - return rangedValue; -} - -///CCheckProxy -CCheckProxy::CCheckProxy(const IBonusBearer * Target, CSelector Selector) - : target(Target), - selector(Selector), - cachedLast(0), - hasBonus(false) -{ -} - -CCheckProxy::CCheckProxy(const CCheckProxy & other) - : target(other.target), - selector(other.selector), - cachedLast(other.cachedLast), - hasBonus(other.hasBonus) -{ -} - -bool CCheckProxy::getHasBonus() const -{ - const auto treeVersion = target->getTreeVersion(); - - if(treeVersion != cachedLast) - { - hasBonus = target->hasBonus(selector); - cachedLast = treeVersion; - } - - return hasBonus; -} - ///CAmmo CAmmo::CAmmo(const battle::Unit * Owner, CSelector totalSelector) : used(0), diff --git a/lib/battle/CUnitState.h b/lib/battle/CUnitState.h index c5d065084..d00a4aae5 100644 --- a/lib/battle/CUnitState.h +++ b/lib/battle/CUnitState.h @@ -24,47 +24,6 @@ namespace battle { class CUnitState; -class DLL_LINKAGE CTotalsProxy -{ -public: - CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue); - CTotalsProxy(const CTotalsProxy & other); - CTotalsProxy(CTotalsProxy && other) = delete; - - CTotalsProxy & operator=(const CTotalsProxy & other); - CTotalsProxy & operator=(CTotalsProxy && other) = delete; - - int getMeleeValue() const; - int getRangedValue() const; - -private: - const IBonusBearer * target; - CSelector selector; - int initialValue; - - mutable int64_t meleeCachedLast; - mutable int meleeValue; - - mutable int64_t rangedCachedLast; - mutable int rangedValue; -}; - -class DLL_LINKAGE CCheckProxy -{ -public: - CCheckProxy(const IBonusBearer * Target, CSelector Selector); - CCheckProxy(const CCheckProxy & other); - - bool getHasBonus() const; - -private: - const IBonusBearer * target; - CSelector selector; - - mutable int64_t cachedLast; - mutable bool hasBonus; -}; - class DLL_LINKAGE CAmmo { public: diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/CArmedInstance.cpp index 8de7ceb01..b743d19a3 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/CArmedInstance.cpp @@ -36,7 +36,11 @@ void CArmedInstance::randomizeArmy(int type) return; } +// Take Angelic Alliance troop-mixing freedom of non-evil units into account. +CSelector CArmedInstance::nonEvilAlignmentMixSelector = Selector::type(Bonus::NONEVIL_ALIGNMENT_MIX); + CArmedInstance::CArmedInstance() + :nonEvilAlignmentMix(this, nonEvilAlignmentMixSelector) { battle = nullptr; } @@ -57,6 +61,9 @@ void CArmedInstance::updateMoraleBonusFromArmy() std::set factions; bool hasUndead = false; + const std::string undeadCacheKey = "type_UNDEAD"; + static const CSelector undeadSelector = Selector::type(Bonus::UNDEAD); + for(auto slot : Slots()) { const CStackInstance * inst = slot.second; @@ -64,13 +71,12 @@ void CArmedInstance::updateMoraleBonusFromArmy() factions.insert(creature->faction); // Check for undead flag instead of faction (undead mummies are neutral) - hasUndead |= inst->hasBonusOfType(Bonus::UNDEAD); + hasUndead |= inst->hasBonus(undeadSelector, undeadCacheKey); } size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account - // Take Angelic Alliance troop-mixing freedom of non-evil units into account. - if (hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX)) + if (nonEvilAlignmentMix.getHasBonus()) { size_t mixableFactions = 0; diff --git a/lib/mapObjects/CArmedInstance.h b/lib/mapObjects/CArmedInstance.h index b2ede9733..bb5d6a064 100644 --- a/lib/mapObjects/CArmedInstance.h +++ b/lib/mapObjects/CArmedInstance.h @@ -17,6 +17,10 @@ class CGameState; class DLL_LINKAGE CArmedInstance: public CGObjectInstance, public CBonusSystemNode, public CCreatureSet { +private: + CCheckProxy nonEvilAlignmentMix; + static CSelector nonEvilAlignmentMixSelector; + public: BattleInfo *battle; //set to the current battle, if engaged