1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Fix: Visiting hero should receive town bonuses during town siege

This commit is contained in:
Dmitry Orlov 2021-02-05 01:56:21 +03:00
parent 6f0864b47c
commit f861d3a509
8 changed files with 84 additions and 37 deletions

View File

@ -1322,14 +1322,20 @@ CreatureID CCreatureHandler::pickRandomMonster(CRandomGenerator & rand, int tier
return CreatureID(r); return CreatureID(r);
} }
void CCreatureHandler::addBonusForTier(int tier, std::shared_ptr<Bonus> b) void CCreatureHandler::addBonusForTier(int tier, const std::shared_ptr<Bonus> & b)
{ {
assert(vstd::iswithin(tier, 1, 7)); assert(vstd::iswithin(tier, 1, 7));
creaturesOfLevel[tier].addNewBonus(b); creaturesOfLevel[tier].addNewBonus(b);
} }
void CCreatureHandler::addBonusForAllCreatures(std::shared_ptr<Bonus> b) void CCreatureHandler::addBonusForAllCreatures(const std::shared_ptr<Bonus> & b)
{ {
const auto & exportedBonuses = allCreatures.getExportedBonusList();
for(const auto & bonus : exportedBonuses)
{
if(bonus->type == b->type && bonus->subtype == b->subtype)
return;
}
allCreatures.addNewBonus(b); allCreatures.addNewBonus(b);
} }

View File

@ -278,8 +278,8 @@ public:
void deserializationFix(); void deserializationFix();
CreatureID pickRandomMonster(CRandomGenerator & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any CreatureID pickRandomMonster(CRandomGenerator & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any
void addBonusForTier(int tier, std::shared_ptr<Bonus> b); //tier must be <1-7> void addBonusForTier(int tier, const std::shared_ptr<Bonus> & b); //tier must be <1-7>
void addBonusForAllCreatures(std::shared_ptr<Bonus> b); void addBonusForAllCreatures(const std::shared_ptr<Bonus> & b); //due to CBonusSystem::addNewBonus(const std::shared_ptr<Bonus>& b);
void removeBonusesFromAllCreatures(); void removeBonusesFromAllCreatures();
void restoreAllCreaturesNodeType794(); //restore ALL_CREATURES node type for old saves void restoreAllCreaturesNodeType794(); //restore ALL_CREATURES node type for old saves

View File

@ -377,7 +377,11 @@ JsonNode readBuilding(CLegacyConfigParser & parser)
return ret; return ret;
} }
TPropagatorPtr CTownHandler::emptyPropagator = std::make_shared<CPropagatorNodeType>(); TPropagatorPtr & CTownHandler::emptyPropagator()
{
static TPropagatorPtr emptyProp(nullptr);
return emptyProp;
}
std::vector<JsonNode> CTownHandler::loadLegacyData(size_t dataSize) std::vector<JsonNode> CTownHandler::loadLegacyData(size_t dataSize)
{ {
@ -618,7 +622,7 @@ void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building)
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype) std::shared_ptr<Bonus> 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<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype) std::shared_ptr<Bonus> 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. //JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty.
if(bonus->propagator != nullptr if(bonus->propagator != nullptr
&& bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN) && bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN)
bonus->addPropagator(emptyPropagator); bonus->addPropagator(emptyPropagator());
building->addNewBonus(bonus, bonusList); building->addNewBonus(bonus, bonusList);
} }
} }

View File

@ -388,7 +388,7 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
const static ETerrainType::EETerrainType defaultEvilTerrain = ETerrainType::EETerrainType::LAVA; const static ETerrainType::EETerrainType defaultEvilTerrain = ETerrainType::EETerrainType::LAVA;
const static ETerrainType::EETerrainType defaultNeutralTerrain = ETerrainType::EETerrainType::ROUGH; const static ETerrainType::EETerrainType defaultNeutralTerrain = ETerrainType::EETerrainType::ROUGH;
static TPropagatorPtr emptyPropagator; static TPropagatorPtr & emptyPropagator();
void initializeRequirements(); void initializeRequirements();
void initializeOverridden(); void initializeOverridden();

View File

