From f861d3a509d28d9245f8fa37291f2db5b46baa43 Mon Sep 17 00:00:00 2001 From: Dmitry Orlov Date: Fri, 5 Feb 2021 01:56:21 +0300 Subject: [PATCH] Fix: Visiting hero should receive town bonuses during town siege --- lib/CCreatureHandler.cpp | 10 +++++-- lib/CCreatureHandler.h | 4 +-- lib/CTownHandler.cpp | 10 +++++-- lib/CTownHandler.h | 2 +- lib/HeroBonus.cpp | 24 +++++++-------- lib/HeroBonus.h | 2 +- lib/battle/BattleInfo.cpp | 61 +++++++++++++++++++++++++++++++-------- lib/battle/BattleInfo.h | 8 +++-- 8 files changed, 84 insertions(+), 37 deletions(-) diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 8ea7f8461..43ec8a1dc 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -1322,14 +1322,20 @@ CreatureID CCreatureHandler::pickRandomMonster(CRandomGenerator & rand, int tier return CreatureID(r); } -void CCreatureHandler::addBonusForTier(int tier, std::shared_ptr b) +void CCreatureHandler::addBonusForTier(int tier, const std::shared_ptr & b) { assert(vstd::iswithin(tier, 1, 7)); creaturesOfLevel[tier].addNewBonus(b); } -void CCreatureHandler::addBonusForAllCreatures(std::shared_ptr b) +void CCreatureHandler::addBonusForAllCreatures(const std::shared_ptr & b) { + const auto & exportedBonuses = allCreatures.getExportedBonusList(); + for(const auto & bonus : exportedBonuses) + { + if(bonus->type == b->type && bonus->subtype == b->subtype) + return; + } allCreatures.addNewBonus(b); } diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index 8472fc1f4..dfdcd3e01 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -278,8 +278,8 @@ public: void deserializationFix(); CreatureID pickRandomMonster(CRandomGenerator & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any - void addBonusForTier(int tier, std::shared_ptr b); //tier must be <1-7> - void addBonusForAllCreatures(std::shared_ptr b); + void addBonusForTier(int tier, const std::shared_ptr & b); //tier must be <1-7> + void addBonusForAllCreatures(const std::shared_ptr & b); //due to CBonusSystem::addNewBonus(const std::shared_ptr& b); void removeBonusesFromAllCreatures(); void restoreAllCreaturesNodeType794(); //restore ALL_CREATURES node type for old saves diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index 574bd4dbd..4ea32f2da 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -377,7 +377,11 @@ JsonNode readBuilding(CLegacyConfigParser & parser) return ret; } -TPropagatorPtr CTownHandler::emptyPropagator = std::make_shared(); +TPropagatorPtr & CTownHandler::emptyPropagator() +{ + static TPropagatorPtr emptyProp(nullptr); + return emptyProp; +} std::vector CTownHandler::loadLegacyData(size_t dataSize) { @@ -618,7 +622,7 @@ void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) std::shared_ptr CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype) { - return createBonus(build, type, val, emptyPropagator, subtype); + return createBonus(build, type, val, emptyPropagator(), subtype); } std::shared_ptr CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype) @@ -657,7 +661,7 @@ void CTownHandler::loadSpecialBuildingBonuses(const JsonNode & source, BonusList //JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty. if(bonus->propagator != nullptr && bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN) - bonus->addPropagator(emptyPropagator); + bonus->addPropagator(emptyPropagator()); building->addNewBonus(bonus, bonusList); } } diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index a77fad63e..e199b1f36 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -388,7 +388,7 @@ class DLL_LINKAGE CTownHandler : public CHandlerBasegetBonusesRec(beforeUpdate, selector, limit); + const CBonusSystemNode * parent = cparent; + out.insert(parent); + parent->getAllParents(out); } - bonuses.getBonuses(beforeUpdate, selector, limit); - - for(auto b : beforeUpdate) - out.push_back(update(b)); } void CBonusSystemNode::getAllBonusesRec(BonusList &out) const { BonusList beforeUpdate; - FOREACH_CPARENT(p) - { - p->getAllBonusesRec(beforeUpdate); - } + TCNodes lparents; + getAllParents(lparents); + + for(auto parent : lparents) + parent->bonuses.getAllBonuses(beforeUpdate); + bonuses.getAllBonuses(beforeUpdate); for(auto b : beforeUpdate) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 59f693b0f..b28afed3b 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -769,7 +769,6 @@ private: mutable std::map cachedRequests; mutable boost::mutex sync; - void getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const; void getAllBonusesRec(BonusList &out) const; TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const; std::shared_ptr update(const std::shared_ptr & b) const; @@ -793,6 +792,7 @@ public: void getRedAncestors(TNodes &out); void getRedChildren(TNodes &out); void getRedDescendants(TNodes &out); + void getAllParents(TCNodes & out) const; std::shared_ptr getBonusLocalFirst(const CSelector &selector); void attachTo(CBonusSystemNode *parent); diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index eabde3f93..d57f0ad83 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -186,38 +186,70 @@ struct RangeGenerator std::function myRand; }; -void BattleInfo::addOnlyEnemyArmyBonuses(const BonusList & bonusList, BattleInfo * curB, BattleInfo::BattleSide side) + +std::shared_ptr BattleInfo::makeBonusForEnemy(const std::shared_ptr & b, PlayerColor & enemyColor) { - for(auto b : bonusList) + auto bCopy = std::make_shared(*b); + bCopy->effectRange = Bonus::NO_LIMIT; + bCopy->propagator.reset(); + bCopy->limiter.reset(new StackOwnerLimiter(enemyColor)); + return bCopy; +} + +void BattleInfo::addOnlyEnemyArmyBonuses(const BonusList & bonusList, BattleInfo * curB, BattleInfo::BattleSide enemySide) +{ + for(const auto & b : bonusList) //don't copy shared_ptr object, don't change it { if(b->effectRange != Bonus::ONLY_ENEMY_ARMY) continue; - auto bCopy = std::make_shared(*b); - bCopy->effectRange = Bonus::NO_LIMIT; - bCopy->propagator.reset(); - bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[side].color)); + auto bCopy = makeBonusForEnemy(b, curB->sides[enemySide].color); curB->addNewBonus(bCopy); } } -void BattleInfo::setupBonusesFromTown(const CGTownInstance * town, BattleInfo * curB) +void BattleInfo::setupBonusesFromTownToEnemy(const CGTownInstance * town, BattleInfo * curB) { assert(town); - for(auto building : town->builtBuildings) + for(const auto & building : town->builtBuildings) { const auto & bonuses = town->town->buildings.at(building)->buildingBonuses; addOnlyEnemyArmyBonuses(bonuses, curB, BattleInfo::ATTACKER); } } -void BattleInfo::setupBonusesFromUnits(BattleInfo * curB) +void BattleInfo::setupBonusesFromTownToBothSides(const CGTownInstance * town, BattleInfo * curB) +{ + assert(town); + for(const auto & building : town->builtBuildings) + { + const auto & bonuses = town->town->buildings.at(building)->buildingBonuses; + + for(const auto & b : bonuses) + { + if(b->effectRange == Bonus::ONLY_ENEMY_ARMY) + { + auto bCopy = makeBonusForEnemy(b, curB->sides[BattleInfo::ATTACKER].color); + curB->addNewBonus(bCopy); + } + else + { + auto bCopy = std::make_shared(*b); + bCopy->propagator.reset(); + bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[BattleInfo::DEFENDER].color)); + curB->addNewBonus(bCopy); + } + } + } +} + +void BattleInfo::setupBonusesFromUnitsToEnemy(BattleInfo * curB) { for(int i = 0; i < 2; i++) { TNodes nodes; curB->battleGetArmyObject(i)->getRedAncestors(nodes); - for(CBonusSystemNode * n : nodes) + for(auto n : nodes) { const auto & bonuses = n->getExportedBonusList(); addOnlyEnemyArmyBonuses(bonuses, curB, (BattleInfo::BattleSide)!i); @@ -595,8 +627,13 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType curB->tacticDistance = 0; if(town) - setupBonusesFromTown(town, curB); - setupBonusesFromUnits(curB); + { + if(town->visitingHero == heroes[BattleSide::DEFENDER]) + setupBonusesFromTownToBothSides(town, curB); + else + setupBonusesFromTownToEnemy(town, curB); + } + setupBonusesFromUnitsToEnemy(curB); return curB; } diff --git a/lib/battle/BattleInfo.h b/lib/battle/BattleInfo.h index 6310ee642..a89e4554a 100644 --- a/lib/battle/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -138,8 +138,9 @@ public: 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 void setupBonusesFromTownToEnemy(const CGTownInstance * town, BattleInfo * curB); //besieged city may contain buildings with negative bonuses for enemy army + static void setupBonusesFromTownToBothSides(const CGTownInstance * town, BattleInfo * curB); //if visiting hero is town defender during siege, he/she should receive bonuses + static void setupBonusesFromUnitsToEnemy(BattleInfo * curB); 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; @@ -150,7 +151,8 @@ protected: scripting::Pool * getContextPool() const override; private: - static void addOnlyEnemyArmyBonuses(const BonusList & bonusList, BattleInfo * curB, BattleInfo::BattleSide side); + static void addOnlyEnemyArmyBonuses(const BonusList & bonusList, BattleInfo * curB, BattleInfo::BattleSide enemySide); + STRONG_INLINE static std::shared_ptr makeBonusForEnemy(const std::shared_ptr & b, PlayerColor & enemyColor); };