mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-17 01:32:21 +02:00
Merge pull request #5844 from IvanSavenko/bonus_fixes
Fixes to bonus system functionality
This commit is contained in:
@ -133,11 +133,10 @@ SlotID StackWithBonuses::unitSlot() const
|
||||
return slot;
|
||||
}
|
||||
|
||||
TConstBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, const CSelector & limit,
|
||||
const std::string & cachingStr) const
|
||||
TConstBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, const std::string & cachingStr) const
|
||||
{
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
TConstBonusListPtr originalList = origBearer->getAllBonuses(selector, limit, cachingStr);
|
||||
TConstBonusListPtr originalList = origBearer->getAllBonuses(selector, cachingStr);
|
||||
|
||||
vstd::copy_if(*originalList, std::back_inserter(*ret), [this](const std::shared_ptr<Bonus> & b)
|
||||
{
|
||||
@ -147,7 +146,7 @@ TConstBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, c
|
||||
|
||||
for(const Bonus & bonus : bonusesToUpdate)
|
||||
{
|
||||
if(selector(&bonus) && (!limit || limit(&bonus)))
|
||||
if(selector(&bonus))
|
||||
{
|
||||
if(ret->getFirst(Selector::source(BonusSource::SPELL_EFFECT, bonus.sid).And(Selector::typeSubtype(bonus.type, bonus.subtype))))
|
||||
{
|
||||
@ -164,7 +163,7 @@ TConstBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, c
|
||||
for(auto & bonus : bonusesToAdd)
|
||||
{
|
||||
auto b = std::make_shared<Bonus>(bonus);
|
||||
if(selector(b.get()) && (!limit || !limit(b.get())))
|
||||
if(selector(b.get()))
|
||||
ret->push_back(b);
|
||||
}
|
||||
//TODO limiters?
|
||||
|
@ -90,8 +90,7 @@ public:
|
||||
SlotID unitSlot() const override;
|
||||
|
||||
///IBonusBearer
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit,
|
||||
const std::string & cachingStr = "") const override;
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const std::string & cachingStr = "") const override;
|
||||
|
||||
int32_t getTreeVersion() const override;
|
||||
|
||||
|
@ -271,7 +271,7 @@ bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
|
||||
|
||||
double getArtifactBonusRelevance(const CGHeroInstance * hero, const std::shared_ptr<Bonus> & bonus)
|
||||
{
|
||||
if (bonus->propagator && bonus->limiter && bonus->propagator->getPropagatorType() == CBonusSystemNode::BATTLE)
|
||||
if (bonus->propagator && bonus->limiter && bonus->propagator->getPropagatorType() == BonusNodeType::BATTLE_WIDE)
|
||||
{
|
||||
// assume that this is battle wide / other side propagator+limiter
|
||||
// consider it as fully relevant since we don't know about future combat when equipping artifacts
|
||||
@ -290,7 +290,7 @@ double getArtifactBonusRelevance(const CGHeroInstance * hero, const std::shared_
|
||||
|
||||
for (const auto & slot : hero->Slots())
|
||||
{
|
||||
const auto allBonuses = slot.second->getAllBonuses(Selector::all, Selector::all);
|
||||
const auto allBonuses = slot.second->getAllBonuses(Selector::all);
|
||||
BonusLimitationContext context = {*bonus, *slot.second, *allBonuses, stillUndecided};
|
||||
|
||||
uint64_t unitStrength = slot.second->getPower();
|
||||
@ -526,7 +526,7 @@ int32_t getArtifactBonusScoreImpl(const std::shared_ptr<Bonus> & bonus)
|
||||
|
||||
int32_t getArtifactBonusScore(const std::shared_ptr<Bonus> & bonus)
|
||||
{
|
||||
if (bonus->propagator && bonus->propagator->getPropagatorType() == CBonusSystemNode::BATTLE)
|
||||
if (bonus->propagator && bonus->propagator->getPropagatorType() == BonusNodeType::BATTLE_WIDE)
|
||||
{
|
||||
if (bonus->limiter)
|
||||
{
|
||||
|
@ -172,7 +172,7 @@ class TemporaryArmy : public CArmedInstance
|
||||
public:
|
||||
void armyChanged() override {}
|
||||
TemporaryArmy()
|
||||
:CArmedInstance(nullptr, true)
|
||||
:CArmedInstance(nullptr, BonusNodeType::UNKNOWN, true)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
bool needsLastStack() const override;
|
||||
std::shared_ptr<SpecialAction> getActorAction() const;
|
||||
|
||||
HeroExchangeArmy(): CArmedInstance(nullptr, true), requireBuyArmy(false) {}
|
||||
HeroExchangeArmy(): CArmedInstance(nullptr, BonusNodeType::UNKNOWN, true), requireBuyArmy(false) {}
|
||||
};
|
||||
|
||||
struct ExchangeResult
|
||||
|
14
Global.h
14
Global.h
@ -712,13 +712,16 @@ namespace vstd
|
||||
return a + (b - a) * f;
|
||||
}
|
||||
|
||||
/// Divides dividend by divisor and rounds result up
|
||||
/// Divides dividend by divisor and rounds result away from zero
|
||||
/// For use with integer-only arithmetic
|
||||
template<typename Integer1, typename Integer2>
|
||||
Integer1 divideAndCeil(const Integer1 & dividend, const Integer2 & divisor)
|
||||
{
|
||||
static_assert(std::is_integral_v<Integer1> && std::is_integral_v<Integer2>, "This function should only be used with integral types");
|
||||
return (dividend + divisor - 1) / divisor;
|
||||
if (dividend >= 0)
|
||||
return (dividend + divisor - 1) / divisor;
|
||||
else
|
||||
return (dividend - divisor + 1) / divisor;
|
||||
}
|
||||
|
||||
/// Divides dividend by divisor and rounds result to nearest
|
||||
@ -727,10 +730,13 @@ namespace vstd
|
||||
Integer1 divideAndRound(const Integer1 & dividend, const Integer2 & divisor)
|
||||
{
|
||||
static_assert(std::is_integral_v<Integer1> && std::is_integral_v<Integer2>, "This function should only be used with integral types");
|
||||
return (dividend + divisor / 2 - 1) / divisor;
|
||||
if (dividend >= 0)
|
||||
return (dividend + divisor / 2 - 1) / divisor;
|
||||
else
|
||||
return (dividend - divisor / 2 + 1) / divisor;
|
||||
}
|
||||
|
||||
/// Divides dividend by divisor and rounds result down
|
||||
/// Divides dividend by divisor and rounds result towards zero
|
||||
/// For use with integer-only arithmetic
|
||||
template<typename Integer1, typename Integer2>
|
||||
Integer1 divideAndFloor(const Integer1 & dividend, const Integer2 & divisor)
|
||||
|
@ -730,6 +730,7 @@
|
||||
"core.bonus.SUMMON_GUARDIANS.description" : "{Summon guardians}\nAt the start of battle summons ${subtype.creature} (${val}%)",
|
||||
"core.bonus.THREE_HEADED_ATTACK.description" : "{Three-headed attack}\nAttacks three adjacent units",
|
||||
"core.bonus.TRANSMUTATION.description" : "{Transmutation}\n${val}% chance to transform attacked unit to a different type",
|
||||
"core.bonus.TRANSMUTATION_IMMUNITY.description" : "{Transmutation Immunity}\nThis unit cannot be transformed into another unit by enemy attack",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Breath Attack}\nAttacks by this unit will also hit any unit positioned immediately behind the target",
|
||||
"core.bonus.UNDEAD.description" : "{Undead}\nCreature is Undead and is immune to effects that only affect living",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.description" : "{Unlimited retaliations}\nThis unit can retaliate against an unlimited number of attacks",
|
||||
|
@ -127,8 +127,8 @@ bool ArtifactsUIController::askToDisassemble(const CGHeroInstance * hero, const
|
||||
|
||||
void ArtifactsUIController::artifactRemoved()
|
||||
{
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<CWindowWithArtifacts>())
|
||||
artWin->update();
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<IArtifactsHolder>())
|
||||
artWin->updateArtifacts();
|
||||
GAME->interface()->waitWhileDialog();
|
||||
}
|
||||
|
||||
@ -139,10 +139,10 @@ void ArtifactsUIController::artifactMoved()
|
||||
numOfMovedArts--;
|
||||
|
||||
if(numOfMovedArts == 0)
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<CWindowWithArtifacts>())
|
||||
{
|
||||
artWin->update();
|
||||
}
|
||||
{
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<IArtifactsHolder>())
|
||||
artWin->updateArtifacts();
|
||||
}
|
||||
GAME->interface()->waitWhileDialog();
|
||||
}
|
||||
|
||||
@ -160,12 +160,12 @@ void ArtifactsUIController::bulkArtMovementStart(size_t totalNumOfArts, size_t p
|
||||
|
||||
void ArtifactsUIController::artifactAssembled()
|
||||
{
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<CWindowWithArtifacts>())
|
||||
artWin->update();
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<IArtifactsHolder>())
|
||||
artWin->updateArtifacts();
|
||||
}
|
||||
|
||||
void ArtifactsUIController::artifactDisassembled()
|
||||
{
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<CWindowWithArtifacts>())
|
||||
artWin->update();
|
||||
for(const auto & artWin : ENGINE->windows().findWindows<IArtifactsHolder>())
|
||||
artWin->updateArtifacts();
|
||||
}
|
||||
|
@ -440,14 +440,14 @@ void ClientCommandManager::handleBonusesCommand(std::istringstream & singleWordB
|
||||
return ss.str();
|
||||
};
|
||||
printCommandMessage("Bonuses of " + GAME->interface()->localState->getCurrentArmy()->getObjectName() + "\n");
|
||||
printCommandMessage(format(*GAME->interface()->localState->getCurrentArmy()->getAllBonuses(Selector::all, Selector::all)) + "\n");
|
||||
printCommandMessage(format(*GAME->interface()->localState->getCurrentArmy()->getAllBonuses(Selector::all)) + "\n");
|
||||
|
||||
printCommandMessage("\nInherited bonuses:\n");
|
||||
TCNodes parents;
|
||||
GAME->interface()->localState->getCurrentArmy()->getParents(parents);
|
||||
for(const CBonusSystemNode *parent : parents)
|
||||
{
|
||||
printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
|
||||
printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all)) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,6 +530,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
||||
int distance = attacker->position.isValid() ? owner.getBattle()->battleGetDistances(attacker, attacker->getPosition())[attackFromHex.toInt()] : 0;
|
||||
DamageEstimation retaliation;
|
||||
BattleAttackInfo attackInfo(attacker, targetStack, distance, false );
|
||||
attackInfo.attackerPos = attackFromHex;
|
||||
DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation);
|
||||
estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
|
||||
estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
|
||||
|
@ -805,7 +805,7 @@ void BattleStacksController::removeExpiredColorFilters()
|
||||
{
|
||||
if (!filter.persistent)
|
||||
{
|
||||
if (filter.source && !filter.target->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(filter.source->id)), Selector::all))
|
||||
if (filter.source && !filter.target->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(filter.source->id))))
|
||||
return true;
|
||||
if (filter.effectColor == Colors::TRANSPARENCY && filter.transparency == 255)
|
||||
return true;
|
||||
|
@ -162,6 +162,12 @@ public:
|
||||
virtual void updateGarrisons() = 0;
|
||||
};
|
||||
|
||||
class IArtifactsHolder
|
||||
{
|
||||
public:
|
||||
virtual void updateArtifacts() = 0;
|
||||
};
|
||||
|
||||
class IMarketHolder
|
||||
{
|
||||
public:
|
||||
|
@ -1476,6 +1476,13 @@ CCastleInterface::~CCastleInterface()
|
||||
GAME->interface()->castleInt = nullptr;
|
||||
}
|
||||
|
||||
void CCastleInterface::updateArtifacts()
|
||||
{
|
||||
// handle equipping / unequipping Legion pieces
|
||||
for(auto creatureInfoBox : creainfo)
|
||||
creatureInfoBox->update();
|
||||
}
|
||||
|
||||
void CCastleInterface::updateGarrisons()
|
||||
{
|
||||
garr->setArmy(town->getUpperArmy(), EGarrisonType::UPPER);
|
||||
|
@ -223,7 +223,7 @@ public:
|
||||
};
|
||||
|
||||
/// Class which manages the castle window
|
||||
class CCastleInterface : public CStatusbarWindow, public IGarrisonHolder
|
||||
class CCastleInterface final : public CStatusbarWindow, public IGarrisonHolder, public IArtifactsHolder
|
||||
{
|
||||
std::shared_ptr<CLabel> title;
|
||||
std::shared_ptr<CLabel> income;
|
||||
@ -257,6 +257,7 @@ public:
|
||||
CCastleInterface(const CGTownInstance * Town, const CGTownInstance * from = nullptr);
|
||||
~CCastleInterface();
|
||||
|
||||
void updateArtifacts() override;
|
||||
void updateGarrisons() override;
|
||||
bool holdsGarrison(const CArmedInstance * army) override;
|
||||
|
||||
|
@ -848,7 +848,7 @@ void CStackWindow::init()
|
||||
|
||||
void CStackWindow::initBonusesList()
|
||||
{
|
||||
BonusList receivedBonuses = *info->stackNode->getBonuses(CSelector(Bonus::Permanent), Selector::all);
|
||||
BonusList receivedBonuses = *info->stackNode->getBonuses(CSelector(Bonus::Permanent));
|
||||
BonusList abilities = info->creature->getExportedBonusList();
|
||||
|
||||
// remove all bonuses that are not propagated away
|
||||
@ -900,9 +900,12 @@ void CStackWindow::initBonusesList()
|
||||
BonusList groupIndepMin = group;
|
||||
BonusList groupIndepMax = group;
|
||||
BonusList groupNoMinMax = group;
|
||||
BonusList groupBaseOnly = group;
|
||||
|
||||
groupIndepMin.remove_if([](const Bonus * b) { return b->valType != BonusValueType::INDEPENDENT_MIN; });
|
||||
groupIndepMax.remove_if([](const Bonus * b) { return b->valType != BonusValueType::INDEPENDENT_MAX; });
|
||||
groupNoMinMax.remove_if([](const Bonus * b) { return b->valType == BonusValueType::INDEPENDENT_MAX || b->valType == BonusValueType::INDEPENDENT_MIN; });
|
||||
groupBaseOnly.remove_if([](const Bonus * b) { return b->valType != BonusValueType::ADDITIVE_VALUE || b->valType == BonusValueType::BASE_NUMBER; });
|
||||
|
||||
int valIndepMin = groupIndepMin.totalValue();
|
||||
int valIndepMax = groupIndepMax.totalValue();
|
||||
@ -914,8 +917,8 @@ void CStackWindow::initBonusesList()
|
||||
usedGroup = groupIndepMin; // bonus value was limited due to INDEPENDENT_MIN bonus -> show this bonus
|
||||
else if (!groupIndepMax.empty() && valNoMinMax != valIndepMax)
|
||||
usedGroup = groupIndepMax; // bonus value was limited due to INDEPENDENT_MAX bonus -> show this bonus
|
||||
else
|
||||
usedGroup = groupNoMinMax; // bonus value is not limited - show first non-independent bonus
|
||||
else if (!groupBaseOnly.empty())
|
||||
usedGroup = groupNoMinMax; // bonus value is not limited and has bonuses other than percent to base / percent to all - show first non-independent bonus
|
||||
|
||||
// It is possible that empty group was selected. For example, there is only INDEPENDENT effect with value of 0, which does not actually has any effect on this unit
|
||||
// For example, orb of vulnerability on unit without any resistances
|
||||
|
@ -261,7 +261,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
}
|
||||
}
|
||||
|
||||
CExchangeWindow::update();
|
||||
CExchangeWindow::updateArtifacts();
|
||||
}
|
||||
|
||||
void CExchangeWindow::creatureArrowButtonCallback(bool leftToRight, SlotID slotId)
|
||||
@ -361,7 +361,7 @@ void CExchangeWindow::updateGarrisons()
|
||||
{
|
||||
garr->recreateSlots();
|
||||
|
||||
update();
|
||||
updateArtifacts();
|
||||
}
|
||||
|
||||
bool CExchangeWindow::holdsGarrison(const CArmedInstance * army)
|
||||
@ -375,13 +375,13 @@ void CExchangeWindow::questLogShortcut()
|
||||
GAME->interface()->showQuestLog();
|
||||
}
|
||||
|
||||
void CExchangeWindow::update()
|
||||
void CExchangeWindow::updateArtifacts()
|
||||
{
|
||||
const bool qeLayout = isQuickExchangeLayoutAvailable();
|
||||
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
CWindowWithArtifacts::update();
|
||||
CWindowWithArtifacts::updateArtifacts();
|
||||
|
||||
for(size_t leftRight : {0, 1})
|
||||
{
|
||||
|
@ -75,7 +75,7 @@ public:
|
||||
|
||||
void keyPressed(EShortcut key) override;
|
||||
|
||||
void update() override;
|
||||
void updateArtifacts() override;
|
||||
|
||||
// IGarrisonHolder impl
|
||||
void updateGarrisons() override;
|
||||
|
@ -46,7 +46,7 @@ void CHeroSwitcher::clickPressed(const Point & cursorPosition)
|
||||
//TODO: do not recreate window
|
||||
if (false)
|
||||
{
|
||||
owner->update();
|
||||
owner->updateArtifacts();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -153,7 +153,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
|
||||
{
|
||||
auto divisionRoundUp = [](int x, int y){ return (x + (y - 1)) / y; };
|
||||
int lines = divisionRoundUp(hero->secSkills.size(), 2);
|
||||
secSkillSlider = std::make_shared<CSlider>(Point(284, 276), 189, [this](int val){ CHeroWindow::update(); }, 4, lines, 0, Orientation::VERTICAL, CSlider::BROWN);
|
||||
secSkillSlider = std::make_shared<CSlider>(Point(284, 276), 189, [this](int val){ CHeroWindow::updateArtifacts(); }, 4, lines, 0, Orientation::VERTICAL, CSlider::BROWN);
|
||||
secSkillSlider->setPanningStep(48);
|
||||
secSkillSlider->setScrollBounds(Rect(-266, 0, secSkillSlider->pos.x - pos.x + secSkillSlider->pos.w, secSkillSlider->pos.h));
|
||||
}
|
||||
@ -182,14 +182,14 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
|
||||
labels.push_back(std::make_shared<CLabel>(69, 232, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, LIBRARY->generaltexth->jktexts[6]));
|
||||
labels.push_back(std::make_shared<CLabel>(213, 232, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, LIBRARY->generaltexth->jktexts[7]));
|
||||
|
||||
CHeroWindow::update();
|
||||
CHeroWindow::updateArtifacts();
|
||||
}
|
||||
|
||||
void CHeroWindow::update()
|
||||
void CHeroWindow::updateArtifacts()
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
CWindowWithArtifacts::update();
|
||||
CWindowWithArtifacts::updateArtifacts();
|
||||
auto & heroscrn = LIBRARY->generaltexth->heroscrn;
|
||||
assert(curHero);
|
||||
|
||||
|
@ -101,7 +101,7 @@ public:
|
||||
|
||||
CHeroWindow(const CGHeroInstance * hero);
|
||||
|
||||
void update() override;
|
||||
void updateArtifacts() override;
|
||||
|
||||
void dismissCurrent(); //dismissed currently displayed hero (curHero)
|
||||
void commanderWindow();
|
||||
|
@ -85,7 +85,7 @@ void CMarketWindow::updateExperience()
|
||||
|
||||
void CMarketWindow::update()
|
||||
{
|
||||
CWindowWithArtifacts::update();
|
||||
CWindowWithArtifacts::updateArtifacts();
|
||||
assert(marketWidget);
|
||||
marketWidget->update();
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ public:
|
||||
void updateArtifacts() override;
|
||||
void updateGarrisons() override;
|
||||
void updateExperience() override;
|
||||
void update() override;
|
||||
|
||||
void update();
|
||||
void close() override;
|
||||
bool holdsGarrison(const CArmedInstance * army) override;
|
||||
|
||||
|
@ -164,7 +164,7 @@ void CWindowWithArtifacts::enableKeyboardShortcuts() const
|
||||
artSet->enableKeyboardShortcuts();
|
||||
}
|
||||
|
||||
void CWindowWithArtifacts::update()
|
||||
void CWindowWithArtifacts::updateArtifacts()
|
||||
{
|
||||
for(const auto & artSet : artSets)
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "../widgets/CArtifactsOfHeroBackpack.h"
|
||||
#include "CWindowObject.h"
|
||||
|
||||
class CWindowWithArtifacts : virtual public CWindowObject
|
||||
class CWindowWithArtifacts : virtual public CWindowObject, public IArtifactsHolder
|
||||
{
|
||||
public:
|
||||
using CArtifactsOfHeroPtr = std::shared_ptr<CArtifactsOfHeroBase>;
|
||||
@ -36,7 +36,7 @@ public:
|
||||
void deactivate() override;
|
||||
void enableKeyboardShortcuts() const;
|
||||
|
||||
virtual void update();
|
||||
void updateArtifacts() override;
|
||||
|
||||
protected:
|
||||
void markPossibleSlots() const;
|
||||
|
@ -1733,7 +1733,7 @@
|
||||
"type" : "CREATURE_GROWTH",
|
||||
"subtype" : "creatureLevel2",
|
||||
"val" : 5,
|
||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||
"propagator": "TOWN_AND_VISITOR"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1746,7 +1746,7 @@
|
||||
"type" : "CREATURE_GROWTH",
|
||||
"subtype" : "creatureLevel3",
|
||||
"val" : 4,
|
||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||
"propagator": "TOWN_AND_VISITOR"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1759,7 +1759,7 @@
|
||||
"type" : "CREATURE_GROWTH",
|
||||
"subtype" : "creatureLevel4",
|
||||
"val" : 3,
|
||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||
"propagator": "TOWN_AND_VISITOR"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1772,7 +1772,7 @@
|
||||
"type" : "CREATURE_GROWTH",
|
||||
"subtype" : "creatureLevel5",
|
||||
"val" : 2,
|
||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||
"propagator": "TOWN_AND_VISITOR"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1785,7 +1785,7 @@
|
||||
"type" : "CREATURE_GROWTH",
|
||||
"subtype" : "creatureLevel6",
|
||||
"val" : 1,
|
||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||
"propagator": "TOWN_AND_VISITOR"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2094,7 +2094,7 @@
|
||||
"growth" : {
|
||||
"type" : "CREATURE_GROWTH_PERCENT",
|
||||
"val" : 50,
|
||||
"propagator": "PLAYER_PROPAGATOR"
|
||||
"propagator": "PLAYER"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -12,7 +12,7 @@
|
||||
"val": 1
|
||||
},
|
||||
{
|
||||
"propagator": "PLAYER_PROPAGATOR",
|
||||
"propagator": "PLAYER",
|
||||
"type": "THIEVES_GUILD_ACCESS",
|
||||
"val": 1
|
||||
}
|
||||
@ -130,7 +130,7 @@
|
||||
"lighthouse" : {
|
||||
"bonuses": [
|
||||
{
|
||||
"propagator": "PLAYER_PROPAGATOR",
|
||||
"propagator": "PLAYER",
|
||||
"type": "MOVEMENT",
|
||||
"subtype": "heroMovementSea",
|
||||
"val": 500
|
||||
@ -257,7 +257,7 @@
|
||||
"thievesGuild" : {
|
||||
"bonuses": [
|
||||
{
|
||||
"propagator": "PLAYER_PROPAGATOR",
|
||||
"propagator": "PLAYER",
|
||||
"type": "THIEVES_GUILD_ACCESS",
|
||||
"val": 2
|
||||
}
|
||||
|
@ -383,7 +383,7 @@
|
||||
{
|
||||
"type" : "MORALE",
|
||||
"val" : 1,
|
||||
"propagator" : "HERO",
|
||||
"propagator" : "ARMY",
|
||||
"description" : "PLACEHOLDER",
|
||||
"stacking" : "Angels"
|
||||
},
|
||||
@ -453,7 +453,7 @@
|
||||
"raisesMorale" : {
|
||||
"type" : "MORALE",
|
||||
"val" : 1,
|
||||
"propagator" : "HERO",
|
||||
"propagator" : "ARMY",
|
||||
"description" : "@creatures.core.angel.bonus.raisesMorale",
|
||||
"stacking" : "Angels"
|
||||
},
|
||||
|
@ -179,7 +179,7 @@
|
||||
"special1": {
|
||||
"bonuses": [
|
||||
{
|
||||
"propagator": "PLAYER_PROPAGATOR",
|
||||
"propagator": "PLAYER",
|
||||
"type": "MOVEMENT",
|
||||
"subtype": "heroMovementSea",
|
||||
"val": 500
|
||||
@ -212,14 +212,14 @@
|
||||
"val": 2
|
||||
},
|
||||
{
|
||||
"propagator": "PLAYER_PROPAGATOR",
|
||||
"propagator": "PLAYER",
|
||||
"type": "THIEVES_GUILD_ACCESS",
|
||||
"val": 1
|
||||
}
|
||||
],
|
||||
"upgrades" : "tavern"
|
||||
},
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "MORALE", "val": 2, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "MORALE", "val": 2, "propagator": "PLAYER" } ] },
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },
|
||||
|
@ -188,10 +188,10 @@
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "requires" : [ "mageGuild1" ],
|
||||
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 10, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 10, "propagator": "PLAYER" } ] },
|
||||
"special3": { "requires" : [ "dwellingLvl1" ], "marketModes" : ["creature-undead"] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
|
||||
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 20, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 20, "propagator": "PLAYER" } ] },
|
||||
|
||||
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
|
||||
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
|
||||
|
@ -196,7 +196,7 @@
|
||||
"special3": { "type" : "treasury", "requires" : [ "horde1" ] },
|
||||
"horde2": { "id" : 24, "upgrades" : "dwellingLvl5" },
|
||||
"horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl5", "requires" : [ "horde2" ], "mode" : "auto" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "LUCK", "val": 2, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "LUCK", "val": 2, "propagator": "PLAYER" } ] },
|
||||
|
||||
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
|
||||
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
|
||||
|
@ -21,7 +21,7 @@
|
||||
"type" : "MOVEMENT",
|
||||
"subtype" : "heroMovementSea",
|
||||
"val" : 500,
|
||||
"propagator": "PLAYER_PROPAGATOR"
|
||||
"propagator": "PLAYER"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
||||
"anyOf" : [
|
||||
{
|
||||
"type" : "string",
|
||||
"enum" : [ "TIMES_HERO_LEVEL", "TIMES_STACK_LEVEL", "DIVIDE_STACK_LEVEL", "BONUS_OWNER_UPDATER", "TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL" ]
|
||||
"enum" : [ "TIMES_HERO_LEVEL", "TIMES_STACK_LEVEL", "DIVIDE_STACK_LEVEL", "BONUS_OWNER_UPDATER", "TIMES_STACK_SIZE", "TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL" ]
|
||||
},
|
||||
{
|
||||
"description" : "GROWS_WITH_LEVEL updater",
|
||||
@ -107,6 +107,43 @@
|
||||
"description" : "Maximal bonus value"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description" : "TIMES_ARMY_SIZE updater",
|
||||
"type" : "object",
|
||||
"required" : ["type"],
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"const" : "TIMES_ARMY_SIZE",
|
||||
},
|
||||
"stepSize" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 1,
|
||||
"description" : "Size of each step, in levels"
|
||||
},
|
||||
"minimum" : {
|
||||
"type" : "integer",
|
||||
"description" : "Minimal bonus value"
|
||||
},
|
||||
"maximum" : {
|
||||
"type" : "integer",
|
||||
"description" : "Maximal bonus value"
|
||||
},
|
||||
"filteredLevel" : {
|
||||
"type" : "integer",
|
||||
"description" : "Level of units to count"
|
||||
},
|
||||
"filteredFaction" : {
|
||||
"type" : "string",
|
||||
"description" : "Faction of units to count"
|
||||
},
|
||||
"filteredCreature" : {
|
||||
"type" : "string",
|
||||
"description" : "Specific unit to count"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -138,7 +175,7 @@
|
||||
"propagator" : {
|
||||
"description" : "propagator",
|
||||
"type" : "string",
|
||||
"enum" : [ "BATTLE_WIDE", "VISITED_TOWN_AND_VISITOR", "PLAYER_PROPAGATOR", "HERO", "TEAM_PROPAGATOR", "GLOBAL_EFFECT" ]
|
||||
"enum" : [ "BATTLE_WIDE", "TOWN_AND_VISITOR", "PLAYER", "HERO", "TOWN", "ARMY", "TEAM", "GLOBAL_EFFECT" ]
|
||||
},
|
||||
"updater" : {
|
||||
"$ref" : "#/definitions/updater"
|
||||
|
@ -1,10 +1,22 @@
|
||||
# Bonus Propagators
|
||||
|
||||
Propagators allow to propagate bonus effect "upwards". For example, they can be used to make unit ability battle-wide, or to provide some bonuses to hero. See [Bonus System Guide](../Guides/Bonus_System.md) for more information.
|
||||
|
||||
## Available propagators
|
||||
|
||||
- BATTLE_WIDE: Affects both sides during battle
|
||||
- VISITED_TOWN_AND_VISITOR: Used with Legion artifacts (town visited by hero)
|
||||
- PLAYER_PROPAGATOR: Bonus will affect all objects owned by player. Used by Statue of Legion.
|
||||
- HERO: Bonus will be transferred to hero (for example from stacks in his army).
|
||||
- TEAM_PROPAGATOR: Bonus will affect all objects owned by player and his allies.
|
||||
- GLOBAL_EFFECT: This effect will influence all creatures, heroes and towns on the map.
|
||||
- `ARMY`: Propagators that allow bonuses to be transferred to an army. It is typically used by creature abilities to affect the army that the creature is part of.
|
||||
- `HERO`: Similar to `ARMY`, but works only with armies led by heroes.
|
||||
- `TOWN`: Similar to `ARMY`, but only affects units that are part of the town garrison.
|
||||
- `TOWN_AND_VISITOR`: Propagator that allows the town and the visiting hero to interact. It can be used to propagate the effects of town buildings to the visiting hero outside of combat or the effects of the hero to the town (e.g. Legion artifacts)
|
||||
- `BATTLE_WIDE` - Propagator that allows bonuses to affect all entities in battles. It is typically used for creature abilities or artifacts that need to affect either both sides or only the enemy side in combat.
|
||||
- `PLAYER`: The bonus affects all objects owned by the player. Used by the Statue of Legion.
|
||||
- `TEAM`: The bonus affects all objects owned by the player and their allies.
|
||||
- `GLOBAL_EFFECT`: This effect influences all creatures, towns and recruited heroes on the map.
|
||||
|
||||
## Deprecated propagators
|
||||
|
||||
These propagators are still supported, but in future they may be removed.
|
||||
|
||||
- `VISITED_TOWN_AND_VISITOR`: Replaced by `TOWN_AND_VISITOR`
|
||||
- `PLAYER_PROPAGATOR`: Replaced by `PLAYER`
|
||||
- `TEAM_PROPAGATOR`: Replaced by `TEAM`
|
||||
|
@ -84,19 +84,78 @@ Usage:
|
||||
|
||||
Effect: Updates val to `val = clamp(val * floor(stackSize / stepSize), minimum, maximum)`, where stackSize is total number of creatures in current unit stack
|
||||
|
||||
Parameters `minimum` and `maximum` are optional and can be dropped if not needed
|
||||
Example of short form with default parameters:
|
||||
|
||||
Example:
|
||||
```json
|
||||
"updater" : "TIMES_STACK_SIZE"
|
||||
```
|
||||
|
||||
Example of long form with custom parameters:
|
||||
|
||||
```json
|
||||
"updater" : {
|
||||
"type" : "TIMES_STACK_SIZE",
|
||||
|
||||
// Optional, by default - unlimited
|
||||
"minimum" : 0,
|
||||
|
||||
// Optional, by default - unlimited
|
||||
"maximum" : 100,
|
||||
|
||||
// Optional, by default - 1
|
||||
"stepSize" : 2
|
||||
}
|
||||
```
|
||||
|
||||
## TIMES_ARMY_SIZE
|
||||
|
||||
Effect: Updates val to `val = clamp(val * floor(stackSize / stepSize), minimum, maximum)`, where stackSize is total number of creatures in hero army that fulful filter
|
||||
|
||||
Parameters:
|
||||
|
||||
- `minimum`: minimum possible value of the bonus value. Unlimited by default
|
||||
- `maximum`: maximum possible value of the bonus value. Unlimited by default
|
||||
- `stepSize`: number of units needed to increase updater multiplier by 1
|
||||
- `filteredCreature`: identifier of specific unit to filter
|
||||
- `filteredLevel`: level of units that need to be counted. Redundant if `filteredCreature` is used
|
||||
- `filteredFaction`: faction of units that need to be counted. Redundant if `filteredCreature` is used
|
||||
|
||||
Filtering for specific unit:
|
||||
|
||||
```json
|
||||
"updater" : {
|
||||
"type" : "TIMES_ARMY_SIZE",
|
||||
"filteredCreature" : "pikeman",
|
||||
|
||||
// Optional, by default - unlimited
|
||||
"minimum" : 0,
|
||||
|
||||
// Optional, by default - unlimited
|
||||
"maximum" : 100,
|
||||
|
||||
// Optional, by default - 1
|
||||
"stepSize" : 2
|
||||
}
|
||||
```
|
||||
|
||||
Filtering for specific faction:
|
||||
|
||||
```json
|
||||
"updater" : {
|
||||
"type" : "TIMES_STACK_SIZE",
|
||||
"filteredFaction" : "castle"
|
||||
}
|
||||
```
|
||||
|
||||
Filtering for specific unit level:
|
||||
|
||||
```json
|
||||
"updater" : {
|
||||
"type" : "TIMES_STACK_SIZE",
|
||||
"filteredLevel" : 2
|
||||
}
|
||||
```
|
||||
|
||||
## BONUS_OWNER_UPDATER
|
||||
|
||||
Helper updater for proper functionality of `OPPOSITE_SIDE` limiter
|
||||
|
@ -313,8 +313,8 @@ si32 CCreature::maxAmount(const TResources &res) const //how many creatures can
|
||||
}
|
||||
|
||||
CCreature::CCreature()
|
||||
:CBonusSystemNode(BonusNodeType::CREATURE)
|
||||
{
|
||||
setNodeType(CBonusSystemNode::CREATURE);
|
||||
fightValue = AIValue = growth = hordeGrowth = ammMin = ammMax = 0;
|
||||
}
|
||||
|
||||
@ -329,7 +329,7 @@ void CCreature::addBonus(int val, BonusType type, BonusSubtypeID subtype)
|
||||
BonusList & exported = getExportedBonusList();
|
||||
|
||||
BonusList existing;
|
||||
exported.getBonuses(existing, selector, Selector::all);
|
||||
exported.getBonuses(existing, selector);
|
||||
|
||||
if(existing.empty())
|
||||
{
|
||||
|
@ -707,20 +707,22 @@ void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::strin
|
||||
}
|
||||
}
|
||||
|
||||
CStackInstance::CStackInstance(IGameInfoCallback *cb, bool isHypothetic)
|
||||
: CBonusSystemNode(isHypothetic)
|
||||
CStackInstance::CStackInstance(IGameInfoCallback *cb)
|
||||
: CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false)
|
||||
{}
|
||||
|
||||
CStackInstance::CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic)
|
||||
: CBonusSystemNode(nodeType, isHypothetic)
|
||||
, CStackBasicDescriptor(nullptr, 0)
|
||||
, CArtifactSet(cb)
|
||||
, GameCallbackHolder(cb)
|
||||
, nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE))
|
||||
, initiative(this, Selector::type()(BonusType::STACKS_SPEED))
|
||||
, totalExperience(0)
|
||||
{
|
||||
setNodeType(STACK_INSTANCE);
|
||||
}
|
||||
{}
|
||||
|
||||
CStackInstance::CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity Count, bool isHypothetic)
|
||||
: CStackInstance(cb, false)
|
||||
: CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false)
|
||||
{
|
||||
setType(id);
|
||||
setCount(Count);
|
||||
@ -833,6 +835,7 @@ void CStackInstance::setCount(TQuantity newCount)
|
||||
}
|
||||
|
||||
CStackBasicDescriptor::setCount(newCount);
|
||||
nodeHasChanged();
|
||||
}
|
||||
|
||||
std::string CStackInstance::bonusToString(const std::shared_ptr<Bonus>& bonus) const
|
||||
@ -1039,14 +1042,13 @@ CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb)
|
||||
{}
|
||||
|
||||
CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id)
|
||||
: CStackInstance(cb)
|
||||
: CStackInstance(cb, BonusNodeType::COMMANDER, false)
|
||||
, name("Commando")
|
||||
{
|
||||
alive = true;
|
||||
level = 1;
|
||||
setCount(1);
|
||||
setType(nullptr);
|
||||
setNodeType (CBonusSystemNode::COMMANDER);
|
||||
secondarySkills.resize (ECommander::SPELL_POWER + 1);
|
||||
setType(id);
|
||||
//TODO - parse them
|
||||
|
@ -146,7 +146,8 @@ public:
|
||||
virtual int getLevel() const; //different for regular stack and commander
|
||||
CreatureID getCreatureID() const; //-1 if not available
|
||||
std::string getName() const; //plural or singular
|
||||
CStackInstance(IGameInfoCallback *cb, bool isHypothetic = false);
|
||||
CStackInstance(IGameInfoCallback *cb);
|
||||
CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic = false);
|
||||
CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity count, bool isHypothetic = false);
|
||||
virtual ~CStackInstance() = default;
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
PlayerState::PlayerState(IGameInfoCallback *cb)
|
||||
: CBonusSystemNode(PLAYER)
|
||||
: CBonusSystemNode(BonusNodeType::PLAYER)
|
||||
, GameCallbackHolder(cb)
|
||||
, color(-1)
|
||||
, human(false)
|
||||
|
@ -26,7 +26,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
///CStack
|
||||
CStack::CStack(const CStackInstance * Base, const PlayerColor & O, int I, BattleSide Side, const SlotID & S):
|
||||
CBonusSystemNode(STACK_BATTLE),
|
||||
CBonusSystemNode(BonusNodeType::STACK_BATTLE),
|
||||
base(Base),
|
||||
ID(I),
|
||||
typeID(Base->getId()),
|
||||
@ -40,7 +40,7 @@ CStack::CStack(const CStackInstance * Base, const PlayerColor & O, int I, Battle
|
||||
}
|
||||
|
||||
CStack::CStack():
|
||||
CBonusSystemNode(STACK_BATTLE),
|
||||
CBonusSystemNode(BonusNodeType::STACK_BATTLE),
|
||||
owner(PlayerColor::NEUTRAL),
|
||||
slot(SlotID(255)),
|
||||
initialPosition(BattleHex())
|
||||
@ -48,7 +48,7 @@ CStack::CStack():
|
||||
}
|
||||
|
||||
CStack::CStack(const CStackBasicDescriptor * stack, const PlayerColor & O, int I, BattleSide Side, const SlotID & S):
|
||||
CBonusSystemNode(STACK_BATTLE),
|
||||
CBonusSystemNode(BonusNodeType::STACK_BATTLE),
|
||||
ID(I),
|
||||
typeID(stack->getId()),
|
||||
baseAmount(stack->getCount()),
|
||||
@ -134,7 +134,7 @@ std::vector<SpellID> CStack::activeSpells() const
|
||||
return b->type != BonusType::NONE && b->sid.as<SpellID>().toSpell() && !b->sid.as<SpellID>().toSpell()->isAdventure();
|
||||
}));
|
||||
|
||||
TConstBonusListPtr spellEffects = getBonuses(selector, Selector::all, cachingStr.str());
|
||||
TConstBonusListPtr spellEffects = getBonuses(selector, cachingStr.str());
|
||||
for(const auto & it : *spellEffects)
|
||||
{
|
||||
if(!vstd::contains(ret, it->sid.as<SpellID>())) //do not duplicate spells with multiple effects
|
||||
@ -155,7 +155,7 @@ const CGHeroInstance * CStack::getMyHero() const
|
||||
return dynamic_cast<const CGHeroInstance *>(base->getArmy());
|
||||
else //we are attached directly?
|
||||
for(const CBonusSystemNode * n : getParentNodes())
|
||||
if(n->getNodeType() == HERO)
|
||||
if(n->getNodeType() == BonusNodeType::HERO)
|
||||
return dynamic_cast<const CGHeroInstance *>(n);
|
||||
|
||||
return nullptr;
|
||||
|
@ -467,7 +467,8 @@ BattleInfo::BattleInfo(IGameInfoCallback *cb, const BattleLayout & layout):
|
||||
}
|
||||
|
||||
BattleInfo::BattleInfo(IGameInfoCallback *cb)
|
||||
:GameCallbackHolder(cb),
|
||||
:CBonusSystemNode(BonusNodeType::BATTLE_WIDE),
|
||||
GameCallbackHolder(cb),
|
||||
sides({SideInBattle(cb), SideInBattle(cb)}),
|
||||
layout(std::make_unique<BattleLayout>()),
|
||||
round(-1),
|
||||
@ -477,7 +478,6 @@ BattleInfo::BattleInfo(IGameInfoCallback *cb)
|
||||
tacticsSide(BattleSide::NONE),
|
||||
tacticDistance(0)
|
||||
{
|
||||
setNodeType(BATTLE);
|
||||
}
|
||||
|
||||
BattleLayout BattleInfo::getLayout() const
|
||||
|
@ -1797,7 +1797,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(vstd::RNG & rand, const ba
|
||||
std::stringstream cachingStr;
|
||||
cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << spellID.num;
|
||||
|
||||
if(subject->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(spellID)), Selector::all, cachingStr.str()))
|
||||
if(subject->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(spellID)), cachingStr.str()))
|
||||
continue;
|
||||
|
||||
auto spellPtr = spellID.toSpell();
|
||||
|
@ -495,7 +495,7 @@ bool CUnitState::isGhost() const
|
||||
|
||||
bool CUnitState::isFrozen() const
|
||||
{
|
||||
return hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(SpellID(SpellID::STONE_GAZE))), Selector::all);
|
||||
return hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(SpellID(SpellID::STONE_GAZE))));
|
||||
}
|
||||
|
||||
bool CUnitState::isValidTarget(bool allowDead) const
|
||||
@ -948,9 +948,9 @@ CUnitStateDetached::CUnitStateDetached(const IUnitInfo * unit_, const IBonusBear
|
||||
{
|
||||
}
|
||||
|
||||
TConstBonusListPtr CUnitStateDetached::getAllBonuses(const CSelector & selector, const CSelector & limit, const std::string & cachingStr) const
|
||||
TConstBonusListPtr CUnitStateDetached::getAllBonuses(const CSelector & selector, const std::string & cachingStr) const
|
||||
{
|
||||
return bonus->getAllBonuses(selector, limit, cachingStr);
|
||||
return bonus->getAllBonuses(selector, cachingStr);
|
||||
}
|
||||
|
||||
int32_t CUnitStateDetached::getTreeVersion() const
|
||||
|
@ -278,7 +278,7 @@ public:
|
||||
|
||||
CUnitStateDetached & operator= (const CUnitState & other);
|
||||
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit, const std::string & cachingStr = "") const override;
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const std::string & cachingStr = "") const override;
|
||||
|
||||
int32_t getTreeVersion() const override;
|
||||
|
||||
|
@ -523,7 +523,7 @@ int DamageCalculator::battleBonusValue(const IBonusBearer * bearer, const CSelec
|
||||
: Selector::effectRange()(BonusLimitEffect::ONLY_MELEE_FIGHT);
|
||||
|
||||
//any regular bonuses or just ones for melee/ranged
|
||||
return bearer->getBonuses(selector, noLimit.Or(limitMatches))->totalValue();
|
||||
return bearer->getBonuses(selector)->valOfBonuses(noLimit.Or(limitMatches));
|
||||
};
|
||||
|
||||
DamageEstimation DamageCalculator::calculateDmgRange() const
|
||||
|
@ -276,6 +276,29 @@ enum class BonusValueType : uint8_t
|
||||
#undef BONUS_VALUE
|
||||
};
|
||||
|
||||
enum class BonusNodeType
|
||||
{
|
||||
NONE = -1,
|
||||
UNKNOWN,
|
||||
STACK_INSTANCE,
|
||||
STACK_BATTLE,
|
||||
ARMY,
|
||||
ARTIFACT,
|
||||
CREATURE,
|
||||
ARTIFACT_INSTANCE,
|
||||
HERO,
|
||||
PLAYER,
|
||||
TEAM,
|
||||
TOWN_AND_VISITOR,
|
||||
BATTLE_WIDE,
|
||||
COMMANDER,
|
||||
GLOBAL_EFFECTS,
|
||||
BOAT,
|
||||
TOWN
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusValueType> bonusValueMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusSource> bonusSourceMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusDuration::Type> bonusDurationMap;
|
||||
|
@ -70,7 +70,10 @@ int BonusList::totalValue(int baseValue) const
|
||||
};
|
||||
|
||||
auto applyPercentageRoundUp = [](int base, int percent) -> int {
|
||||
return (static_cast<int64_t>(base) * (100 + percent) + 99) / 100;
|
||||
if (base >= 0)
|
||||
return (static_cast<int64_t>(base) * (100 + percent) + 99) / 100;
|
||||
else
|
||||
return (static_cast<int64_t>(base) * (100 + percent) - 99) / 100;
|
||||
};
|
||||
|
||||
auto applyPercentageRoundDown = [](int base, int percent) -> int {
|
||||
@ -172,11 +175,11 @@ std::shared_ptr<const Bonus> BonusList::getFirst(const CSelector &selector) cons
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BonusList::getBonuses(BonusList & out, const CSelector &selector, const CSelector &limit) const
|
||||
void BonusList::getBonuses(BonusList & out, const CSelector &selector) const
|
||||
{
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
if(selector(b.get()) && (!limit || ((bool)limit && limit(b.get()))))
|
||||
if(selector(b.get()))
|
||||
out.push_back(b);
|
||||
}
|
||||
}
|
||||
@ -190,8 +193,7 @@ void BonusList::getAllBonuses(BonusList &out) const
|
||||
int BonusList::valOfBonuses(const CSelector &select, int baseValue) const
|
||||
{
|
||||
BonusList ret;
|
||||
CSelector limit = nullptr;
|
||||
getBonuses(ret, select, limit);
|
||||
getBonuses(ret, select);
|
||||
return ret.totalValue(baseValue);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ public:
|
||||
// BonusList functions
|
||||
void stackBonuses();
|
||||
int totalValue(int baseValue = 0) const;
|
||||
void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit = nullptr) const;
|
||||
void getBonuses(BonusList &out, const CSelector &selector) const;
|
||||
void getAllBonuses(BonusList &out) const;
|
||||
|
||||
//special find functions
|
||||
|
@ -18,6 +18,7 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
constexpr bool cachingEnabled = true;
|
||||
static std::atomic<int32_t> globalCounter = 1;
|
||||
|
||||
std::shared_ptr<Bonus> CBonusSystemNode::getLocalBonus(const CSelector & selector)
|
||||
{
|
||||
@ -64,27 +65,23 @@ void CBonusSystemNode::getAllParents(TCNodes & out) const //retrieves list of pa
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getAllBonusesRec(BonusList &out, const CSelector & selector) const
|
||||
void CBonusSystemNode::getAllBonusesRec(BonusList &out) const
|
||||
{
|
||||
BonusList beforeUpdate;
|
||||
|
||||
for(const auto * parent : parentsToInherit)
|
||||
parent->getAllBonusesRec(beforeUpdate, selector);
|
||||
parent->getAllBonusesRec(beforeUpdate);
|
||||
|
||||
bonuses.getAllBonuses(beforeUpdate);
|
||||
|
||||
for(const auto & b : beforeUpdate)
|
||||
{
|
||||
//We should not run updaters on non-selected bonuses
|
||||
auto updated = selector(b.get()) && b->updater
|
||||
? getUpdatedBonus(b, b->updater)
|
||||
: b;
|
||||
|
||||
auto updated = b->updater ? getUpdatedBonus(b, b->updater) : b;
|
||||
out.push_back(updated);
|
||||
}
|
||||
}
|
||||
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr) const
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const std::string &cachingStr) const
|
||||
{
|
||||
if (cachingEnabled)
|
||||
{
|
||||
@ -107,7 +104,7 @@ TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, co
|
||||
{
|
||||
// Cached bonuses are up-to-date - use shared/read access and compute results
|
||||
std::shared_lock lock(sync);
|
||||
cachedBonuses.getBonuses(*ret, selector, limit);
|
||||
cachedBonuses.getBonuses(*ret, selector);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -117,7 +114,7 @@ TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, co
|
||||
if (cachedLast == nodeChanged)
|
||||
{
|
||||
// While our thread was waiting, another one have updated bonus tree. Use cached bonuses.
|
||||
cachedBonuses.getBonuses(*ret, selector, limit);
|
||||
cachedBonuses.getBonuses(*ret, selector);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -126,11 +123,11 @@ TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, co
|
||||
|
||||
cachedBonuses.clear();
|
||||
|
||||
getAllBonusesRec(allBonuses, Selector::all);
|
||||
getAllBonusesRec(allBonuses);
|
||||
limitBonuses(allBonuses, cachedBonuses);
|
||||
cachedBonuses.stackBonuses();
|
||||
cachedLast = nodeChanged;
|
||||
cachedBonuses.getBonuses(*ret, selector, limit);
|
||||
cachedBonuses.getBonuses(*ret, selector);
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,20 +148,20 @@ TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, co
|
||||
}
|
||||
else
|
||||
{
|
||||
return getAllBonusesWithoutCaching(selector, limit);
|
||||
return getAllBonusesWithoutCaching(selector);
|
||||
}
|
||||
}
|
||||
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit) const
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector) const
|
||||
{
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
|
||||
// Get bonus results without caching enabled.
|
||||
BonusList beforeLimiting;
|
||||
BonusList afterLimiting;
|
||||
getAllBonusesRec(beforeLimiting, selector);
|
||||
getAllBonusesRec(beforeLimiting);
|
||||
limitBonuses(beforeLimiting, afterLimiting);
|
||||
afterLimiting.getBonuses(*ret, selector, limit);
|
||||
afterLimiting.getBonuses(*ret, selector);
|
||||
ret->stackBonuses();
|
||||
return ret;
|
||||
}
|
||||
@ -175,21 +172,17 @@ std::shared_ptr<Bonus> CBonusSystemNode::getUpdatedBonus(const std::shared_ptr<B
|
||||
return updater->createUpdatedBonus(b, * this);
|
||||
}
|
||||
|
||||
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
|
||||
nodeType(UNKNOWN),
|
||||
CBonusSystemNode::CBonusSystemNode(BonusNodeType NodeType, bool isHypotetic):
|
||||
nodeType(NodeType),
|
||||
cachedLast(0),
|
||||
nodeChanged(0),
|
||||
isHypotheticNode(isHypotetic)
|
||||
{
|
||||
}
|
||||
|
||||
CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType):
|
||||
nodeType(NodeType),
|
||||
cachedLast(0),
|
||||
nodeChanged(0),
|
||||
isHypotheticNode(false)
|
||||
{
|
||||
}
|
||||
CBonusSystemNode::CBonusSystemNode(BonusNodeType NodeType):
|
||||
CBonusSystemNode(NodeType, false)
|
||||
{}
|
||||
|
||||
CBonusSystemNode::~CBonusSystemNode()
|
||||
{
|
||||
@ -218,7 +211,7 @@ void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
|
||||
parent.children.push_back(this);
|
||||
}
|
||||
|
||||
nodeHasChanged();
|
||||
parent.nodeHasChanged();
|
||||
}
|
||||
|
||||
void CBonusSystemNode::attachToSource(const CBonusSystemNode & parent)
|
||||
@ -253,8 +246,8 @@ void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
|
||||
}
|
||||
else
|
||||
{
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
|
||||
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)",
|
||||
nodeShortInfo(), static_cast<int>(nodeType), parent.nodeShortInfo(), static_cast<int>(parent.nodeType));
|
||||
}
|
||||
|
||||
if(!isHypothetic())
|
||||
@ -263,11 +256,11 @@ void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
|
||||
parent.children -= this;
|
||||
else
|
||||
{
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) is not a child of %s (nodeType=%d)"
|
||||
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) is not a child of %s (nodeType=%d)",
|
||||
nodeShortInfo(), static_cast<int>(nodeType), parent.nodeShortInfo(), static_cast<int>(parent.nodeType));
|
||||
}
|
||||
}
|
||||
nodeHasChanged();
|
||||
parent.nodeHasChanged();
|
||||
}
|
||||
|
||||
|
||||
@ -287,8 +280,8 @@ void CBonusSystemNode::detachFromSource(const CBonusSystemNode & parent)
|
||||
}
|
||||
else
|
||||
{
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
|
||||
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)",
|
||||
nodeShortInfo(), static_cast<int>(nodeType), parent.nodeShortInfo(), static_cast<int>(parent.nodeType));
|
||||
}
|
||||
|
||||
nodeHasChanged();
|
||||
@ -304,7 +297,7 @@ void CBonusSystemNode::removeBonusesRecursive(const CSelector & s)
|
||||
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
|
||||
{
|
||||
BonusList bl;
|
||||
exportedBonuses.getBonuses(bl, s, Selector::all);
|
||||
exportedBonuses.getBonuses(bl, s);
|
||||
for(const auto & b : bl)
|
||||
{
|
||||
b->turnsRemain--;
|
||||
@ -355,7 +348,7 @@ void CBonusSystemNode::removeBonus(const std::shared_ptr<Bonus>& b)
|
||||
void CBonusSystemNode::removeBonuses(const CSelector & selector)
|
||||
{
|
||||
BonusList toRemove;
|
||||
exportedBonuses.getBonuses(toRemove, selector, Selector::all);
|
||||
exportedBonuses.getBonuses(toRemove, selector);
|
||||
for(const auto & bonus : toRemove)
|
||||
removeBonus(bonus);
|
||||
}
|
||||
@ -364,9 +357,10 @@ bool CBonusSystemNode::actsAsBonusSourceOnly() const
|
||||
{
|
||||
switch(nodeType)
|
||||
{
|
||||
case CREATURE:
|
||||
case ARTIFACT:
|
||||
case ARTIFACT_INSTANCE:
|
||||
case BonusNodeType::CREATURE:
|
||||
case BonusNodeType::ARTIFACT:
|
||||
case BonusNodeType::ARTIFACT_INSTANCE:
|
||||
case BonusNodeType::BOAT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -382,7 +376,7 @@ void CBonusSystemNode::propagateBonus(const std::shared_ptr<Bonus> & b, const CB
|
||||
: b;
|
||||
bonuses.push_back(propagated);
|
||||
logBonus->trace("#$# %s #propagated to# %s", propagated->Description(nullptr), nodeName());
|
||||
nodeHasChanged();
|
||||
invalidateChildrenNodes(globalCounter);
|
||||
}
|
||||
|
||||
TNodes lchildren;
|
||||
@ -400,15 +394,17 @@ void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
|
||||
else
|
||||
logBonus->warn("Attempt to remove #$# %s, which is not propagated to %s", b->Description(nullptr), nodeName());
|
||||
|
||||
bonuses.remove_if([this, b](const auto & bonus)
|
||||
bonuses.remove_if([b](const auto & bonus)
|
||||
{
|
||||
if (bonus->propagationUpdater && bonus->propagationUpdater == b->propagationUpdater)
|
||||
{
|
||||
nodeHasChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
invalidateChildrenNodes(globalCounter);
|
||||
}
|
||||
|
||||
TNodes lchildren;
|
||||
@ -550,7 +546,7 @@ void CBonusSystemNode::exportBonuses()
|
||||
exportBonus(b);
|
||||
}
|
||||
|
||||
CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
|
||||
BonusNodeType CBonusSystemNode::getNodeType() const
|
||||
{
|
||||
return nodeType;
|
||||
}
|
||||
@ -560,11 +556,6 @@ const TCNodesVector& CBonusSystemNode::getParentNodes() const
|
||||
return parentsToInherit;
|
||||
}
|
||||
|
||||
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
|
||||
{
|
||||
nodeType = type;
|
||||
}
|
||||
|
||||
BonusList & CBonusSystemNode::getExportedBonusList()
|
||||
{
|
||||
return exportedBonuses;
|
||||
@ -612,11 +603,26 @@ void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out)
|
||||
|
||||
void CBonusSystemNode::nodeHasChanged()
|
||||
{
|
||||
static std::atomic<int32_t> globalCounter = 1;
|
||||
|
||||
invalidateChildrenNodes(++globalCounter);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::recomputePropagationUpdaters(const CBonusSystemNode & source)
|
||||
{
|
||||
for(const auto & b : exportedBonuses)
|
||||
{
|
||||
if (b->propagator && b->propagationUpdater)
|
||||
{
|
||||
unpropagateBonus(b);
|
||||
propagateBonus(b, source);
|
||||
}
|
||||
}
|
||||
|
||||
for(const CBonusSystemNode * parent : source.parentsToInherit)
|
||||
if (parent->actsAsBonusSourceOnly())
|
||||
recomputePropagationUpdaters(*parent);
|
||||
|
||||
}
|
||||
|
||||
void CBonusSystemNode::invalidateChildrenNodes(int32_t changeCounter)
|
||||
{
|
||||
if (nodeChanged == changeCounter)
|
||||
@ -624,6 +630,8 @@ void CBonusSystemNode::invalidateChildrenNodes(int32_t changeCounter)
|
||||
|
||||
nodeChanged = changeCounter;
|
||||
|
||||
recomputePropagationUpdaters(*this);
|
||||
|
||||
for(CBonusSystemNode * child : children)
|
||||
child->invalidateChildrenNodes(changeCounter);
|
||||
}
|
||||
|
@ -26,13 +26,6 @@ using TCNodesVector = std::vector<const CBonusSystemNode *>;
|
||||
class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public virtual Serializeable, public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
enum ENodeTypes
|
||||
{
|
||||
NONE = -1,
|
||||
UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALTY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM,
|
||||
TOWN_AND_VISITOR, BATTLE, COMMANDER, GLOBAL_EFFECTS, ALL_CREATURES, TOWN
|
||||
};
|
||||
|
||||
struct HashStringCompare {
|
||||
static size_t hash(const std::string& data)
|
||||
{
|
||||
@ -53,7 +46,7 @@ private:
|
||||
TNodesVector parentsToPropagate; // we may attach our bonuses to them
|
||||
TNodesVector children;
|
||||
|
||||
ENodeTypes nodeType;
|
||||
BonusNodeType nodeType;
|
||||
bool isHypotheticNode;
|
||||
|
||||
mutable BonusList cachedBonuses;
|
||||
@ -69,8 +62,8 @@ private:
|
||||
mutable RequestsMap cachedRequests;
|
||||
mutable std::shared_mutex sync;
|
||||
|
||||
void getAllBonusesRec(BonusList &out, const CSelector & selector) const;
|
||||
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit) const;
|
||||
void getAllBonusesRec(BonusList &out) const;
|
||||
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector) const;
|
||||
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
|
||||
void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
|
||||
|
||||
@ -82,6 +75,7 @@ private:
|
||||
|
||||
void propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source);
|
||||
void unpropagateBonus(const std::shared_ptr<Bonus> & b);
|
||||
void recomputePropagationUpdaters(const CBonusSystemNode & source);
|
||||
bool actsAsBonusSourceOnly() const;
|
||||
|
||||
void newRedDescendant(CBonusSystemNode & descendant) const; //propagation needed
|
||||
@ -96,11 +90,11 @@ protected:
|
||||
void exportBonuses();
|
||||
|
||||
public:
|
||||
explicit CBonusSystemNode(bool isHypotetic = false);
|
||||
explicit CBonusSystemNode(ENodeTypes NodeType);
|
||||
explicit CBonusSystemNode(BonusNodeType nodeType, bool isHypotetic);
|
||||
explicit CBonusSystemNode(BonusNodeType nodeType);
|
||||
virtual ~CBonusSystemNode();
|
||||
|
||||
TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = "") const override;
|
||||
TConstBonusListPtr getAllBonuses(const CSelector &selector, const std::string &cachingStr = "") const override;
|
||||
void getParents(TCNodes &out) const; //retrieves list of parent nodes (nodes to inherit bonuses from),
|
||||
|
||||
/// Returns first bonus matching selector
|
||||
@ -129,8 +123,7 @@ public:
|
||||
|
||||
BonusList & getExportedBonusList();
|
||||
const BonusList & getExportedBonusList() const;
|
||||
CBonusSystemNode::ENodeTypes getNodeType() const;
|
||||
void setNodeType(CBonusSystemNode::ENodeTypes type);
|
||||
BonusNodeType getNodeType() const;
|
||||
const TCNodesVector & getParentNodes() const;
|
||||
|
||||
void nodeHasChanged();
|
||||
|
@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
int IBonusBearer::valOfBonuses(const CSelector &selector, const std::string &cachingStr, int baseValue) const
|
||||
{
|
||||
TConstBonusListPtr hlp = getAllBonuses(selector, nullptr, cachingStr);
|
||||
TConstBonusListPtr hlp = getAllBonuses(selector, cachingStr);
|
||||
return hlp->totalValue(baseValue);
|
||||
}
|
||||
|
||||
@ -27,19 +27,9 @@ bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachin
|
||||
return !getBonuses(selector, cachingStr)->empty();
|
||||
}
|
||||
|
||||
bool IBonusBearer::hasBonus(const CSelector &selector, const CSelector &limit, const std::string &cachingStr) const
|
||||
{
|
||||
return !getBonuses(selector, limit, cachingStr)->empty();
|
||||
}
|
||||
|
||||
TConstBonusListPtr IBonusBearer::getBonuses(const CSelector &selector, const std::string &cachingStr) const
|
||||
{
|
||||
return getAllBonuses(selector, nullptr, cachingStr);
|
||||
}
|
||||
|
||||
TConstBonusListPtr IBonusBearer::getBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr) const
|
||||
{
|
||||
return getAllBonuses(selector, limit, cachingStr);
|
||||
return getAllBonuses(selector, cachingStr);
|
||||
}
|
||||
|
||||
TConstBonusListPtr IBonusBearer::getBonusesFrom(BonusSource source) const
|
||||
@ -120,7 +110,7 @@ bool IBonusBearer::hasBonusFrom(BonusSource source) const
|
||||
|
||||
std::shared_ptr<const Bonus> IBonusBearer::getBonus(const CSelector &selector) const
|
||||
{
|
||||
auto bonuses = getAllBonuses(selector, Selector::all);
|
||||
auto bonuses = getAllBonuses(selector);
|
||||
return bonuses->getFirst(Selector::all);
|
||||
}
|
||||
|
||||
|
@ -20,11 +20,9 @@ public:
|
||||
// * selector is predicate that tests if Bonus matches our criteria
|
||||
IBonusBearer() = default;
|
||||
virtual ~IBonusBearer() = default;
|
||||
virtual TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const = 0;
|
||||
virtual TConstBonusListPtr getAllBonuses(const CSelector &selector, const std::string &cachingStr = {}) const = 0;
|
||||
int valOfBonuses(const CSelector &selector, const std::string &cachingStr = {}, int baseValue = 0) const;
|
||||
bool hasBonus(const CSelector &selector, const std::string &cachingStr = {}) const;
|
||||
bool hasBonus(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const;
|
||||
TConstBonusListPtr getBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const;
|
||||
TConstBonusListPtr getBonuses(const CSelector &selector, const std::string &cachingStr = {}) const;
|
||||
|
||||
std::shared_ptr<const Bonus> getBonus(const CSelector &selector) const; //returns any bonus visible on node that matches (or nullptr if none matches)
|
||||
|
@ -45,7 +45,7 @@ static const CStack * retrieveStackBattle(const CBonusSystemNode * node)
|
||||
{
|
||||
switch(node->getNodeType())
|
||||
{
|
||||
case CBonusSystemNode::STACK_BATTLE:
|
||||
case BonusNodeType::STACK_BATTLE:
|
||||
return dynamic_cast<const CStack *>(node);
|
||||
default:
|
||||
return nullptr;
|
||||
@ -56,9 +56,9 @@ static const CStackInstance * retrieveStackInstance(const CBonusSystemNode * nod
|
||||
{
|
||||
switch(node->getNodeType())
|
||||
{
|
||||
case CBonusSystemNode::STACK_INSTANCE:
|
||||
case BonusNodeType::STACK_INSTANCE:
|
||||
return (dynamic_cast<const CStackInstance *>(node));
|
||||
case CBonusSystemNode::STACK_BATTLE:
|
||||
case BonusNodeType::STACK_BATTLE:
|
||||
return (dynamic_cast<const CStack *>(node))->base;
|
||||
default:
|
||||
return nullptr;
|
||||
@ -69,9 +69,9 @@ static const CCreature * retrieveCreature(const CBonusSystemNode *node)
|
||||
{
|
||||
switch(node->getNodeType())
|
||||
{
|
||||
case CBonusSystemNode::CREATURE:
|
||||
case BonusNodeType::CREATURE:
|
||||
return (dynamic_cast<const CCreature *>(node));
|
||||
case CBonusSystemNode::STACK_BATTLE:
|
||||
case BonusNodeType::STACK_BATTLE:
|
||||
return (dynamic_cast<const CStack *>(node))->unitType();
|
||||
default:
|
||||
const CStackInstance * csi = retrieveStackInstance(node);
|
||||
@ -262,7 +262,7 @@ CreatureTerrainLimiter::CreatureTerrainLimiter(TerrainId terrain):
|
||||
|
||||
ILimiter::EDecision CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const
|
||||
{
|
||||
if (context.node.getNodeType() != CBonusSystemNode::STACK_BATTLE && context.node.getNodeType() != CBonusSystemNode::STACK_INSTANCE)
|
||||
if (context.node.getNodeType() != BonusNodeType::STACK_BATTLE && context.node.getNodeType() != BonusNodeType::STACK_INSTANCE)
|
||||
return ILimiter::EDecision::NOT_APPLICABLE;
|
||||
|
||||
if (terrainType == ETerrainId::NATIVE_TERRAIN)
|
||||
@ -277,7 +277,7 @@ ILimiter::EDecision CreatureTerrainLimiter::limit(const BonusLimitationContext &
|
||||
|
||||
// TODO: CStack and CStackInstance need some common base type that represents any stack
|
||||
// Closest existing class is ACreature, however it is also used as base for CCreature, which is not a stack
|
||||
if (context.node.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
if (context.node.getNodeType() == BonusNodeType::STACK_BATTLE)
|
||||
{
|
||||
const auto * unit = dynamic_cast<const CStack *>(&context.node);
|
||||
auto unitNativeTerrain = unit->getFactionID().toEntity(LIBRARY)->getNativeTerrain();
|
||||
@ -294,7 +294,7 @@ ILimiter::EDecision CreatureTerrainLimiter::limit(const BonusLimitationContext &
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.node.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
if (context.node.getNodeType() == BonusNodeType::STACK_BATTLE)
|
||||
{
|
||||
const auto * unit = dynamic_cast<const CStack *>(&context.node);
|
||||
if (unit->getCurrentTerrain() == terrainType)
|
||||
@ -462,7 +462,7 @@ ILimiter::EDecision RankRangeLimiter::limit(const BonusLimitationContext &contex
|
||||
const CStackInstance * csi = retrieveStackInstance(&context.node);
|
||||
if(csi)
|
||||
{
|
||||
if (csi->getNodeType() == CBonusSystemNode::COMMANDER) //no stack exp bonuses for commander creatures
|
||||
if (csi->getNodeType() == BonusNodeType::COMMANDER) //no stack exp bonuses for commander creatures
|
||||
return ILimiter::EDecision::DISCARD;
|
||||
if (csi->getExpRank() > minRank && csi->getExpRank() < maxRank)
|
||||
return ILimiter::EDecision::ACCEPT;
|
||||
|
@ -16,37 +16,51 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
|
||||
{
|
||||
{"BATTLE_WIDE", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE)},
|
||||
{"VISITED_TOWN_AND_VISITOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::TOWN_AND_VISITOR)},
|
||||
{"PLAYER_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER)},
|
||||
{"HERO", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO)},
|
||||
{"TEAM_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::TEAM)}, //untested
|
||||
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)}
|
||||
}; //untested
|
||||
{"BATTLE_WIDE", std::make_shared<CPropagatorNodeType>(BonusNodeType::BATTLE_WIDE)},
|
||||
{"TOWN_AND_VISITOR", std::make_shared<CPropagatorNodeType>(BonusNodeType::TOWN_AND_VISITOR)},
|
||||
{"PLAYER", std::make_shared<CPropagatorNodeType>(BonusNodeType::PLAYER)},
|
||||
{"HERO", std::make_shared<CPropagatorNodeType>(BonusNodeType::HERO)},
|
||||
{"TOWN", std::make_shared<CPropagatorNodeType>(BonusNodeType::TOWN)},
|
||||
{"ARMY", std::make_shared<CPropagatorNodeType>(BonusNodeType::ARMY)},
|
||||
{"TEAM", std::make_shared<CPropagatorNodeType>(BonusNodeType::TEAM)},
|
||||
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(BonusNodeType::GLOBAL_EFFECTS)},
|
||||
|
||||
// deprecated, for compatibility
|
||||
{"VISITED_TOWN_AND_VISITOR", std::make_shared<CPropagatorNodeType>(BonusNodeType::TOWN_AND_VISITOR)},
|
||||
{"PLAYER_PROPAGATOR", std::make_shared<CPropagatorNodeType>(BonusNodeType::PLAYER)},
|
||||
{"TEAM_PROPAGATOR", std::make_shared<CPropagatorNodeType>(BonusNodeType::TEAM)},
|
||||
|
||||
};
|
||||
|
||||
bool IPropagator::shouldBeAttached(CBonusSystemNode *dest) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CBonusSystemNode::ENodeTypes IPropagator::getPropagatorType() const
|
||||
BonusNodeType IPropagator::getPropagatorType() const
|
||||
{
|
||||
return CBonusSystemNode::ENodeTypes::NONE;
|
||||
return BonusNodeType::NONE;
|
||||
}
|
||||
|
||||
CPropagatorNodeType::CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType)
|
||||
CPropagatorNodeType::CPropagatorNodeType(BonusNodeType NodeType)
|
||||
: nodeType(NodeType)
|
||||
{
|
||||
}
|
||||
|
||||
CBonusSystemNode::ENodeTypes CPropagatorNodeType::getPropagatorType() const
|
||||
BonusNodeType CPropagatorNodeType::getPropagatorType() const
|
||||
{
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest) const
|
||||
{
|
||||
return nodeType == dest->getNodeType();
|
||||
if (nodeType == dest->getNodeType())
|
||||
return true;
|
||||
|
||||
if (nodeType == BonusNodeType::ARMY)
|
||||
return dest->getNodeType() == BonusNodeType::HERO || dest->getNodeType() == BonusNodeType::TOWN;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -23,7 +23,7 @@ class DLL_LINKAGE IPropagator : public Serializeable
|
||||
public:
|
||||
virtual ~IPropagator() = default;
|
||||
virtual bool shouldBeAttached(CBonusSystemNode *dest) const;
|
||||
virtual CBonusSystemNode::ENodeTypes getPropagatorType() const;
|
||||
virtual BonusNodeType getPropagatorType() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h)
|
||||
{}
|
||||
@ -31,12 +31,12 @@ public:
|
||||
|
||||
class DLL_LINKAGE CPropagatorNodeType : public IPropagator
|
||||
{
|
||||
CBonusSystemNode::ENodeTypes nodeType;
|
||||
BonusNodeType nodeType;
|
||||
|
||||
public:
|
||||
CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType = CBonusSystemNode::ENodeTypes::UNKNOWN);
|
||||
CPropagatorNodeType(BonusNodeType NodeType = BonusNodeType::UNKNOWN);
|
||||
bool shouldBeAttached(CBonusSystemNode *dest) const override;
|
||||
CBonusSystemNode::ENodeTypes getPropagatorType() const override;
|
||||
BonusNodeType getPropagatorType() const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h)
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ GrowsWithLevelUpdater::GrowsWithLevelUpdater(int valPer20, int stepSize)
|
||||
|
||||
std::shared_ptr<Bonus> GrowsWithLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||
if(context.getNodeType() == BonusNodeType::HERO)
|
||||
{
|
||||
int level = dynamic_cast<const CGHeroInstance &>(context).level;
|
||||
int steps = stepSize ? level / stepSize : level;
|
||||
@ -74,7 +74,7 @@ JsonNode GrowsWithLevelUpdater::toJsonNode() const
|
||||
|
||||
std::shared_ptr<Bonus> TimesHeroLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||
if(context.getNodeType() == BonusNodeType::HERO)
|
||||
{
|
||||
int level = dynamic_cast<const CGHeroInstance &>(context).level;
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
@ -96,7 +96,7 @@ JsonNode TimesHeroLevelUpdater::toJsonNode() const
|
||||
|
||||
std::shared_ptr<Bonus> TimesHeroLevelDivideStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||
if(context.getNodeType() == BonusNodeType::HERO)
|
||||
{
|
||||
auto newBonus = TimesHeroLevelUpdater::createUpdatedBonus(b, context);
|
||||
newBonus->updater = divideStackLevel;
|
||||
@ -119,19 +119,18 @@ std::shared_ptr<Bonus> TimesStackSizeUpdater::apply(const std::shared_ptr<Bonus>
|
||||
{
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= std::clamp(count / stepSize, minimum, maximum);
|
||||
newBonus->updater = nullptr; // prevent double-apply
|
||||
return newBonus;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> TimesStackSizeUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE || context.getNodeType() == CBonusSystemNode::COMMANDER)
|
||||
if(context.getNodeType() == BonusNodeType::STACK_INSTANCE || context.getNodeType() == BonusNodeType::COMMANDER)
|
||||
{
|
||||
int count = dynamic_cast<const CStackInstance &>(context).getCount();
|
||||
return apply(b, count);
|
||||
}
|
||||
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
if(context.getNodeType() == BonusNodeType::STACK_BATTLE)
|
||||
{
|
||||
const auto & stack = dynamic_cast<const CStack &>(context);
|
||||
return apply(b, stack.getCount());
|
||||
@ -149,6 +148,44 @@ JsonNode TimesStackSizeUpdater::toJsonNode() const
|
||||
return JsonNode("TIMES_STACK_SIZE");
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> TimesArmySizeUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == BonusNodeType::ARMY || context.getNodeType() == BonusNodeType::HERO || context.getNodeType() == BonusNodeType::TOWN)
|
||||
{
|
||||
const auto & army = dynamic_cast<const CArmedInstance &>(context);
|
||||
int totalSize = 0;
|
||||
for (const auto & unit : army.Slots())
|
||||
{
|
||||
if (filteredCreature.hasValue() && filteredCreature != unit.second->getCreatureID())
|
||||
continue;
|
||||
|
||||
if (filteredFaction.hasValue() && filteredFaction != unit.second->getFactionID())
|
||||
continue;
|
||||
|
||||
if (filteredLevel != -1 && filteredLevel != unit.second->getLevel())
|
||||
continue;
|
||||
|
||||
totalSize += unit.second->getCount();
|
||||
}
|
||||
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= std::clamp(totalSize / stepSize, minimum, maximum);
|
||||
return newBonus;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string TimesArmySizeUpdater::toString() const
|
||||
{
|
||||
return "TimesArmySizeUpdater";
|
||||
}
|
||||
|
||||
JsonNode TimesArmySizeUpdater::toJsonNode() const
|
||||
{
|
||||
return JsonNode("TIMES_ARMY_SIZE");
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Bonus> TimesStackLevelUpdater::apply(const std::shared_ptr<Bonus> & b, int level) const
|
||||
{
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
@ -159,13 +196,13 @@ std::shared_ptr<Bonus> TimesStackLevelUpdater::apply(const std::shared_ptr<Bonus
|
||||
|
||||
std::shared_ptr<Bonus> TimesStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE || context.getNodeType() == CBonusSystemNode::COMMANDER)
|
||||
if(context.getNodeType() == BonusNodeType::STACK_INSTANCE || context.getNodeType() == BonusNodeType::COMMANDER)
|
||||
{
|
||||
int level = dynamic_cast<const CStackInstance &>(context).getLevel();
|
||||
return apply(b, level);
|
||||
}
|
||||
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
if(context.getNodeType() == BonusNodeType::STACK_BATTLE)
|
||||
{
|
||||
const auto & stack = dynamic_cast<const CStack &>(context);
|
||||
//update if stack doesn't have an instance (summons, war machines)
|
||||
@ -203,13 +240,13 @@ std::shared_ptr<Bonus> DivideStackLevelUpdater::apply(const std::shared_ptr<Bonu
|
||||
|
||||
std::shared_ptr<Bonus> DivideStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE || context.getNodeType() == CBonusSystemNode::COMMANDER)
|
||||
if(context.getNodeType() == BonusNodeType::STACK_INSTANCE || context.getNodeType() == BonusNodeType::COMMANDER)
|
||||
{
|
||||
int level = dynamic_cast<const CStackInstance &>(context).getLevel();
|
||||
return apply(b, level);
|
||||
}
|
||||
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
if(context.getNodeType() == BonusNodeType::STACK_BATTLE)
|
||||
{
|
||||
const auto & stack = dynamic_cast<const CStack &>(context);
|
||||
//update if stack doesn't have an instance (summons, war machines)
|
||||
|
@ -89,9 +89,9 @@ class DLL_LINKAGE TimesStackSizeUpdater : public IUpdater
|
||||
{
|
||||
std::shared_ptr<Bonus> apply(const std::shared_ptr<Bonus> & b, int count) const;
|
||||
|
||||
int minimum;
|
||||
int maximum;
|
||||
int stepSize;
|
||||
int minimum = std::numeric_limits<int>::min();
|
||||
int maximum = std::numeric_limits<int>::max();
|
||||
int stepSize = 1;
|
||||
public:
|
||||
TimesStackSizeUpdater() = default;
|
||||
TimesStackSizeUpdater(int minimum, int maximum, int stepSize)
|
||||
@ -113,6 +113,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE TimesArmySizeUpdater : public IUpdater
|
||||
{
|
||||
public:
|
||||
int minimum = std::numeric_limits<int>::min();
|
||||
int maximum = std::numeric_limits<int>::max();
|
||||
int stepSize = 1;
|
||||
int filteredLevel = -1;
|
||||
CreatureID filteredCreature;
|
||||
FactionID filteredFaction;
|
||||
TimesArmySizeUpdater() = default;
|
||||
|
||||
std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;
|
||||
std::string toString() const override;
|
||||
JsonNode toJsonNode() const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h)
|
||||
{
|
||||
h & static_cast<IUpdater &>(*this);
|
||||
h & minimum;
|
||||
h & maximum;
|
||||
h & stepSize;
|
||||
h & filteredLevel;
|
||||
h & filteredCreature;
|
||||
h & filteredFaction;
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE TimesStackLevelUpdater : public IUpdater
|
||||
{
|
||||
std::shared_ptr<Bonus> apply(const std::shared_ptr<Bonus> & b, int level) const;
|
||||
|
@ -293,10 +293,10 @@ bool CChargedArtifact::getRemoveOnDepletion() const
|
||||
}
|
||||
|
||||
CArtifact::CArtifact()
|
||||
: iconIndex(ArtifactID::NONE),
|
||||
: CBonusSystemNode(BonusNodeType::ARTIFACT),
|
||||
iconIndex(ArtifactID::NONE),
|
||||
price(0)
|
||||
{
|
||||
setNodeType(ARTIFACT);
|
||||
possibleSlots[ArtBearer::HERO]; //we want to generate map entry even if it will be empty
|
||||
possibleSlots[ArtBearer::CREATURE]; //we want to generate map entry even if it will be empty
|
||||
possibleSlots[ArtBearer::COMMANDER];
|
||||
|
@ -224,7 +224,7 @@ CArtifactInstance::CArtifactInstance(IGameInfoCallback *cb, const CArtifact * ar
|
||||
}
|
||||
|
||||
CArtifactInstance::CArtifactInstance(IGameInfoCallback *cb)
|
||||
: CBonusSystemNode(ARTIFACT_INSTANCE)
|
||||
: CBonusSystemNode(BonusNodeType::ARTIFACT_INSTANCE)
|
||||
, CCombinedArtifactInstance(cb)
|
||||
{
|
||||
}
|
||||
|
@ -251,10 +251,10 @@ void CTownHandler::loadBuildingBonuses(const JsonNode & source, BonusList & bonu
|
||||
bonus->description.appendTextID(building->getNameTextID());
|
||||
|
||||
//JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty.
|
||||
assert(bonus->propagator == nullptr || bonus->propagator->getPropagatorType() != CBonusSystemNode::ENodeTypes::UNKNOWN);
|
||||
assert(bonus->propagator == nullptr || bonus->propagator->getPropagatorType() != BonusNodeType::UNKNOWN);
|
||||
|
||||
if(bonus->propagator != nullptr
|
||||
&& bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN)
|
||||
&& bonus->propagator->getPropagatorType() == BonusNodeType::UNKNOWN)
|
||||
bonus->addPropagator(emptyPropagator());
|
||||
building->addNewBonus(bonus, bonusList);
|
||||
}
|
||||
|
@ -152,9 +152,9 @@ int CGameState::getDate(Date mode) const
|
||||
}
|
||||
|
||||
CGameState::CGameState()
|
||||
:globalEffects(BonusNodeType::GLOBAL_EFFECTS)
|
||||
{
|
||||
heroesPool = std::make_unique<TavernHeroesPool>(this);
|
||||
globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
|
||||
}
|
||||
|
||||
CGameState::~CGameState()
|
||||
@ -1596,9 +1596,8 @@ CGHeroInstance * CGameState::getUsedHero(const HeroTypeID & hid) const
|
||||
}
|
||||
|
||||
TeamState::TeamState()
|
||||
{
|
||||
setNodeType(TEAM);
|
||||
}
|
||||
:CBonusSystemNode(BonusNodeType::TEAM)
|
||||
{}
|
||||
|
||||
CArtifactInstance * CGameState::createScroll(const SpellID & spellId)
|
||||
{
|
||||
|
@ -386,6 +386,7 @@ static TUpdaterPtr parseUpdater(const JsonNode & updaterJson)
|
||||
{"TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL", std::make_shared<TimesHeroLevelDivideStackLevelUpdater>()},
|
||||
{"DIVIDE_STACK_LEVEL", std::make_shared<DivideStackLevelUpdater>()},
|
||||
{"TIMES_STACK_LEVEL", std::make_shared<TimesStackLevelUpdater>()},
|
||||
{"TIMES_STACK_SIZE", std::make_shared<TimesStackSizeUpdater>()},
|
||||
{"BONUS_OWNER_UPDATER", std::make_shared<OwnerUpdater>()}
|
||||
};
|
||||
|
||||
@ -427,6 +428,35 @@ static TUpdaterPtr parseUpdater(const JsonNode & updaterJson)
|
||||
}
|
||||
return std::make_shared<TimesStackSizeUpdater>(minimum, maximum, std::max(1, stepSize));
|
||||
}
|
||||
if(updaterJson["type"].String() == "TIMES_ARMY_SIZE")
|
||||
{
|
||||
auto result = std::make_shared<TimesArmySizeUpdater>();
|
||||
|
||||
result->minimum = updaterJson["minimum"].isNull() ? std::numeric_limits<int>::min() : updaterJson["minimum"].Integer();
|
||||
result->maximum = updaterJson["maximum"].isNull() ? std::numeric_limits<int>::max() : updaterJson["maximum"].Integer();
|
||||
result->stepSize = updaterJson["stepSize"].isNull() ? 1 : updaterJson["stepSize"].Integer();
|
||||
result->filteredLevel = updaterJson["filteredLevel"].isNull() ? -1 : updaterJson["filteredLevel"].Integer();
|
||||
if (result->minimum > result->maximum)
|
||||
{
|
||||
logMod->warn("TIMES_ARMY_SIZE updater: minimum value (%d) is above maximum value(%d)!", result->minimum, result->maximum);
|
||||
std::swap(result->minimum, result->maximum);
|
||||
}
|
||||
if (!updaterJson["filteredFaction"].isNull())
|
||||
{
|
||||
LIBRARY->identifiers()->requestIdentifier( "faction", updaterJson["filteredFaction"], [result](int32_t identifier)
|
||||
{
|
||||
result->filteredFaction = FactionID(identifier);
|
||||
});
|
||||
}
|
||||
if (!updaterJson["filteredCreature"].isNull())
|
||||
{
|
||||
LIBRARY->identifiers()->requestIdentifier( "creature", updaterJson["filteredCreature"], [result](int32_t identifier)
|
||||
{
|
||||
result->filteredCreature = CreatureID(identifier);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
logMod->warn("Unknown updater type \"%s\"", updaterJson["type"].String());
|
||||
break;
|
||||
|
@ -42,13 +42,13 @@ void CArmedInstance::randomizeArmy(FactionID type)
|
||||
}
|
||||
|
||||
CArmedInstance::CArmedInstance(IGameInfoCallback *cb)
|
||||
:CArmedInstance(cb, false)
|
||||
:CArmedInstance(cb, BonusNodeType::ARMY, false)
|
||||
{
|
||||
}
|
||||
|
||||
CArmedInstance::CArmedInstance(IGameInfoCallback *cb, bool isHypothetic):
|
||||
CArmedInstance::CArmedInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic):
|
||||
CGObjectInstance(cb),
|
||||
CBonusSystemNode(isHypothetic),
|
||||
CBonusSystemNode(nodeType, isHypothetic),
|
||||
nonEvilAlignmentMix(this, Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX)), // Take Angelic Alliance troop-mixing freedom of non-evil units into account.
|
||||
battle(nullptr)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CArmedInstance(IGameInfoCallback *cb);
|
||||
CArmedInstance(IGameInfoCallback *cb, bool isHypothetic);
|
||||
CArmedInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic);
|
||||
|
||||
PlayerColor getOwner() const override
|
||||
{
|
||||
|
@ -50,7 +50,11 @@ void CGDwellingRandomizationInfo::serializeJson(JsonSerializeFormat & handler)
|
||||
}
|
||||
|
||||
CGDwelling::CGDwelling(IGameInfoCallback *cb):
|
||||
CArmedInstance(cb)
|
||||
CGDwelling(cb, BonusNodeType::ARMY)
|
||||
{}
|
||||
|
||||
CGDwelling::CGDwelling(IGameInfoCallback *cb, BonusNodeType nodeType):
|
||||
CArmedInstance(cb, nodeType, false)
|
||||
{}
|
||||
|
||||
CGDwelling::~CGDwelling() = default;
|
||||
|
@ -39,6 +39,7 @@ public:
|
||||
std::optional<CGDwellingRandomizationInfo> randomizationInfo; //random dwelling options; not serialized
|
||||
TCreaturesSet creatures; //creatures[level] -> <vector of alternative ids (base creature and upgrades, creatures amount>
|
||||
|
||||
CGDwelling(IGameInfoCallback *cb, BonusNodeType nodeType);
|
||||
CGDwelling(IGameInfoCallback *cb);
|
||||
~CGDwelling() override;
|
||||
|
||||
|
@ -244,7 +244,7 @@ int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti)
|
||||
}
|
||||
|
||||
CGHeroInstance::CGHeroInstance(IGameInfoCallback * cb)
|
||||
: CArmedInstance(cb),
|
||||
: CArmedInstance(cb, BonusNodeType::HERO, false),
|
||||
CArtifactSet(cb),
|
||||
tacticFormationEnabled(false),
|
||||
inTownGarrison(false),
|
||||
@ -259,7 +259,6 @@ CGHeroInstance::CGHeroInstance(IGameInfoCallback * cb)
|
||||
turnInfoCache(std::make_unique<TurnInfoCache>(this)),
|
||||
manaPerKnowledgeCached(this, Selector::type()(BonusType::MANA_PER_KNOWLEDGE_PERCENTAGE))
|
||||
{
|
||||
setNodeType(HERO);
|
||||
ID = Obj::HERO;
|
||||
secSkills.emplace_back(SecondarySkill::NONE, -1);
|
||||
}
|
||||
|
@ -206,7 +206,11 @@ int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds,
|
||||
|
||||
TResources CGTownInstance::dailyIncome() const
|
||||
{
|
||||
TResources ret;
|
||||
ResourceSet ret;
|
||||
|
||||
for (GameResID k : GameResID::ALL_RESOURCES())
|
||||
ret[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
|
||||
|
||||
for(const auto & p : getTown()->buildings)
|
||||
{
|
||||
BuildingID buildingUpgrade;
|
||||
@ -261,8 +265,9 @@ TownFortifications CGTownInstance::fortificationsLevel() const
|
||||
}
|
||||
|
||||
CGTownInstance::CGTownInstance(IGameInfoCallback *cb):
|
||||
CGDwelling(cb),
|
||||
CGDwelling(cb, BonusNodeType::TOWN),
|
||||
IMarket(cb),
|
||||
townAndVis(BonusNodeType::TOWN_AND_VISITOR),
|
||||
built(0),
|
||||
destroyed(0),
|
||||
identifier(0),
|
||||
@ -271,7 +276,6 @@ CGTownInstance::CGTownInstance(IGameInfoCallback *cb):
|
||||
spellResearchAcceptedCounter(0),
|
||||
spellResearchAllowed(true)
|
||||
{
|
||||
setNodeType(CBonusSystemNode::TOWN);
|
||||
attachTo(townAndVis);
|
||||
}
|
||||
|
||||
@ -1209,11 +1213,6 @@ GrowthInfo::Entry::Entry(int _count, std::string fullDescription):
|
||||
{
|
||||
}
|
||||
|
||||
CTownAndVisitingHero::CTownAndVisitingHero()
|
||||
{
|
||||
setNodeType(TOWN_AND_VISITOR);
|
||||
}
|
||||
|
||||
int GrowthInfo::totalGrowth() const
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -26,12 +26,6 @@ struct DamageRange;
|
||||
template<typename ContainedClass>
|
||||
class LogicalExpression;
|
||||
|
||||
class DLL_LINKAGE CTownAndVisitingHero : public CBonusSystemNode
|
||||
{
|
||||
public:
|
||||
CTownAndVisitingHero();
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE GrowthInfo
|
||||
{
|
||||
struct Entry
|
||||
@ -61,7 +55,7 @@ class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public I
|
||||
public:
|
||||
enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
|
||||
|
||||
CTownAndVisitingHero townAndVis;
|
||||
CBonusSystemNode townAndVis;
|
||||
si32 built; //how many buildings has been built this turn
|
||||
si32 destroyed; //how many buildings has been destroyed this turn
|
||||
ui32 identifier; //special identifier from h3m (only > RoE maps)
|
||||
|
@ -22,6 +22,11 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
FlaggableMapObject::FlaggableMapObject(IGameInfoCallback *cb)
|
||||
:CGObjectInstance(cb)
|
||||
,CBonusSystemNode(BonusNodeType::UNKNOWN)
|
||||
{}
|
||||
|
||||
const IOwnableObject * FlaggableMapObject::asOwnable() const
|
||||
{
|
||||
return this;
|
||||
|
@ -25,7 +25,7 @@ class DLL_LINKAGE FlaggableMapObject final : public CGObjectInstance, public IOw
|
||||
void initBonuses();
|
||||
|
||||
public:
|
||||
using CGObjectInstance::CGObjectInstance;
|
||||
FlaggableMapObject(IGameInfoCallback *cb);
|
||||
|
||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||
|
@ -142,6 +142,10 @@ std::vector<CreatureID> CGMine::providedCreatures() const
|
||||
ResourceSet CGMine::dailyIncome() const
|
||||
{
|
||||
ResourceSet result;
|
||||
|
||||
for (GameResID k : GameResID::ALL_RESOURCES())
|
||||
result[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
|
||||
|
||||
result[producedResource] += defaultResProduction();
|
||||
|
||||
const auto & playerSettings = cb->getPlayerSettings(getOwner());
|
||||
@ -867,7 +871,11 @@ const IOwnableObject * CGGarrison::asOwnable() const
|
||||
|
||||
ResourceSet CGGarrison::dailyIncome() const
|
||||
{
|
||||
return {};
|
||||
ResourceSet result;
|
||||
for (GameResID k : GameResID::ALL_RESOURCES())
|
||||
result[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<CreatureID> CGGarrison::providedCreatures() const
|
||||
@ -930,7 +938,7 @@ void CGGarrison::addAntimagicGarrisonBonus()
|
||||
bonus->type = BonusType::BLOCK_ALL_MAGIC;
|
||||
bonus->source = BonusSource::OBJECT_TYPE;
|
||||
bonus->sid = BonusSourceID(this->ID);
|
||||
bonus->propagator = std::make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE);
|
||||
bonus->propagator = std::make_shared<CPropagatorNodeType>(BonusNodeType::BATTLE_WIDE);
|
||||
bonus->duration = BonusDuration::PERMANENT;
|
||||
this->addNewBonus(bonus);
|
||||
}
|
||||
@ -987,6 +995,7 @@ void CGMagi::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance *
|
||||
|
||||
CGBoat::CGBoat(IGameInfoCallback * cb)
|
||||
: CGObjectInstance(cb)
|
||||
, CBonusSystemNode(BonusNodeType::BOAT)
|
||||
{
|
||||
direction = 4;
|
||||
layer = EPathfindingLayer::SAIL;
|
||||
|
@ -86,7 +86,7 @@ void registerTypes(Serializer &s)
|
||||
s.template registerType<CGUniversity>(23);
|
||||
s.template registerType<CGHeroPlaceholder>(24);
|
||||
s.template registerType<CArmedInstance>(25);
|
||||
s.template registerType<CBonusSystemNode>(26);
|
||||
// s.template registerType<CBonusSystemNode>(26);
|
||||
s.template registerType<CCreatureSet>(27);
|
||||
s.template registerType<CGHeroInstance>(28);
|
||||
s.template registerType<CGDwelling>(30);
|
||||
@ -298,6 +298,8 @@ void registerTypes(Serializer &s)
|
||||
s.template registerType<DivideStackLevelUpdater>(246);
|
||||
s.template registerType<SetHeroExperience>(247);
|
||||
s.template registerType<GiveStackExperience>(248);
|
||||
s.template registerType<TimesStackSizeUpdater>(249);
|
||||
s.template registerType<TimesArmySizeUpdater>(250);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -317,7 +317,7 @@ bool DimensionDoorMechanics::canBeCastImpl(spells::Problem & problem, const IGam
|
||||
std::stringstream cachingStr;
|
||||
cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << owner->id.num;
|
||||
|
||||
int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), Selector::all, cachingStr.str())->size();
|
||||
int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), cachingStr.str())->size();
|
||||
int castsLimit = owner->getLevelPower(schoolLevel);
|
||||
|
||||
bool isTournamentRulesLimitEnabled = cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT);
|
||||
|
@ -246,21 +246,22 @@ int BonusBearerProxy::getBonuses(lua_State * L)
|
||||
|
||||
if(hasRangeSelector)
|
||||
{
|
||||
auto rangeSelector = [](const Bonus * b)
|
||||
{
|
||||
return false;//TODO: BonusBearerProxy::getBonuses rangeSelector
|
||||
};
|
||||
//TODO: BonusBearerProxy::getBonuses rangeSelector
|
||||
//auto rangeSelector = [](const Bonus * b)
|
||||
//{
|
||||
// return false;
|
||||
//};
|
||||
|
||||
ret = object->getBonuses(selector, rangeSelector);
|
||||
ret = object->getBonuses(selector);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = object->getBonuses(selector, Selector::all);
|
||||
ret = object->getBonuses(selector);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = object->getBonuses(Selector::all, Selector::all);
|
||||
ret = object->getBonuses(Selector::all);
|
||||
}
|
||||
|
||||
S.clear();
|
||||
|
@ -83,13 +83,13 @@ public:
|
||||
|
||||
void redirectBonusesToFake()
|
||||
{
|
||||
ON_CALL(*this, getAllBonuses(_, _, _)).WillByDefault(Invoke(&bonusFake, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(*this, getAllBonuses(_, _)).WillByDefault(Invoke(&bonusFake, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(*this, getTreeVersion()).WillByDefault(Invoke(&bonusFake, &BonusBearerMock::getTreeVersion));
|
||||
}
|
||||
|
||||
void expectAnyBonusSystemCall()
|
||||
{
|
||||
EXPECT_CALL(*this, getAllBonuses(_, _, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(*this, getAllBonuses(_, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(*this, getTreeVersion()).Times(AtLeast(0));
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(mock, getAllBonuses(_, _, _)).WillRepeatedly(Invoke(&bonusMock, &BonusBearerMock::getAllBonuses));
|
||||
EXPECT_CALL(mock, getAllBonuses(_, _)).WillRepeatedly(Invoke(&bonusMock, &BonusBearerMock::getAllBonuses));
|
||||
EXPECT_CALL(mock, getTreeVersion()).WillRepeatedly(Return(1));
|
||||
|
||||
bonusMock.addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::STACK_HEALTH, BonusSource::CREATURE_ABILITY, UNIT_HEALTH, BonusSourceID()));
|
||||
@ -235,7 +235,7 @@ TEST_F(HealthTest, singleUnitStack)
|
||||
|
||||
//one Titan
|
||||
|
||||
EXPECT_CALL(mock, getAllBonuses(_, _, _)).WillRepeatedly(Invoke(&bonusMock, &BonusBearerMock::getAllBonuses));
|
||||
EXPECT_CALL(mock, getAllBonuses(_, _)).WillRepeatedly(Invoke(&bonusMock, &BonusBearerMock::getAllBonuses));
|
||||
EXPECT_CALL(mock, getTreeVersion()).WillRepeatedly(Return(1));
|
||||
|
||||
bonusMock.addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::STACK_HEALTH, BonusSource::CREATURE_ABILITY, 300, BonusSourceID()));
|
||||
|
@ -34,13 +34,13 @@ void UnitFake::makeDead()
|
||||
|
||||
void UnitFake::redirectBonusesToFake()
|
||||
{
|
||||
ON_CALL(*this, getAllBonuses(_, _, _)).WillByDefault(Invoke(&bonusFake, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(*this, getAllBonuses(_, _)).WillByDefault(Invoke(&bonusFake, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(*this, getTreeVersion()).WillByDefault(Invoke(&bonusFake, &BonusBearerMock::getTreeVersion));
|
||||
}
|
||||
|
||||
void UnitFake::expectAnyBonusSystemCall()
|
||||
{
|
||||
EXPECT_CALL(*this, getAllBonuses(_, _, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(*this, getAllBonuses(_, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(*this, getTreeVersion()).Times(AtLeast(0));
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ void BonusBearerMock::addNewBonus(const std::shared_ptr<Bonus> & b)
|
||||
treeVersion++;
|
||||
}
|
||||
|
||||
TConstBonusListPtr BonusBearerMock::getAllBonuses(const CSelector & selector, const CSelector & limit, const std::string & cachingStr) const
|
||||
TConstBonusListPtr BonusBearerMock::getAllBonuses(const CSelector & selector, const std::string & cachingStr) const
|
||||
{
|
||||
if(cachedLast != treeVersion)
|
||||
{
|
||||
@ -34,7 +34,7 @@ TConstBonusListPtr BonusBearerMock::getAllBonuses(const CSelector & selector, co
|
||||
}
|
||||
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
bonuses.getBonuses(*ret, selector, limit);
|
||||
bonuses.getBonuses(*ret, selector);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
|
||||
void addNewBonus(const std::shared_ptr<Bonus> & b);
|
||||
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit, const std::string & cachingStr = "") const override;
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const std::string & cachingStr = "") const override;
|
||||
|
||||
int32_t getTreeVersion() const override;
|
||||
private:
|
||||
|
@ -15,7 +15,7 @@
|
||||
class UnitMock : public battle::Unit
|
||||
{
|
||||
public:
|
||||
MOCK_CONST_METHOD3(getAllBonuses, TConstBonusListPtr(const CSelector &, const CSelector &, const std::string &));
|
||||
MOCK_CONST_METHOD2(getAllBonuses, TConstBonusListPtr(const CSelector &, const std::string &));
|
||||
MOCK_CONST_METHOD0(getTreeVersion, int32_t());
|
||||
|
||||
MOCK_CONST_METHOD0(getCasterUnitId, int32_t());
|
||||
|
@ -33,7 +33,7 @@ public:
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
ON_CALL(actualCaster, getAllBonuses(_, _, _)).WillByDefault(Invoke(&casterBonuses, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(actualCaster, getAllBonuses(_, _)).WillByDefault(Invoke(&casterBonuses, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(actualCaster, getTreeVersion()).WillByDefault(Invoke(&casterBonuses, &BonusBearerMock::getTreeVersion));
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ TEST_F(AbilityCasterTest, MagicAbilityAffectedByGenericBonus)
|
||||
|
||||
casterBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::MAGIC_SCHOOL_SKILL, BonusSource::OTHER, 2, BonusSourceID(), BonusSubtypeID(SpellSchool::ANY)));
|
||||
|
||||
EXPECT_CALL(actualCaster, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(actualCaster, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
|
||||
|
||||
setupSubject(1);
|
||||
@ -71,7 +71,7 @@ TEST_F(AbilityCasterTest, MagicAbilityIgnoresSchoolBonus)
|
||||
|
||||
casterBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::MAGIC_SCHOOL_SKILL, BonusSource::OTHER, 2, BonusSourceID(), BonusSubtypeID(SpellSchool::AIR)));
|
||||
|
||||
EXPECT_CALL(actualCaster, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(actualCaster, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
|
||||
|
||||
setupSubject(1);
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(mechanicsMock, isMagicalEffect()).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
EXPECT_CALL(mechanicsMock, getSpellIndex()).WillRepeatedly(Return(castSpell));
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class BonusConditionTest : public TargetConditionItemTest
|
||||
public:
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class CreatureConditionTest : public TargetConditionItemTest
|
||||
public:
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(0);
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(0);
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(0);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
|
||||
EXPECT_CALL(mechanicsMock, getSpell()).Times(AtLeast(1)).WillRepeatedly(Return(&spellMock));
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
const int64_t EFFECT_VALUE = 101;
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(0);
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(0);
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(0);
|
||||
EXPECT_CALL(unitMock, getAvailableHealth()).WillOnce(Return(UNIT_HP));
|
||||
EXPECT_CALL(mechanicsMock, getEffectValue()).WillOnce(Return(EFFECT_VALUE));
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
{
|
||||
ownerMatches = ::testing::get<0>(GetParam());
|
||||
isMagicalEffect = ::testing::get<1>(GetParam());
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
EXPECT_CALL(mechanicsMock, isMagicalEffect()).Times(AtLeast(0)).WillRepeatedly(Return(isMagicalEffect));
|
||||
EXPECT_CALL(mechanicsMock, ownerMatches(Eq(&unitMock), Field(&boost::logic::tribool::value, boost::logic::tribool::false_value))).WillRepeatedly(Return(ownerMatches));
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
isMagicalEffect = GetParam();
|
||||
EXPECT_CALL(mechanicsMock, isMagicalEffect()).WillRepeatedly(Return(isMagicalEffect));
|
||||
if(isMagicalEffect)
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
EXPECT_CALL(mechanicsMock, getSpellIndex()).WillRepeatedly(Return(castSpell));
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
isPositive = ::testing::get<0>(GetParam());
|
||||
hasBonus = ::testing::get<1>(GetParam());
|
||||
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(0));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
EXPECT_CALL(mechanicsMock, isPositiveSpell()).WillRepeatedly(Return(isPositive));
|
||||
if(hasBonus)
|
||||
|
@ -21,7 +21,7 @@ class SpellEffectConditionTest : public TargetConditionItemTest
|
||||
public:
|
||||
void setDefaultExpectations()
|
||||
{
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getAllBonuses(_, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ protected:
|
||||
void SetUp() override
|
||||
{
|
||||
using namespace ::testing;
|
||||
ON_CALL(unitMock, getAllBonuses(_, _, _)).WillByDefault(Invoke(&unitBonuses, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(unitMock, getAllBonuses(_, _)).WillByDefault(Invoke(&unitBonuses, &BonusBearerMock::getAllBonuses));
|
||||
ON_CALL(unitMock, getTreeVersion()).WillByDefault(Invoke(&unitBonuses, &BonusBearerMock::getTreeVersion));
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user