1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-23 00:28:08 +02:00

Feature: Town Negative Bonuses. Fix: Negative bonus bearers should not be affected

This commit is contained in:
Dmitry Orlov
2021-02-03 20:19:56 +03:00
parent 3cb88b6964
commit 6f0864b47c
6 changed files with 86 additions and 40 deletions

View File

@ -404,18 +404,22 @@ void MoraleLuckBox::set(const IBonusBearer * node)
text += "\n" + noLuck->Description(); text += "\n" + noLuck->Description();
bonusValue = 0; bonusValue = 0;
} }
else if(modifierList->empty())
text += CGI->generaltexth->arraytxt[noneTxtId];//no modifiers
else else
{ {
for(auto& elem : *modifierList) bool isListActual = false;
{ std::string addInfo = "";
if(elem->val != 0)
//no bonuses with value 0
text += "\n" + elem->Description();
}
}
for(auto & bonus : * modifierList)
{
if(bonus->val && bonus->effectRange != Bonus::ONLY_ENEMY_ARMY)
{
addInfo += "\n" + bonus->Description();
isListActual = true;
}
}
text = isListActual ? text + addInfo
: text + CGI->generaltexth->arraytxt[noneTxtId];//no modifiers
}
std::string imageName; std::string imageName;
if (small) if (small)
imageName = morale ? "IMRL30": "ILCK30"; imageName = morale ? "IMRL30": "ILCK30";

View File

@ -428,6 +428,9 @@ int BonusList::totalValue() const
for(std::shared_ptr<Bonus> b : bonuses) for(std::shared_ptr<Bonus> b : bonuses)
{ {
if(b->effectRange == Bonus::ONLY_ENEMY_ARMY)
continue;
switch(b->valType) switch(b->valType)
{ {
case Bonus::BASE_NUMBER: case Bonus::BASE_NUMBER:
@ -452,7 +455,6 @@ int BonusList::totalValue() const
{ {
vstd::amax(indepMax, b->val); vstd::amax(indepMax, b->val);
} }
break; break;
case Bonus::INDEPENDENT_MIN: case Bonus::INDEPENDENT_MIN:
if (!hasIndepMin) if (!hasIndepMin)
@ -464,7 +466,6 @@ int BonusList::totalValue() const
{ {
vstd::amin(indepMin, b->val); vstd::amin(indepMin, b->val);
} }
break; break;
} }
} }
@ -595,11 +596,14 @@ void BonusList::insert(BonusList::TInternalContainer::iterator position, BonusLi
changed(); changed();
} }
CSelector IBonusBearer::anaffectedByMoraleSelector CSelector IBonusBearer::anaffectedByMoraleSelector =
= Selector::type()(Bonus::NON_LIVING) Selector::type()(Bonus::NON_LIVING)
.Or(Selector::type()(Bonus::UNDEAD)) .Or(Selector::type()(Bonus::UNDEAD))
.Or(Selector::type()(Bonus::NO_MORALE)) .Or(Selector::type()(Bonus::SIEGE_WEAPON))
.Or(Selector::type()(Bonus::SIEGE_WEAPON)); .Or(Selector::effectRange()(Bonus::ONLY_ENEMY_ARMY).Not()
.And(Selector::type()(Bonus::NO_MORALE)
.Or(Selector::type()(Bonus::BLOCK_MORALE))
));
CSelector IBonusBearer::moraleSelector = Selector::type()(Bonus::MORALE); CSelector IBonusBearer::moraleSelector = Selector::type()(Bonus::MORALE);
CSelector IBonusBearer::luckSelector = Selector::type()(Bonus::LUCK); CSelector IBonusBearer::luckSelector = Selector::type()(Bonus::LUCK);

View File