@ -26,7 +26,6 @@
#include "battle/BattleInfo.h" #include "battle/BattleInfo.h"
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents) #define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
#define FOREACH_CPARENT(pname) TCNodes lparents; getParents(lparents); for(const CBonusSystemNode *pname : lparents)
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren) #define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
#define FOREACH_RED_PARENT(pname) TNodes lparents; getRedParents(lparents); for(CBonusSystemNode *pname : lparents) #define FOREACH_RED_PARENT(pname) TNodes lparents; getRedParents(lparents); for(CBonusSystemNode *pname : lparents)
@ -873,26 +872,25 @@ void CBonusSystemNode::getParents(TNodes &out)
} }
} }
void CBonusSystemNode::getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const void CBonusSystemNode::getAllParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
{ {
BonusList beforeUpdate; for(auto & cparent : parents)
FOREACH_CPARENT(p)
{ {
p->getBonusesRec(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 void CBonusSystemNode::getAllBonusesRec(BonusList &out) const
{ {
BonusList beforeUpdate; BonusList beforeUpdate;
FOREACH_CPARENT(p) TCNodes lparents;
{ getAllParents(lparents);
p->getAllBonusesRec(beforeUpdate);
} for(auto parent : lparents)
parent->bonuses.getAllBonuses(beforeUpdate);
bonuses.getAllBonuses(beforeUpdate); bonuses.getAllBonuses(beforeUpdate);
for(auto b : beforeUpdate) for(auto b : beforeUpdate)

View File

@ -769,7 +769,6 @@ private:
mutable std::map<std::string, TBonusListPtr > cachedRequests; mutable std::map<std::string, TBonusListPtr > cachedRequests;
mutable boost::mutex sync; mutable boost::mutex sync;
void getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const;
void getAllBonusesRec(BonusList &out) const; void getAllBonusesRec(BonusList &out) const;
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const; TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
std::shared_ptr<Bonus> update(const std::shared_ptr<Bonus> & b) const; std::shared_ptr<Bonus> update(const std::shared_ptr<Bonus> & b) const;
@ -793,6 +792,7 @@ public:
void getRedAncestors(TNodes &out); void getRedAncestors(TNodes &out);
void getRedChildren(TNodes &out); void getRedChildren(TNodes &out);
void getRedDescendants(TNodes &out); void getRedDescendants(TNodes &out);
void getAllParents(TCNodes & out) const;
std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector &selector); std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector &selector);
void attachTo(CBonusSystemNode *parent); void attachTo(CBonusSystemNode *parent);

View File

@ -186,38 +186,70 @@ struct RangeGenerator
std::function<int()> myRand; std::function<int()> myRand;
}; };
void BattleInfo::addOnlyEnemyArmyBonuses(const BonusList & bonusList, BattleInfo * curB, BattleInfo::BattleSide side)
std::shared_ptr<Bonus> BattleInfo::makeBonusForEnemy(const std::shared_ptr<Bonus> & b, PlayerColor & enemyColor)
{ {
for(auto b : bonusList) auto bCopy = std::make_shared<Bonus>(*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) if(b->effectRange != Bonus::ONLY_ENEMY_ARMY)
continue; continue;
auto bCopy = std::make_shared<Bonus>(*b); auto bCopy = makeBonusForEnemy(b, curB->sides[enemySide].color);
bCopy->effectRange = Bonus::NO_LIMIT;
bCopy->propagator.reset();
bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[side].color));
curB->addNewBonus(bCopy); curB->addNewBonus(bCopy);
} }
} }
void BattleInfo::setupBonusesFromTown(const CGTownInstance * town, BattleInfo * curB) void BattleInfo::setupBonusesFromTownToEnemy(const CGTownInstance * town, BattleInfo * curB)
{ {
assert(town); assert(town);
for(auto building : town->builtBuildings) for(const auto & building : town->builtBuildings)
{ {
const auto & bonuses = town->town->buildings.at(building)->buildingBonuses; const auto & bonuses = town->town->buildings.at(building)->buildingBonuses;
addOnlyEnemyArmyBonuses(bonuses, curB, BattleInfo::ATTACKER); 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<Bonus>(*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++) for(int i = 0; i < 2; i++)
{ {
TNodes nodes; TNodes nodes;
curB->battleGetArmyObject(i)->getRedAncestors(nodes); curB->battleGetArmyObject(i)->getRedAncestors(nodes);
for(CBonusSystemNode * n : nodes) for(auto n : nodes)
{ {
const auto & bonuses = n->getExportedBonusList(); const auto & bonuses = n->getExportedBonusList();
addOnlyEnemyArmyBonuses(bonuses, curB, (BattleInfo::BattleSide)!i); addOnlyEnemyArmyBonuses(bonuses, curB, (BattleInfo::BattleSide)!i);
@ -595,8 +627,13 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
curB->tacticDistance = 0; curB->tacticDistance = 0;
if(town) if(town)
setupBonusesFromTown(town, curB); {
setupBonusesFromUnits(curB); if(town->visitingHero == heroes[BattleSide::DEFENDER])
setupBonusesFromTownToBothSides(town, curB);
else
setupBonusesFromTownToEnemy(town, curB);
}
setupBonusesFromUnitsToEnemy(curB);
return curB; return curB;
} }

View File

@ -138,8 +138,9 @@ 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 setupBonusesFromTownToEnemy(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 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); 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;
@ -150,7 +151,8 @@ protected:
scripting::Pool * getContextPool() const override; scripting::Pool * getContextPool() const override;
private: 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<Bonus> makeBonusForEnemy(const std::shared_ptr<Bonus> & b, PlayerColor & enemyColor);
}; };