|
@@ -0,0 +1,208 @@
|
|
|
+/*
|
|
|
+ * CBonusProxy.cpp, part of VCMI engine
|
|
|
+ *
|
|
|
+ * Authors: listed in file AUTHORS in main folder
|
|
|
+ *
|
|
|
+ * License: GNU General Public License v2.0 or later
|
|
|
+ * Full text of license available in license.txt file, in main folder
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+#include "StdInc.h"
|
|
|
+#include "CBonusProxy.h"
|
|
|
+
|
|
|
+VCMI_LIB_NAMESPACE_BEGIN
|
|
|
+
|
|
|
+///CBonusProxy
|
|
|
+CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector):
|
|
|
+ bonusListCachedLast(0),
|
|
|
+ target(Target),
|
|
|
+ selector(std::move(Selector)),
|
|
|
+ currentBonusListIndex(0)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+CBonusProxy::CBonusProxy(const CBonusProxy & other):
|
|
|
+ bonusListCachedLast(other.bonusListCachedLast),
|
|
|
+ target(other.target),
|
|
|
+ selector(other.selector),
|
|
|
+ currentBonusListIndex(other.currentBonusListIndex)
|
|
|
+{
|
|
|
+ bonusList[currentBonusListIndex] = other.bonusList[currentBonusListIndex];
|
|
|
+}
|
|
|
+
|
|
|
+CBonusProxy::CBonusProxy(CBonusProxy && other) noexcept:
|
|
|
+ bonusListCachedLast(0),
|
|
|
+ target(other.target),
|
|
|
+ currentBonusListIndex(0)
|
|
|
+{
|
|
|
+ std::swap(bonusListCachedLast, other.bonusListCachedLast);
|
|
|
+ std::swap(selector, other.selector);
|
|
|
+ std::swap(bonusList, other.bonusList);
|
|
|
+ std::swap(currentBonusListIndex, other.currentBonusListIndex);
|
|
|
+}
|
|
|
+
|
|
|
+CBonusProxy & CBonusProxy::operator=(const CBonusProxy & other)
|
|
|
+{
|
|
|
+ boost::lock_guard<boost::mutex> lock(swapGuard);
|
|
|
+
|
|
|
+ selector = other.selector;
|
|
|
+ swapBonusList(other.bonusList[other.currentBonusListIndex]);
|
|
|
+ bonusListCachedLast = other.bonusListCachedLast;
|
|
|
+
|
|
|
+ return *this;
|
|
|
+}
|
|
|
+
|
|
|
+CBonusProxy & CBonusProxy::operator=(CBonusProxy && other) noexcept
|
|
|
+{
|
|
|
+ std::swap(bonusListCachedLast, other.bonusListCachedLast);
|
|
|
+ std::swap(selector, other.selector);
|
|
|
+ std::swap(bonusList, other.bonusList);
|
|
|
+ std::swap(currentBonusListIndex, other.currentBonusListIndex);
|
|
|
+
|
|
|
+ return *this;
|
|
|
+}
|
|
|
+
|
|
|
+void CBonusProxy::swapBonusList(TConstBonusListPtr other) const
|
|
|
+{
|
|
|
+ // The idea here is to avoid changing active bonusList while it can be read by a different thread.
|
|
|
+ // Because such use of shared ptr is not thread safe
|
|
|
+ // So to avoid this we change the second offline instance and swap active index
|
|
|
+ auto newCurrent = 1 - currentBonusListIndex;
|
|
|
+ bonusList[newCurrent] = std::move(other);
|
|
|
+ currentBonusListIndex = newCurrent;
|
|
|
+}
|
|
|
+
|
|
|
+TConstBonusListPtr CBonusProxy::getBonusList() const
|
|
|
+{
|
|
|
+ auto needUpdateBonusList = [&]() -> bool
|
|
|
+ {
|
|
|
+ return target->getTreeVersion() != bonusListCachedLast || !bonusList[currentBonusListIndex];
|
|
|
+ };
|
|
|
+
|
|
|
+ // avoid locking if everything is up-to-date
|
|
|
+ if(needUpdateBonusList())
|
|
|
+ {
|
|
|
+ boost::lock_guard<boost::mutex>lock(swapGuard);
|
|
|
+
|
|
|
+ if(needUpdateBonusList())
|
|
|
+ {
|
|
|
+ //TODO: support limiters
|
|
|
+ swapBonusList(target->getAllBonuses(selector, Selector::all));
|
|
|
+ bonusListCachedLast = target->getTreeVersion();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return bonusList[currentBonusListIndex];
|
|
|
+}
|
|
|
+
|
|
|
+const BonusList * CBonusProxy::operator->() const
|
|
|
+{
|
|
|
+ return getBonusList().get();
|
|
|
+}
|
|
|
+
|
|
|
+CTotalsProxy::CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue):
|
|
|
+ CBonusProxy(Target, std::move(Selector)),
|
|
|
+ initialValue(InitialValue),
|
|
|
+ meleeCachedLast(0),
|
|
|
+ meleeValue(0),
|
|
|
+ rangedCachedLast(0),
|
|
|
+ rangedValue(0)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+CTotalsProxy::CTotalsProxy(const CTotalsProxy & other)
|
|
|
+ : CBonusProxy(other),
|
|
|
+ initialValue(other.initialValue),
|
|
|
+ meleeCachedLast(other.meleeCachedLast),
|
|
|
+ meleeValue(other.meleeValue),
|
|
|
+ rangedCachedLast(other.rangedCachedLast),
|
|
|
+ rangedValue(other.rangedValue)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+int CTotalsProxy::getValue() const
|
|
|
+{
|
|
|
+ const auto treeVersion = target->getTreeVersion();
|
|
|
+
|
|
|
+ if(treeVersion != valueCachedLast)
|
|
|
+ {
|
|
|
+ auto bonuses = getBonusList();
|
|
|
+
|
|
|
+ value = initialValue + bonuses->totalValue();
|
|
|
+ valueCachedLast = treeVersion;
|
|
|
+ }
|
|
|
+ return value;
|
|
|
+}
|
|
|
+
|
|
|
+int CTotalsProxy::getValueAndList(TConstBonusListPtr & outBonusList) const
|
|
|
+{
|
|
|
+ const auto treeVersion = target->getTreeVersion();
|
|
|
+ outBonusList = getBonusList();
|
|
|
+
|
|
|
+ if(treeVersion != valueCachedLast)
|
|
|
+ {
|
|
|
+ value = initialValue + outBonusList->totalValue();
|
|
|
+ valueCachedLast = 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(std::move(Selector)),
|
|
|
+ cachedLast(0),
|
|
|
+ hasBonus(false)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+//This constructor should be placed here to avoid side effects
|
|
|
+CCheckProxy::CCheckProxy(const CCheckProxy & other) = default;
|
|
|
+
|
|
|
+bool CCheckProxy::getHasBonus() const
|
|
|
+{
|
|
|
+ const auto treeVersion = target->getTreeVersion();
|
|
|
+
|
|
|
+ if(treeVersion != cachedLast)
|
|
|
+ {
|
|
|
+ hasBonus = target->hasBonus(selector);
|
|
|
+ cachedLast = treeVersion;
|
|
|
+ }
|
|
|
+
|
|
|
+ return hasBonus;
|
|
|
+}
|
|
|
+
|
|
|
+VCMI_LIB_NAMESPACE_END
|