@ -56,6 +56,11 @@ public:
auto thisCopy = *this; auto thisCopy = *this;
return [thisCopy, rhs](const Bonus *b) mutable { return thisCopy(b) || rhs(b); }; return [thisCopy, rhs](const Bonus *b) mutable { return thisCopy(b) || rhs(b); };
} }
CSelector Not() const
{
auto thisCopy = *this;
return [thisCopy](const Bonus *b) mutable { return !thisCopy(b); };
}
bool operator()(const Bonus *b) const bool operator()(const Bonus *b) const
{ {

View File

@ -186,6 +186,45 @@ struct RangeGenerator
std::function<int()> myRand; std::function<int()> myRand;
}; };
void BattleInfo::addOnlyEnemyArmyBonuses(const BonusList & bonusList, BattleInfo * curB, BattleInfo::BattleSide side)
{
for(auto b : bonusList)
{
if(b->effectRange != Bonus::ONLY_ENEMY_ARMY)
continue;
auto bCopy = std::make_shared<Bonus>(*b);
bCopy->effectRange = Bonus::NO_LIMIT;
bCopy->propagator.reset();
bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[side].color));
curB->addNewBonus(bCopy);
}
}
void BattleInfo::setupBonusesFromTown(const CGTownInstance * town, BattleInfo * curB)
{
assert(town);
for(auto building : town->builtBuildings)
{
const auto & bonuses = town->town->buildings.at(building)->buildingBonuses;
addOnlyEnemyArmyBonuses(bonuses, curB, BattleInfo::ATTACKER);
}
}
void BattleInfo::setupBonusesFromUnits(BattleInfo * curB)
{
for(int i = 0; i < 2; i++)
{
TNodes nodes;
curB->battleGetArmyObject(i)->getRedAncestors(nodes);
for(CBonusSystemNode * n : nodes)
{
const auto & bonuses = n->getExportedBonusList();
addOnlyEnemyArmyBonuses(bonuses, curB, (BattleInfo::BattleSide)!i);
}
}
}
BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town) BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town)
{ {
CMP_stack cmpst; CMP_stack cmpst;
@ -528,7 +567,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
//overlay premies given //overlay premies given
//native terrain bonuses //native terrain bonuses
auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>(); static auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>();
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::STACKS_SPEED, Bonus::TERRAIN_NATIVE, 1, 0, 0)->addLimiter(nativeTerrain)); curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::STACKS_SPEED, Bonus::TERRAIN_NATIVE, 1, 0, 0)->addLimiter(nativeTerrain));
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::TERRAIN_NATIVE, 1, 0, PrimarySkill::ATTACK)->addLimiter(nativeTerrain)); curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::TERRAIN_NATIVE, 1, 0, PrimarySkill::ATTACK)->addLimiter(nativeTerrain));
@ -555,31 +594,13 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
else else
curB->tacticDistance = 0; curB->tacticDistance = 0;
if(town)
// workaround — bonuses affecting only enemy - DOES NOT WORK setupBonusesFromTown(town, curB);
for(int i = 0; i < 2; i++) setupBonusesFromUnits(curB);
{
TNodes nodes;
curB->battleGetArmyObject(i)->getRedAncestors(nodes);
for(CBonusSystemNode *n : nodes)
{
for(auto b : n->getExportedBonusList())
{
if(b->effectRange == Bonus::ONLY_ENEMY_ARMY/* && b->propagator && b->propagator->shouldBeAttached(curB)*/)
{
auto bCopy = std::make_shared<Bonus>(*b);
bCopy->effectRange = Bonus::NO_LIMIT;
bCopy->propagator.reset();
bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[!i].color));
curB->addNewBonus(bCopy);
}
}
}
}
return curB; return curB;
} }
const CGHeroInstance * BattleInfo::getHero(PlayerColor player) const const CGHeroInstance * BattleInfo::getHero(PlayerColor player) const
{ {
for(int i = 0; i < sides.size(); i++) for(int i = 0; i < sides.size(); i++)

View File

@ -22,6 +22,11 @@ class CStackBasicDescriptor;
class DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback, public IBattleState class DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback, public IBattleState
{ {
public: public:
enum BattleSide
{
ATTACKER = 0,
DEFENDER
};
std::array<SideInBattle, 2> sides; //sides[0] - attacker, sides[1] - defender std::array<SideInBattle, 2> sides; //sides[0] - attacker, sides[1] - defender
si32 round, activeStack; si32 round, activeStack;
const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege) const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege)
@ -133,6 +138,8 @@ public:
void localInit(); void localInit();
static void setupBonusesFromTown(const CGTownInstance * town, BattleInfo * curB); //besieged city may contain buildings with negative bonuses for enemy army.
static void setupBonusesFromUnits(BattleInfo * curB); //besieged city may contain buildings with negative bonuses for enemy army.
static BattleInfo * setupBattle(int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town); static BattleInfo * setupBattle(int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town);
ui8 whatSide(PlayerColor player) const; ui8 whatSide(PlayerColor player) const;
@ -141,6 +148,9 @@ public:
protected: protected:
scripting::Pool * getContextPool() const override; scripting::Pool * getContextPool() const override;
private:
static void addOnlyEnemyArmyBonuses(const BonusList & bonusList, BattleInfo * curB, BattleInfo::BattleSide side);
}; };

View File

@ -1265,6 +1265,8 @@ void CGTownInstance::recreateBuildingsBonuses()
for(auto bonus : building->buildingBonuses) for(auto bonus : building->buildingBonuses)
{ {
if(bonus->effectRange == Bonus::ONLY_ENEMY_ARMY) //will be added in the 'setupBattle' to Battle node.
continue;
if(bonus->propagator != nullptr && bonus->propagator->getPropagatorType() == ALL_CREATURES) if(bonus->propagator != nullptr && bonus->propagator->getPropagatorType() == ALL_CREATURES)
VLC->creh->addBonusForAllCreatures(bonus); VLC->creh->addBonusForAllCreatures(bonus);
else else