1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-02 00:10:22 +02:00

vcmi: split CBonusSystemNode, BonusParams and prop

More splitting of HeroBonus.h
This commit is contained in:
Konstantin 2023-04-30 17:35:15 +03:00
parent 713e3004df
commit 1d34c73c2d
24 changed files with 1253 additions and 1115 deletions

View File

@ -22,8 +22,10 @@
#include "../../lib/CGameState.h"
#include "../../lib/NetPacksBase.h"
#include "../../lib/NetPacks.h"
#include "../../lib/bonuses/CBonusSystemNode.h"
#include "../../lib/bonuses/Limiters.h"
#include "../../lib/bonuses/Updaters.h"
#include "../../lib/bonuses/Propagators.h"
#include "../../lib/serializer/CTypeList.h"
#include "../../lib/serializer/BinarySerializer.h"
#include "../../lib/serializer/BinaryDeserializer.h"

View File

@ -43,8 +43,10 @@
#include "../lib/CArtHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/bonuses/CBonusSystemNode.h"
#include "../lib/bonuses/Limiters.h"
#include "../lib/bonuses/Updaters.h"
#include "../lib/bonuses/Propagators.h"
#include "../lib/serializer/CTypeList.h"
#include "../lib/serializer/BinaryDeserializer.h"
#include "../lib/serializer/BinarySerializer.h"

View File

@ -27,9 +27,12 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/battle/SiegeInfo.cpp
${MAIN_LIB_DIR}/battle/Unit.cpp
${MAIN_LIB_DIR}/bonuses/BonusParams.cpp
${MAIN_LIB_DIR}/bonuses/CBonusProxy.cpp
${MAIN_LIB_DIR}/bonuses/CBonusSystemNode.cpp
${MAIN_LIB_DIR}/bonuses/HeroBonus.cpp
${MAIN_LIB_DIR}/bonuses/Limiters.cpp
${MAIN_LIB_DIR}/bonuses/Propagators.cpp
${MAIN_LIB_DIR}/bonuses/Updaters.cpp
${MAIN_LIB_DIR}/events/ApplyDamage.cpp
@ -304,9 +307,12 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/battle/SiegeInfo.h
${MAIN_LIB_DIR}/battle/Unit.h
${MAIN_LIB_DIR}/bonuses/BonusParams.h
${MAIN_LIB_DIR}/bonuses/CBonusProxy.h
${MAIN_LIB_DIR}/bonuses/CBonusSystemNode.h
${MAIN_LIB_DIR}/bonuses/HeroBonus.h
${MAIN_LIB_DIR}/bonuses/Limiters.h
${MAIN_LIB_DIR}/bonuses/Propagators.h
${MAIN_LIB_DIR}/bonuses/Updaters.h
${MAIN_LIB_DIR}/events/ApplyDamage.h

View File

@ -13,6 +13,7 @@
#include <vcmi/ArtifactService.h>
#include "bonuses/HeroBonus.h"
#include "bonuses/CBonusSystemNode.h"
#include "GameConstants.h"
#include "IHandlerBase.h"

View File

@ -10,6 +10,7 @@
#pragma once
#include "bonuses/HeroBonus.h"
#include "bonuses/CBonusSystemNode.h"
#include "ConstTransitivePtr.h"
#include "ResourceSet.h"
#include "GameConstants.h"

View File

@ -10,6 +10,7 @@
#pragma once
#include "bonuses/HeroBonus.h"
#include "bonuses/CBonusSystemNode.h"
#include "GameConstants.h"
#include "CArtHandler.h"
#include "CCreatureHandler.h"

View File

@ -13,6 +13,7 @@
#include <vcmi/Team.h>
#include "bonuses/HeroBonus.h"
#include "bonuses/CBonusSystemNode.h"
#include "ResourceSet.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -11,6 +11,7 @@
#pragma once
#include "JsonNode.h"
#include "bonuses/HeroBonus.h"
#include "bonuses/CBonusSystemNode.h"
#include "CCreatureHandler.h" //todo: remove
#include "battle/BattleHex.h"
#include "mapObjects/CGHeroInstance.h" // for commander serialization

View File

@ -24,6 +24,7 @@
#include "mapObjects/CObjectClassesHandler.h"
#include "mapObjects/CObjectHandler.h"
#include "bonuses/HeroBonus.h"
#include "bonuses/Propagators.h"
#include "ResourceSet.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -18,7 +18,9 @@
#include "CModHandler.h"
#include "BattleFieldHandler.h"
#include "ObstacleHandler.h"
#include "bonuses/CBonusSystemNode.h"
#include "bonuses/Limiters.h"
#include "bonuses/Propagators.h"
#include "bonuses/Updaters.h"
#include "serializer/CSerializer.h" // for SAVEGAME_MAGIC

View File

@ -13,8 +13,10 @@
#include "ScopeGuard.h"
#include "bonuses/BonusParams.h"
#include "bonuses/HeroBonus.h"
#include "bonuses/Limiters.h"
#include "bonuses/Propagators.h"
#include "bonuses/Updaters.h"
#include "filesystem/Filesystem.h"
#include "VCMI_Lib.h" //for identifier resolution

View File

@ -10,6 +10,7 @@
#pragma once
#include "../int3.h"
#include "../bonuses/HeroBonus.h"
#include "../bonuses/CBonusSystemNode.h"
#include "CBattleInfoCallback.h"
#include "IBattleState.h"
#include "SiegeInfo.h"

259
lib/bonuses/BonusParams.cpp Normal file
View File

@ -0,0 +1,259 @@
/*
* BonusParams.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "BonusParams.h"
#include "../ResourceSet.h"
VCMI_LIB_NAMESPACE_BEGIN
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
isConverted(true)
{
if(deprecatedTypeStr == "SECONDARY_SKILL_PREMY" || deprecatedTypeStr == "SPECIAL_SECONDARY_SKILL")
{
if(deprecatedSubtype == SecondarySkill::PATHFINDING || deprecatedSubtypeStr == "skill.pathfinding")
type = Bonus::ROUGH_TERRAIN_DISCOUNT;
else if(deprecatedSubtype == SecondarySkill::DIPLOMACY || deprecatedSubtypeStr == "skill.diplomacy")
type = Bonus::WANDERING_CREATURES_JOIN_BONUS;
else if(deprecatedSubtype == SecondarySkill::WISDOM || deprecatedSubtypeStr == "skill.wisdom")
type = Bonus::MAX_LEARNABLE_SPELL_LEVEL;
else if(deprecatedSubtype == SecondarySkill::MYSTICISM || deprecatedSubtypeStr == "skill.mysticism")
type = Bonus::MANA_REGENERATION;
else if(deprecatedSubtype == SecondarySkill::NECROMANCY || deprecatedSubtypeStr == "skill.necromancy")
type = Bonus::UNDEAD_RAISE_PERCENTAGE;
else if(deprecatedSubtype == SecondarySkill::LEARNING || deprecatedSubtypeStr == "skill.learning")
type = Bonus::HERO_EXPERIENCE_GAIN_PERCENT;
else if(deprecatedSubtype == SecondarySkill::RESISTANCE || deprecatedSubtypeStr == "skill.resistance")
type = Bonus::MAGIC_RESISTANCE;
else if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
type = Bonus::LEARN_BATTLE_SPELL_CHANCE;
else if(deprecatedSubtype == SecondarySkill::SCOUTING || deprecatedSubtypeStr == "skill.scouting")
type = Bonus::SIGHT_RADIUS;
else if(deprecatedSubtype == SecondarySkill::INTELLIGENCE || deprecatedSubtypeStr == "skill.intelligence")
{
type = Bonus::MANA_PER_KNOWLEDGE;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
}
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
type = Bonus::SPELL_DAMAGE;
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
type = Bonus::LEARN_MEETING_SPELL_LIMIT;
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
{
subtype = 1;
subtypeRelevant = true;
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
}
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
{
subtype = 0;
subtypeRelevant = true;
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
}
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
{
subtype = -1;
subtypeRelevant = true;
type = Bonus::GENERAL_DAMAGE_REDUCTION;
}
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
{
subtype = 0;
subtypeRelevant = true;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
{
subtype = 1;
subtypeRelevant = true;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
{
type = Bonus::GENERATE_RESOURCE;
subtype = GameResID(EGameResID::GOLD);
subtypeRelevant = true;
}
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 4;
}
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 1;
}
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 2;
}
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 8;
}
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
{
type = Bonus::BONUS_DAMAGE_CHANCE;
subtypeRelevant = true;
subtypeStr = "core:creature.ballista";
}
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
{
type = Bonus::SPECIFIC_SPELL_POWER;
subtypeRelevant = true;
subtypeStr = "core:spell.firstAid";
}
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
{
type = Bonus::CATAPULT_EXTRA_SHOTS;
subtypeRelevant = true;
subtypeStr = "core:spell.catapultShot";
}
else
isConverted = false;
}
else if (deprecatedTypeStr == "SECONDARY_SKILL_VAL2")
{
if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
type = Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT;
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
{
type = Bonus::HERO_GRANTS_ATTACKS;
subtypeRelevant = true;
subtypeStr = "core:creature.ballista";
}
else
isConverted = false;
}
else if (deprecatedTypeStr == "SEA_MOVEMENT")
{
subtype = 0;
subtypeRelevant = true;
valueType = Bonus::ADDITIVE_VALUE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if (deprecatedTypeStr == "LAND_MOVEMENT")
{
subtype = 1;
subtypeRelevant = true;
valueType = Bonus::ADDITIVE_VALUE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if (deprecatedTypeStr == "MAXED_SPELL")
{
type = Bonus::SPELL;
subtypeStr = deprecatedSubtypeStr;
subtypeRelevant = true;
valueType = Bonus::INDEPENDENT_MAX;
valueTypeRelevant = true;
val = 3;
valRelevant = true;
}
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
{
type = Bonus::HP_REGENERATION;
val = 100000; //very high value to always chose stack health
valRelevant = true;
}
else if (deprecatedTypeStr == "KING1")
{
type = Bonus::KING;
val = 0;
valRelevant = true;
}
else if (deprecatedTypeStr == "KING2")
{
type = Bonus::KING;
val = 2;
valRelevant = true;
}
else if (deprecatedTypeStr == "KING3")
{
type = Bonus::KING;
val = 3;
valRelevant = true;
}
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
type = Bonus::SIGHT_RADIUS;
else if (deprecatedTypeStr == "SELF_MORALE")
{
type = Bonus::MORALE;
val = 1;
valRelevant = true;
valueType = Bonus::INDEPENDENT_MAX;
valueTypeRelevant = true;
}
else if (deprecatedTypeStr == "SELF_LUCK")
{
type = Bonus::LUCK;
val = 1;
valRelevant = true;
valueType = Bonus::INDEPENDENT_MAX;
valueTypeRelevant = true;
}
else
isConverted = false;
}
const JsonNode & BonusParams::toJson()
{
assert(isConverted);
if(ret.isNull())
{
ret["type"].String() = vstd::findKey(bonusNameMap, type);
if(subtypeRelevant && !subtypeStr.empty())
ret["subtype"].String() = subtypeStr;
else if(subtypeRelevant)
ret["subtype"].Integer() = subtype;
if(valueTypeRelevant)
ret["valueType"].String() = vstd::findKey(bonusValueMap, valueType);
if(valRelevant)
ret["val"].Float() = val;
if(targetTypeRelevant)
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetType);
jsonCreated = true;
}
return ret;
};
CSelector BonusParams::toSelector()
{
assert(isConverted);
if(subtypeRelevant && !subtypeStr.empty())
JsonUtils::resolveIdentifier(subtype, toJson(), "subtype");
auto ret = Selector::type()(type);
if(subtypeRelevant)
ret = ret.And(Selector::subtype()(subtype));
if(valueTypeRelevant)
ret = ret.And(Selector::valueType(valueType));
if(targetTypeRelevant)
ret = ret.And(Selector::targetSourceType()(targetType));
return ret;
}
VCMI_LIB_NAMESPACE_END

41
lib/bonuses/BonusParams.h Normal file
View File

@ -0,0 +1,41 @@
/*
* BonusParams.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "HeroBonus.h"
#include "../GameConstants.h"
#include "../JsonNode.h"
VCMI_LIB_NAMESPACE_BEGIN
struct DLL_LINKAGE BonusParams {
bool isConverted;
Bonus::BonusType type = Bonus::NONE;
TBonusSubtype subtype = -1;
std::string subtypeStr;
bool subtypeRelevant = false;
Bonus::ValueType valueType = Bonus::BASE_NUMBER;
bool valueTypeRelevant = false;
si32 val = 0;
bool valRelevant = false;
Bonus::BonusSource targetType = Bonus::SECONDARY_SKILL;
bool targetTypeRelevant = false;
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
const JsonNode & toJson();
CSelector toSelector();
private:
JsonNode ret;
bool jsonCreated = false;
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,689 @@
/*
* CBonusSystemNode.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CBonusSystemNode.h"
#include "Limiters.h"
#include "Updaters.h"
#include "Propagators.h"
VCMI_LIB_NAMESPACE_BEGIN
std::atomic<int64_t> CBonusSystemNode::treeChanged(1);
constexpr bool CBonusSystemNode::cachingEnabled = true;
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
PlayerColor CBonusSystemNode::retrieveNodeOwner(const CBonusSystemNode * node)
{
return node ? node->getOwner() : PlayerColor::CANNOT_DETERMINE;
}
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
{
auto ret = bonuses.getFirst(selector);
if(ret)
return ret;
FOREACH_PARENT(pname)
{
ret = pname->getBonusLocalFirst(selector);
if (ret)
return ret;
}
return nullptr;
}
std::shared_ptr<const Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector) const
{
return (const_cast<CBonusSystemNode*>(this))->getBonusLocalFirst(selector);
}
void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
{
for(const auto & elem : parents)
{
const CBonusSystemNode *parent = elem;
out.insert(parent);
}
}
void CBonusSystemNode::getParents(TNodes &out)
{
for (auto & elem : parents)
{
const CBonusSystemNode *parent = elem;
out.insert(const_cast<CBonusSystemNode*>(parent));
}
}
void CBonusSystemNode::getAllParents(TCNodes & out) const //retrieves list of parent nodes (nodes to inherit bonuses from)
{
for(auto * parent : parents)
{
out.insert(parent);
parent->getAllParents(out);
}
}
void CBonusSystemNode::getAllBonusesRec(BonusList &out, const CSelector & selector) const
{
//out has been reserved sufficient capacity at getAllBonuses() call
BonusList beforeUpdate;
TCNodes lparents;
getAllParents(lparents);
if(!lparents.empty())
{
//estimate on how many bonuses are missing yet - must be positive
beforeUpdate.reserve(std::max(out.capacity() - out.size(), bonuses.size()));
}
else
{
beforeUpdate.reserve(bonuses.size()); //at most all local bonuses
}
for(const auto * parent : lparents)
{
parent->getAllBonusesRec(beforeUpdate, selector);
}
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;
//do not add bonus with updater
bool bonusExists = false;
for(const auto & bonus : out)
{
if (bonus == updated)
bonusExists = true;
if (bonus->updater && bonus->updater == updated->updater)
bonusExists = true;
}
if (!bonusExists)
out.push_back(updated);
}
}
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root, const std::string &cachingStr) const
{
bool limitOnUs = (!root || root == this); //caching won't work when we want to limit bonuses against an external node
if (CBonusSystemNode::cachingEnabled && limitOnUs)
{
// Exclusive access for one thread
boost::lock_guard<boost::mutex> lock(sync);
// If the bonus system tree changes(state of a single node or the relations to each other) then
// cache all bonus objects. Selector objects doesn't matter.
if (cachedLast != treeChanged)
{
BonusList allBonuses;
allBonuses.reserve(cachedBonuses.capacity()); //we assume we'll get about the same number of bonuses
cachedBonuses.clear();
cachedRequests.clear();
getAllBonusesRec(allBonuses, Selector::all);
limitBonuses(allBonuses, cachedBonuses);
cachedBonuses.stackBonuses();
cachedLast = treeChanged;
}
// If a bonus system request comes with a caching string then look up in the map if there are any
// pre-calculated bonus results. Limiters can't be cached so they have to be calculated.
if(!cachingStr.empty())
{
auto it = cachedRequests.find(cachingStr);
if(it != cachedRequests.end())
{
//Cached list contains bonuses for our query with applied limiters
return it->second;
}
}
//We still don't have the bonuses (didn't returned them from cache)
//Perform bonus selection
auto ret = std::make_shared<BonusList>();
cachedBonuses.getBonuses(*ret, selector, limit);
// Save the results in the cache
if(!cachingStr.empty())
cachedRequests[cachingStr] = ret;
return ret;
}
else
{
return getAllBonusesWithoutCaching(selector, limit, root);
}
}
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root) const
{
auto ret = std::make_shared<BonusList>();
// Get bonus results without caching enabled.
BonusList beforeLimiting;
BonusList afterLimiting;
getAllBonusesRec(beforeLimiting, selector);
if(!root || root == this)
{
limitBonuses(beforeLimiting, afterLimiting);
}
else if(root)
{
//We want to limit our query against an external node. We get all its bonuses,
// add the ones we're considering and see if they're cut out by limiters
BonusList rootBonuses;
BonusList limitedRootBonuses;
getAllBonusesRec(rootBonuses, selector);
for(const auto & b : beforeLimiting)
rootBonuses.push_back(b);
root->limitBonuses(rootBonuses, limitedRootBonuses);
for(const auto & b : beforeLimiting)
if(vstd::contains(limitedRootBonuses, b))
afterLimiting.push_back(b);
}
afterLimiting.getBonuses(*ret, selector, limit);
ret->stackBonuses();
return ret;
}
std::shared_ptr<Bonus> CBonusSystemNode::getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const
{
assert(updater);
return updater->createUpdatedBonus(b, * this);
}
CBonusSystemNode::CBonusSystemNode()
:CBonusSystemNode(false)
{
}
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
bonuses(true),
exportedBonuses(true),
nodeType(UNKNOWN),
cachedLast(0),
isHypotheticNode(isHypotetic)
{
}
CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType):
bonuses(true),
exportedBonuses(true),
nodeType(NodeType),
cachedLast(0),
isHypotheticNode(false)
{
}
CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other) noexcept:
bonuses(std::move(other.bonuses)),
exportedBonuses(std::move(other.exportedBonuses)),
nodeType(other.nodeType),
description(other.description),
cachedLast(0),
isHypotheticNode(other.isHypotheticNode)
{
std::swap(parents, other.parents);
std::swap(children, other.children);
//fixing bonus tree without recalculation
if(!isHypothetic())
{
for(CBonusSystemNode * n : parents)
{
n->children -= &other;
n->children.push_back(this);
}
}
for(CBonusSystemNode * n : children)
{
n->parents -= &other;
n->parents.push_back(this);
}
//cache ignored
//cachedBonuses
//cachedRequests
}
CBonusSystemNode::~CBonusSystemNode()
{
detachFromAll();
if(!children.empty())
{
while(!children.empty())
children.front()->detachFrom(*this);
}
}
void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
{
assert(!vstd::contains(parents, &parent));
parents.push_back(&parent);
if(!isHypothetic())
{
if(parent.actsAsBonusSourceOnly())
parent.newRedDescendant(*this);
else
newRedDescendant(parent);
parent.newChildAttached(*this);
}
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
{
assert(vstd::contains(parents, &parent));
if(!isHypothetic())
{
if(parent.actsAsBonusSourceOnly())
parent.removedRedDescendant(*this);
else
removedRedDescendant(parent);
}
if (vstd::contains(parents, &parent))
{
parents -= &parent;
}
else
{
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
}
if(!isHypothetic())
{
parent.childDetached(*this);
}
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::removeBonusesRecursive(const CSelector & s)
{
removeBonuses(s);
for(CBonusSystemNode * child : children)
child->removeBonusesRecursive(s);
}
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
{
BonusList bl;
exportedBonuses.getBonuses(bl, s, Selector::all);
for(const auto & b : bl)
{
b->turnsRemain--;
if(b->turnsRemain <= 0)
removeBonus(b);
}
for(CBonusSystemNode *child : children)
child->reduceBonusDurations(s);
}
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
{
//turnsRemain shouldn't be zero for following durations
if(Bonus::NTurns(b.get()) || Bonus::NDays(b.get()) || Bonus::OneWeek(b.get()))
{
assert(b->turnsRemain);
}
assert(!vstd::contains(exportedBonuses, b));
exportedBonuses.push_back(b);
exportBonus(b);
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::accumulateBonus(const std::shared_ptr<Bonus>& b)
{
auto bonus = exportedBonuses.getFirst(Selector::typeSubtype(b->type, b->subtype)); //only local bonuses are interesting //TODO: what about value type?
if(bonus)
bonus->val += b->val;
else
addNewBonus(std::make_shared<Bonus>(*b)); //duplicate needed, original may get destroyed
}
void CBonusSystemNode::removeBonus(const std::shared_ptr<Bonus>& b)
{
exportedBonuses -= b;
if(b->propagator)
unpropagateBonus(b);
else
bonuses -= b;
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::removeBonuses(const CSelector & selector)
{
BonusList toRemove;
exportedBonuses.getBonuses(toRemove, selector, Selector::all);
for(const auto & bonus : toRemove)
removeBonus(bonus);
}
bool CBonusSystemNode::actsAsBonusSourceOnly() const
{
switch(nodeType)
{
case CREATURE:
case ARTIFACT:
case ARTIFACT_INSTANCE:
return true;
default:
return false;
}
}
void CBonusSystemNode::propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source)
{
if(b->propagator->shouldBeAttached(this))
{
auto propagated = b->propagationUpdater
? source.getUpdatedBonus(b, b->propagationUpdater)
: b;
bonuses.push_back(propagated);
logBonus->trace("#$# %s #propagated to# %s", propagated->Description(), nodeName());
}
FOREACH_RED_CHILD(child)
child->propagateBonus(b, source);
}
void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
{
if(b->propagator->shouldBeAttached(this))
{
bonuses -= b;
logBonus->trace("#$# %s #is no longer propagated to# %s", b->Description(), nodeName());
}
FOREACH_RED_CHILD(child)
child->unpropagateBonus(b);
}
void CBonusSystemNode::newChildAttached(CBonusSystemNode & child)
{
assert(!vstd::contains(children, &child));
children.push_back(&child);
}
void CBonusSystemNode::childDetached(CBonusSystemNode & child)
{
if(vstd::contains(children, &child))
children -= &child;
else
{
logBonus->error("Error on Detach. Node %s (nodeType=%d) is not a child of %s (nodeType=%d)"
, child.nodeShortInfo(), child.nodeType, nodeShortInfo(), nodeType);
}
}
void CBonusSystemNode::detachFromAll()
{
while(!parents.empty())
detachFrom(*parents.front());
}
bool CBonusSystemNode::isIndependentNode() const
{
return parents.empty() && children.empty();
}
std::string CBonusSystemNode::nodeName() const
{
return !description.empty()
? description
: std::string("Bonus system node of type ") + typeid(*this).name();
}
std::string CBonusSystemNode::nodeShortInfo() const
{
std::ostringstream str;
str << "'" << typeid(* this).name() << "'";
description.length() > 0
? str << " (" << description << ")"
: str << " (no description)";
return str.str();
}
void CBonusSystemNode::deserializationFix()
{
exportBonuses();
}
void CBonusSystemNode::getRedParents(TNodes & out)
{
FOREACH_PARENT(pname)
{
if(pname->actsAsBonusSourceOnly())
{
out.insert(pname);
}
}
if(!actsAsBonusSourceOnly())
{
for(CBonusSystemNode *child : children)
{
out.insert(child);
}
}
}
void CBonusSystemNode::getRedChildren(TNodes &out)
{
FOREACH_PARENT(pname)
{
if(!pname->actsAsBonusSourceOnly())
{
out.insert(pname);
}
}
if(actsAsBonusSourceOnly())
{
for(CBonusSystemNode *child : children)
{
out.insert(child);
}
}
}
void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant)
{
for(const auto & b : exportedBonuses)
{
if(b->propagator)
descendant.propagateBonus(b, *this);
}
TNodes redParents;
getRedAncestors(redParents); //get all red parents recursively
for(auto * parent : redParents)
{
for(const auto & b : parent->exportedBonuses)
{
if(b->propagator)
descendant.propagateBonus(b, *this);
}
}
}
void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant)
{
for(const auto & b : exportedBonuses)
if(b->propagator)
descendant.unpropagateBonus(b);
TNodes redParents;
getRedAncestors(redParents); //get all red parents recursively
for(auto * parent : redParents)
{
for(const auto & b : parent->exportedBonuses)
if(b->propagator)
descendant.unpropagateBonus(b);
}
}
void CBonusSystemNode::getRedAncestors(TNodes &out)
{
getRedParents(out);
TNodes redParents;
getRedParents(redParents);
for(CBonusSystemNode * parent : redParents)
parent->getRedAncestors(out);
}
void CBonusSystemNode::exportBonus(const std::shared_ptr<Bonus> & b)
{
if(b->propagator)
propagateBonus(b, *this);
else
bonuses.push_back(b);
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::exportBonuses()
{
for(const auto & b : exportedBonuses)
exportBonus(b);
}
CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
{
return nodeType;
}
const BonusList& CBonusSystemNode::getBonusList() const
{
return bonuses;
}
const TNodesVector& CBonusSystemNode::getParentNodes() const
{
return parents;
}
const TNodesVector& CBonusSystemNode::getChildrenNodes() const
{
return children;
}
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
{
nodeType = type;
}
BonusList & CBonusSystemNode::getExportedBonusList()
{
return exportedBonuses;
}
const BonusList & CBonusSystemNode::getExportedBonusList() const
{
return exportedBonuses;
}
const std::string& CBonusSystemNode::getDescription() const
{
return description;
}
void CBonusSystemNode::setDescription(const std::string &description)
{
this->description = description;
}
void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out) const
{
assert(&allBonuses != &out); //todo should it work in-place?
BonusList undecided = allBonuses;
BonusList & accepted = out;
while(true)
{
int undecidedCount = static_cast<int>(undecided.size());
for(int i = 0; i < undecided.size(); i++)
{
auto b = undecided[i];
BonusLimitationContext context = {*b, *this, out, undecided};
auto decision = b->limiter ? b->limiter->limit(context) : ILimiter::EDecision::ACCEPT; //bonuses without limiters will be accepted by default
if(decision == ILimiter::EDecision::DISCARD)
{
undecided.erase(i);
i--; continue;
}
else if(decision == ILimiter::EDecision::ACCEPT)
{
accepted.push_back(b);
undecided.erase(i);
i--; continue;
}
else
assert(decision == ILimiter::EDecision::NOT_SURE);
}
if(undecided.size() == undecidedCount) //we haven't moved a single bonus -> limiters reached a stable state
return;
}
}
TBonusListPtr CBonusSystemNode::limitBonuses(const BonusList &allBonuses) const
{
auto ret = std::make_shared<BonusList>();
limitBonuses(allBonuses, *ret);
return ret;
}
void CBonusSystemNode::treeHasChanged()
{
treeChanged++;
}
int64_t CBonusSystemNode::getTreeVersion() const
{
return treeChanged;
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,139 @@
/*
* CBonusSystemNode.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "HeroBonus.h"
VCMI_LIB_NAMESPACE_BEGIN
using TNodes = std::set<CBonusSystemNode *>;
using TCNodes = std::set<const CBonusSystemNode *>;
using TNodesVector = std::vector<CBonusSystemNode *>;
class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, 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
};
private:
BonusList bonuses; //wielded bonuses (local or up-propagated here)
BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
TNodesVector parents; //parents -> we inherit bonuses from them, we may attach our bonuses to them
TNodesVector children;
ENodeTypes nodeType;
std::string description;
bool isHypotheticNode;
static const bool cachingEnabled;
mutable BonusList cachedBonuses;
mutable int64_t cachedLast;
static std::atomic<int64_t> treeChanged;
// Setting a value to cachingStr before getting any bonuses caches the result for later requests.
// This string needs to be unique, that's why it has to be setted in the following manner:
// [property key]_[value] => only for selector
mutable std::map<std::string, TBonusListPtr > cachedRequests;
mutable boost::mutex sync;
void getAllBonusesRec(BonusList &out, const CSelector & selector) const;
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
public:
explicit CBonusSystemNode();
explicit CBonusSystemNode(bool isHypotetic);
explicit CBonusSystemNode(ENodeTypes NodeType);
CBonusSystemNode(CBonusSystemNode && other) noexcept;
virtual ~CBonusSystemNode();
void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
TBonusListPtr limitBonuses(const BonusList &allBonuses) const; //same as above, returns out by val for convienence
TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
void getParents(TCNodes &out) const; //retrieves list of parent nodes (nodes to inherit bonuses from),
std::shared_ptr<const Bonus> getBonusLocalFirst(const CSelector & selector) const;
//non-const interface
void getParents(TNodes &out); //retrieves list of parent nodes (nodes to inherit bonuses from)
void getRedParents(TNodes &out); //retrieves list of red parent nodes (nodes bonuses propagate from)
void getRedAncestors(TNodes &out);
void getRedChildren(TNodes &out);
void getAllParents(TCNodes & out) const;
static PlayerColor retrieveNodeOwner(const CBonusSystemNode * node);
std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector & selector);
void attachTo(CBonusSystemNode & parent);
void detachFrom(CBonusSystemNode & parent);
void detachFromAll();
virtual void addNewBonus(const std::shared_ptr<Bonus>& b);
void accumulateBonus(const std::shared_ptr<Bonus>& b); //add value of bonus with same type/subtype or create new
void newChildAttached(CBonusSystemNode & child);
void childDetached(CBonusSystemNode & child);
void propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source);
void unpropagateBonus(const std::shared_ptr<Bonus> & b);
void removeBonus(const std::shared_ptr<Bonus>& b);
void removeBonuses(const CSelector & selector);
void removeBonusesRecursive(const CSelector & s);
void newRedDescendant(CBonusSystemNode & descendant); //propagation needed
void removedRedDescendant(CBonusSystemNode & descendant); //de-propagation needed
bool isIndependentNode() const; //node is independent when it has no parents nor children
bool actsAsBonusSourceOnly() const;
///updates count of remaining turns and removes outdated bonuses by selector
void reduceBonusDurations(const CSelector &s);
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
virtual std::string nodeName() const;
virtual std::string nodeShortInfo() const;
bool isHypothetic() const { return isHypotheticNode; }
void deserializationFix();
void exportBonus(const std::shared_ptr<Bonus> & b);
void exportBonuses();
const BonusList &getBonusList() const;
BonusList & getExportedBonusList();
const BonusList & getExportedBonusList() const;
CBonusSystemNode::ENodeTypes getNodeType() const;
void setNodeType(CBonusSystemNode::ENodeTypes type);
const TNodesVector &getParentNodes() const;
const TNodesVector &getChildrenNodes() const;
const std::string &getDescription() const;
void setDescription(const std::string &description);
static void treeHasChanged();
int64_t getTreeVersion() const override;
virtual PlayerColor getOwner() const
{
return PlayerColor::NEUTRAL;
}
template <typename Handler> void serialize(Handler &h, const int version)
{
// h & bonuses;
h & nodeType;
h & exportedBonuses;
h & description;
BONUS_TREE_DESERIALIZATION_FIX
//h & parents & children;
}
friend class CBonusProxy;
};
VCMI_LIB_NAMESPACE_END

View File

@ -10,8 +10,10 @@
#include "StdInc.h"
#include "HeroBonus.h"
#include "CBonusSystemNode.h"
#include "Limiters.h"
#include "Updaters.h"
#include "Propagators.h"
#include "../VCMI_Lib.h"
#include "../spells/CSpellHandler.h"
@ -30,9 +32,6 @@
VCMI_LIB_NAMESPACE_BEGIN
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
#define BONUS_NAME(x) { #x, Bonus::x },
const std::map<std::string, Bonus::BonusType> bonusNameMap = {
BONUS_LIST
@ -71,16 +70,6 @@ const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect =
BONUS_ITEM(ONLY_MELEE_FIGHT)
};
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
const std::set<std::string> deprecatedBonusSet = {
"SECONDARY_SKILL_PREMY",
"SECONDARY_SKILL_VAL2",
@ -159,9 +148,6 @@ JsonNode CAddInfo::toJsonNode() const
}
}
std::atomic<int64_t> CBonusSystemNode::treeChanged(1);
constexpr bool CBonusSystemNode::cachingEnabled = true;
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
{
@ -477,669 +463,6 @@ std::shared_ptr<const Bonus> IBonusBearer::getBonus(const CSelector &selector) c
return bonuses->getFirst(Selector::all);
}
PlayerColor CBonusSystemNode::retrieveNodeOwner(const CBonusSystemNode * node)
{
return node ? node->getOwner() : PlayerColor::CANNOT_DETERMINE;
}
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
{
auto ret = bonuses.getFirst(selector);
if(ret)
return ret;
FOREACH_PARENT(pname)
{
ret = pname->getBonusLocalFirst(selector);
if (ret)
return ret;
}
return nullptr;
}
std::shared_ptr<const Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector) const
{
return (const_cast<CBonusSystemNode*>(this))->getBonusLocalFirst(selector);
}
void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
{
for(const auto & elem : parents)
{
const CBonusSystemNode *parent = elem;
out.insert(parent);
}
}
void CBonusSystemNode::getParents(TNodes &out)
{
for (auto & elem : parents)
{
const CBonusSystemNode *parent = elem;
out.insert(const_cast<CBonusSystemNode*>(parent));
}
}
void CBonusSystemNode::getAllParents(TCNodes & out) const //retrieves list of parent nodes (nodes to inherit bonuses from)
{
for(auto * parent : parents)
{
out.insert(parent);
parent->getAllParents(out);
}
}
void CBonusSystemNode::getAllBonusesRec(BonusList &out, const CSelector & selector) const
{
//out has been reserved sufficient capacity at getAllBonuses() call
BonusList beforeUpdate;
TCNodes lparents;
getAllParents(lparents);
if(!lparents.empty())
{
//estimate on how many bonuses are missing yet - must be positive
beforeUpdate.reserve(std::max(out.capacity() - out.size(), bonuses.size()));
}
else
{
beforeUpdate.reserve(bonuses.size()); //at most all local bonuses
}
for(const auto * parent : lparents)
{
parent->getAllBonusesRec(beforeUpdate, selector);
}
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;
//do not add bonus with updater
bool bonusExists = false;
for(const auto & bonus : out)
{
if (bonus == updated)
bonusExists = true;
if (bonus->updater && bonus->updater == updated->updater)
bonusExists = true;
}
if (!bonusExists)
out.push_back(updated);
}
}
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root, const std::string &cachingStr) const
{
bool limitOnUs = (!root || root == this); //caching won't work when we want to limit bonuses against an external node
if (CBonusSystemNode::cachingEnabled && limitOnUs)
{
// Exclusive access for one thread
boost::lock_guard<boost::mutex> lock(sync);
// If the bonus system tree changes(state of a single node or the relations to each other) then
// cache all bonus objects. Selector objects doesn't matter.
if (cachedLast != treeChanged)
{
BonusList allBonuses;
allBonuses.reserve(cachedBonuses.capacity()); //we assume we'll get about the same number of bonuses
cachedBonuses.clear();
cachedRequests.clear();
getAllBonusesRec(allBonuses, Selector::all);
limitBonuses(allBonuses, cachedBonuses);
cachedBonuses.stackBonuses();
cachedLast = treeChanged;
}
// If a bonus system request comes with a caching string then look up in the map if there are any
// pre-calculated bonus results. Limiters can't be cached so they have to be calculated.
if(!cachingStr.empty())
{
auto it = cachedRequests.find(cachingStr);
if(it != cachedRequests.end())
{
//Cached list contains bonuses for our query with applied limiters
return it->second;
}
}
//We still don't have the bonuses (didn't returned them from cache)
//Perform bonus selection
auto ret = std::make_shared<BonusList>();
cachedBonuses.getBonuses(*ret, selector, limit);
// Save the results in the cache
if(!cachingStr.empty())
cachedRequests[cachingStr] = ret;
return ret;
}
else
{
return getAllBonusesWithoutCaching(selector, limit, root);
}
}
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root) const
{
auto ret = std::make_shared<BonusList>();
// Get bonus results without caching enabled.
BonusList beforeLimiting;
BonusList afterLimiting;
getAllBonusesRec(beforeLimiting, selector);
if(!root || root == this)
{
limitBonuses(beforeLimiting, afterLimiting);
}
else if(root)
{
//We want to limit our query against an external node. We get all its bonuses,
// add the ones we're considering and see if they're cut out by limiters
BonusList rootBonuses;
BonusList limitedRootBonuses;
getAllBonusesRec(rootBonuses, selector);
for(const auto & b : beforeLimiting)
rootBonuses.push_back(b);
root->limitBonuses(rootBonuses, limitedRootBonuses);
for(const auto & b : beforeLimiting)
if(vstd::contains(limitedRootBonuses, b))
afterLimiting.push_back(b);
}
afterLimiting.getBonuses(*ret, selector, limit);
ret->stackBonuses();
return ret;
}
std::shared_ptr<Bonus> CBonusSystemNode::getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const
{
assert(updater);
return updater->createUpdatedBonus(b, * this);
}
CBonusSystemNode::CBonusSystemNode()
:CBonusSystemNode(false)
{
}
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
bonuses(true),
exportedBonuses(true),
nodeType(UNKNOWN),
cachedLast(0),
isHypotheticNode(isHypotetic)
{
}
CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType):
bonuses(true),
exportedBonuses(true),
nodeType(NodeType),
cachedLast(0),
isHypotheticNode(false)
{
}
CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other) noexcept:
bonuses(std::move(other.bonuses)),
exportedBonuses(std::move(other.exportedBonuses)),
nodeType(other.nodeType),
description(other.description),
cachedLast(0),
isHypotheticNode(other.isHypotheticNode)
{
std::swap(parents, other.parents);
std::swap(children, other.children);
//fixing bonus tree without recalculation
if(!isHypothetic())
{
for(CBonusSystemNode * n : parents)
{
n->children -= &other;
n->children.push_back(this);
}
}
for(CBonusSystemNode * n : children)
{
n->parents -= &other;
n->parents.push_back(this);
}
//cache ignored
//cachedBonuses
//cachedRequests
}
CBonusSystemNode::~CBonusSystemNode()
{
detachFromAll();
if(!children.empty())
{
while(!children.empty())
children.front()->detachFrom(*this);
}
}
void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
{
assert(!vstd::contains(parents, &parent));
parents.push_back(&parent);
if(!isHypothetic())
{
if(parent.actsAsBonusSourceOnly())
parent.newRedDescendant(*this);
else
newRedDescendant(parent);
parent.newChildAttached(*this);
}
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
{
assert(vstd::contains(parents, &parent));
if(!isHypothetic())
{
if(parent.actsAsBonusSourceOnly())
parent.removedRedDescendant(*this);
else
removedRedDescendant(parent);
}
if (vstd::contains(parents, &parent))
{
parents -= &parent;
}
else
{
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
}
if(!isHypothetic())
{
parent.childDetached(*this);
}
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::removeBonusesRecursive(const CSelector & s)
{
removeBonuses(s);
for(CBonusSystemNode * child : children)
child->removeBonusesRecursive(s);
}
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
{
BonusList bl;
exportedBonuses.getBonuses(bl, s, Selector::all);
for(const auto & b : bl)
{
b->turnsRemain--;
if(b->turnsRemain <= 0)
removeBonus(b);
}
for(CBonusSystemNode *child : children)
child->reduceBonusDurations(s);
}
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
{
//turnsRemain shouldn't be zero for following durations
if(Bonus::NTurns(b.get()) || Bonus::NDays(b.get()) || Bonus::OneWeek(b.get()))
{
assert(b->turnsRemain);
}
assert(!vstd::contains(exportedBonuses, b));
exportedBonuses.push_back(b);
exportBonus(b);
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::accumulateBonus(const std::shared_ptr<Bonus>& b)
{
auto bonus = exportedBonuses.getFirst(Selector::typeSubtype(b->type, b->subtype)); //only local bonuses are interesting //TODO: what about value type?
if(bonus)
bonus->val += b->val;
else
addNewBonus(std::make_shared<Bonus>(*b)); //duplicate needed, original may get destroyed
}
void CBonusSystemNode::removeBonus(const std::shared_ptr<Bonus>& b)
{
exportedBonuses -= b;
if(b->propagator)
unpropagateBonus(b);
else
bonuses -= b;
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::removeBonuses(const CSelector & selector)
{
BonusList toRemove;
exportedBonuses.getBonuses(toRemove, selector, Selector::all);
for(const auto & bonus : toRemove)
removeBonus(bonus);
}
bool CBonusSystemNode::actsAsBonusSourceOnly() const
{
switch(nodeType)
{
case CREATURE:
case ARTIFACT:
case ARTIFACT_INSTANCE:
return true;
default:
return false;
}
}
void CBonusSystemNode::propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source)
{
if(b->propagator->shouldBeAttached(this))
{
auto propagated = b->propagationUpdater
? source.getUpdatedBonus(b, b->propagationUpdater)
: b;
bonuses.push_back(propagated);
logBonus->trace("#$# %s #propagated to# %s", propagated->Description(), nodeName());
}
FOREACH_RED_CHILD(child)
child->propagateBonus(b, source);
}
void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
{
if(b->propagator->shouldBeAttached(this))
{
bonuses -= b;
logBonus->trace("#$# %s #is no longer propagated to# %s", b->Description(), nodeName());
}
FOREACH_RED_CHILD(child)
child->unpropagateBonus(b);
}
void CBonusSystemNode::newChildAttached(CBonusSystemNode & child)
{
assert(!vstd::contains(children, &child));
children.push_back(&child);
}
void CBonusSystemNode::childDetached(CBonusSystemNode & child)
{
if(vstd::contains(children, &child))
children -= &child;
else
{
logBonus->error("Error on Detach. Node %s (nodeType=%d) is not a child of %s (nodeType=%d)"
, child.nodeShortInfo(), child.nodeType, nodeShortInfo(), nodeType);
}
}
void CBonusSystemNode::detachFromAll()
{
while(!parents.empty())
detachFrom(*parents.front());
}
bool CBonusSystemNode::isIndependentNode() const
{
return parents.empty() && children.empty();
}
std::string CBonusSystemNode::nodeName() const
{
return !description.empty()
? description
: std::string("Bonus system node of type ") + typeid(*this).name();
}
std::string CBonusSystemNode::nodeShortInfo() const
{
std::ostringstream str;
str << "'" << typeid(* this).name() << "'";
description.length() > 0
? str << " (" << description << ")"
: str << " (no description)";
return str.str();
}
void CBonusSystemNode::deserializationFix()
{
exportBonuses();
}
void CBonusSystemNode::getRedParents(TNodes & out)
{
FOREACH_PARENT(pname)
{
if(pname->actsAsBonusSourceOnly())
{
out.insert(pname);
}
}
if(!actsAsBonusSourceOnly())
{
for(CBonusSystemNode *child : children)
{
out.insert(child);
}
}
}
void CBonusSystemNode::getRedChildren(TNodes &out)
{
FOREACH_PARENT(pname)
{
if(!pname->actsAsBonusSourceOnly())
{
out.insert(pname);
}
}
if(actsAsBonusSourceOnly())
{
for(CBonusSystemNode *child : children)
{
out.insert(child);
}
}
}
void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant)
{
for(const auto & b : exportedBonuses)
{
if(b->propagator)
descendant.propagateBonus(b, *this);
}
TNodes redParents;
getRedAncestors(redParents); //get all red parents recursively
for(auto * parent : redParents)
{
for(const auto & b : parent->exportedBonuses)
{
if(b->propagator)
descendant.propagateBonus(b, *this);
}
}
}
void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant)
{
for(const auto & b : exportedBonuses)
if(b->propagator)
descendant.unpropagateBonus(b);
TNodes redParents;
getRedAncestors(redParents); //get all red parents recursively
for(auto * parent : redParents)
{
for(const auto & b : parent->exportedBonuses)
if(b->propagator)
descendant.unpropagateBonus(b);
}
}
void CBonusSystemNode::getRedAncestors(TNodes &out)
{
getRedParents(out);
TNodes redParents;
getRedParents(redParents);
for(CBonusSystemNode * parent : redParents)
parent->getRedAncestors(out);
}
void CBonusSystemNode::exportBonus(const std::shared_ptr<Bonus> & b)
{
if(b->propagator)
propagateBonus(b, *this);
else
bonuses.push_back(b);
CBonusSystemNode::treeHasChanged();
}
void CBonusSystemNode::exportBonuses()
{
for(const auto & b : exportedBonuses)
exportBonus(b);
}
CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
{
return nodeType;
}
const BonusList& CBonusSystemNode::getBonusList() const
{
return bonuses;
}
const TNodesVector& CBonusSystemNode::getParentNodes() const
{
return parents;
}
const TNodesVector& CBonusSystemNode::getChildrenNodes() const
{
return children;
}
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
{
nodeType = type;
}
BonusList & CBonusSystemNode::getExportedBonusList()
{
return exportedBonuses;
}
const BonusList & CBonusSystemNode::getExportedBonusList() const
{
return exportedBonuses;
}
const std::string& CBonusSystemNode::getDescription() const
{
return description;
}
void CBonusSystemNode::setDescription(const std::string &description)
{
this->description = description;
}
void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out) const
{
assert(&allBonuses != &out); //todo should it work in-place?
BonusList undecided = allBonuses;
BonusList & accepted = out;
while(true)
{
int undecidedCount = static_cast<int>(undecided.size());
for(int i = 0; i < undecided.size(); i++)
{
auto b = undecided[i];
BonusLimitationContext context = {*b, *this, out, undecided};
auto decision = b->limiter ? b->limiter->limit(context) : ILimiter::EDecision::ACCEPT; //bonuses without limiters will be accepted by default
if(decision == ILimiter::EDecision::DISCARD)
{
undecided.erase(i);
i--; continue;
}
else if(decision == ILimiter::EDecision::ACCEPT)
{
accepted.push_back(b);
undecided.erase(i);
i--; continue;
}
else
assert(decision == ILimiter::EDecision::NOT_SURE);
}
if(undecided.size() == undecidedCount) //we haven't moved a single bonus -> limiters reached a stable state
return;
}
}
TBonusListPtr CBonusSystemNode::limitBonuses(const BonusList &allBonuses) const
{
auto ret = std::make_shared<BonusList>();
limitBonuses(allBonuses, *ret);
return ret;
}
void CBonusSystemNode::treeHasChanged()
{
treeChanged++;
}
int64_t CBonusSystemNode::getTreeVersion() const
{
return treeChanged;
}
std::string Bonus::Description(std::optional<si32> customValue) const
{
std::ostringstream str;
@ -1314,246 +637,6 @@ std::string Bonus::nameForBonus() const
}
}
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
isConverted(true)
{
if(deprecatedTypeStr == "SECONDARY_SKILL_PREMY" || deprecatedTypeStr == "SPECIAL_SECONDARY_SKILL")
{
if(deprecatedSubtype == SecondarySkill::PATHFINDING || deprecatedSubtypeStr == "skill.pathfinding")
type = Bonus::ROUGH_TERRAIN_DISCOUNT;
else if(deprecatedSubtype == SecondarySkill::DIPLOMACY || deprecatedSubtypeStr == "skill.diplomacy")
type = Bonus::WANDERING_CREATURES_JOIN_BONUS;
else if(deprecatedSubtype == SecondarySkill::WISDOM || deprecatedSubtypeStr == "skill.wisdom")
type = Bonus::MAX_LEARNABLE_SPELL_LEVEL;
else if(deprecatedSubtype == SecondarySkill::MYSTICISM || deprecatedSubtypeStr == "skill.mysticism")
type = Bonus::MANA_REGENERATION;
else if(deprecatedSubtype == SecondarySkill::NECROMANCY || deprecatedSubtypeStr == "skill.necromancy")
type = Bonus::UNDEAD_RAISE_PERCENTAGE;
else if(deprecatedSubtype == SecondarySkill::LEARNING || deprecatedSubtypeStr == "skill.learning")
type = Bonus::HERO_EXPERIENCE_GAIN_PERCENT;
else if(deprecatedSubtype == SecondarySkill::RESISTANCE || deprecatedSubtypeStr == "skill.resistance")
type = Bonus::MAGIC_RESISTANCE;
else if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
type = Bonus::LEARN_BATTLE_SPELL_CHANCE;
else if(deprecatedSubtype == SecondarySkill::SCOUTING || deprecatedSubtypeStr == "skill.scouting")
type = Bonus::SIGHT_RADIUS;
else if(deprecatedSubtype == SecondarySkill::INTELLIGENCE || deprecatedSubtypeStr == "skill.intelligence")
{
type = Bonus::MANA_PER_KNOWLEDGE;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
}
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
type = Bonus::SPELL_DAMAGE;
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
type = Bonus::LEARN_MEETING_SPELL_LIMIT;
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
{
subtype = 1;
subtypeRelevant = true;
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
}
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
{
subtype = 0;
subtypeRelevant = true;
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
}
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
{
subtype = -1;
subtypeRelevant = true;
type = Bonus::GENERAL_DAMAGE_REDUCTION;
}
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
{
subtype = 0;
subtypeRelevant = true;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
{
subtype = 1;
subtypeRelevant = true;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
{
type = Bonus::GENERATE_RESOURCE;
subtype = GameResID(EGameResID::GOLD);
subtypeRelevant = true;
}
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 4;
}
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 1;
}
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 2;
}
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 8;
}
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
{
type = Bonus::BONUS_DAMAGE_CHANCE;
subtypeRelevant = true;
subtypeStr = "core:creature.ballista";
}
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
{
type = Bonus::SPECIFIC_SPELL_POWER;
subtypeRelevant = true;
subtypeStr = "core:spell.firstAid";
}
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
{
type = Bonus::CATAPULT_EXTRA_SHOTS;
subtypeRelevant = true;
subtypeStr = "core:spell.catapultShot";
}
else
isConverted = false;
}
else if (deprecatedTypeStr == "SECONDARY_SKILL_VAL2")
{
if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
type = Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT;
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
{
type = Bonus::HERO_GRANTS_ATTACKS;
subtypeRelevant = true;
subtypeStr = "core:creature.ballista";
}
else
isConverted = false;
}
else if (deprecatedTypeStr == "SEA_MOVEMENT")
{
subtype = 0;
subtypeRelevant = true;
valueType = Bonus::ADDITIVE_VALUE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if (deprecatedTypeStr == "LAND_MOVEMENT")
{
subtype = 1;
subtypeRelevant = true;
valueType = Bonus::ADDITIVE_VALUE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if (deprecatedTypeStr == "MAXED_SPELL")
{
type = Bonus::SPELL;
subtypeStr = deprecatedSubtypeStr;
subtypeRelevant = true;
valueType = Bonus::INDEPENDENT_MAX;
valueTypeRelevant = true;
val = 3;
valRelevant = true;
}
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
{
type = Bonus::HP_REGENERATION;
val = 100000; //very high value to always chose stack health
valRelevant = true;
}
else if (deprecatedTypeStr == "KING1")
{
type = Bonus::KING;
val = 0;
valRelevant = true;
}
else if (deprecatedTypeStr == "KING2")
{
type = Bonus::KING;
val = 2;
valRelevant = true;
}
else if (deprecatedTypeStr == "KING3")
{
type = Bonus::KING;
val = 3;
valRelevant = true;
}
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
type = Bonus::SIGHT_RADIUS;
else if (deprecatedTypeStr == "SELF_MORALE")
{
type = Bonus::MORALE;
val = 1;
valRelevant = true;
valueType = Bonus::INDEPENDENT_MAX;
valueTypeRelevant = true;
}
else if (deprecatedTypeStr == "SELF_LUCK")
{
type = Bonus::LUCK;
val = 1;
valRelevant = true;
valueType = Bonus::INDEPENDENT_MAX;
valueTypeRelevant = true;
}
else
isConverted = false;
}
const JsonNode & BonusParams::toJson()
{
assert(isConverted);
if(ret.isNull())
{
ret["type"].String() = vstd::findKey(bonusNameMap, type);
if(subtypeRelevant && !subtypeStr.empty())
ret["subtype"].String() = subtypeStr;
else if(subtypeRelevant)
ret["subtype"].Integer() = subtype;
if(valueTypeRelevant)
ret["valueType"].String() = vstd::findKey(bonusValueMap, valueType);
if(valRelevant)
ret["val"].Float() = val;
if(targetTypeRelevant)
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetType);
jsonCreated = true;
}
return ret;
};
CSelector BonusParams::toSelector()
{
assert(isConverted);
if(subtypeRelevant && !subtypeStr.empty())
JsonUtils::resolveIdentifier(subtype, toJson(), "subtype");
auto ret = Selector::type()(type);
if(subtypeRelevant)
ret = ret.And(Selector::subtype()(subtype));
if(valueTypeRelevant)
ret = ret.And(Selector::valueType(valueType));
if(targetTypeRelevant)
ret = ret.And(Selector::targetSourceType()(targetType));
return ret;
}
Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
duration(static_cast<ui16>(Duration)),
type(Type),
@ -1722,31 +805,6 @@ std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
return this->shared_from_this();
}
bool IPropagator::shouldBeAttached(CBonusSystemNode *dest)
{
return false;
}
CBonusSystemNode::ENodeTypes IPropagator::getPropagatorType() const
{
return CBonusSystemNode::ENodeTypes::NONE;
}
CPropagatorNodeType::CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType)
: nodeType(NodeType)
{
}
CBonusSystemNode::ENodeTypes CPropagatorNodeType::getPropagatorType() const
{
return nodeType;
}
bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
{
return nodeType == dest->getNodeType();
}
// Updaters
std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)

View File

@ -27,9 +27,6 @@ using TConstBonusListPtr = std::shared_ptr<const BonusList>;
using TLimiterPtr = std::shared_ptr<ILimiter>;
using TPropagatorPtr = std::shared_ptr<IPropagator>;
using TUpdaterPtr = std::shared_ptr<IUpdater>;
using TNodes = std::set<CBonusSystemNode *>;
using TCNodes = std::set<const CBonusSystemNode *>;
using TNodesVector = std::vector<CBonusSystemNode *>;
class CSelector : std::function<bool(const Bonus*)>
{
@ -469,28 +466,6 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
struct DLL_LINKAGE BonusParams {
bool isConverted;
Bonus::BonusType type = Bonus::NONE;
TBonusSubtype subtype = -1;
std::string subtypeStr;
bool subtypeRelevant = false;
Bonus::ValueType valueType = Bonus::BASE_NUMBER;
bool valueTypeRelevant = false;
si32 val = 0;
bool valRelevant = false;
Bonus::BonusSource targetType = Bonus::SECONDARY_SKILL;
bool targetTypeRelevant = false;
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
const JsonNode & toJson();
CSelector toSelector();
private:
JsonNode ret;
bool jsonCreated = false;
};
class DLL_LINKAGE BonusList
{
public:
@ -614,151 +589,6 @@ public:
virtual int64_t getTreeVersion() const = 0;
};
class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, 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
};
private:
BonusList bonuses; //wielded bonuses (local or up-propagated here)
BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
TNodesVector parents; //parents -> we inherit bonuses from them, we may attach our bonuses to them
TNodesVector children;
ENodeTypes nodeType;
std::string description;
bool isHypotheticNode;
static const bool cachingEnabled;
mutable BonusList cachedBonuses;
mutable int64_t cachedLast;
static std::atomic<int64_t> treeChanged;
// Setting a value to cachingStr before getting any bonuses caches the result for later requests.
// This string needs to be unique, that's why it has to be setted in the following manner:
// [property key]_[value] => only for selector
mutable std::map<std::string, TBonusListPtr > cachedRequests;
mutable boost::mutex sync;
void getAllBonusesRec(BonusList &out, const CSelector & selector) const;
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
public:
explicit CBonusSystemNode();
explicit CBonusSystemNode(bool isHypotetic);
explicit CBonusSystemNode(ENodeTypes NodeType);
CBonusSystemNode(CBonusSystemNode && other) noexcept;
virtual ~CBonusSystemNode();
void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
TBonusListPtr limitBonuses(const BonusList &allBonuses) const; //same as above, returns out by val for convienence
TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
void getParents(TCNodes &out) const; //retrieves list of parent nodes (nodes to inherit bonuses from),
std::shared_ptr<const Bonus> getBonusLocalFirst(const CSelector & selector) const;
//non-const interface
void getParents(TNodes &out); //retrieves list of parent nodes (nodes to inherit bonuses from)
void getRedParents(TNodes &out); //retrieves list of red parent nodes (nodes bonuses propagate from)
void getRedAncestors(TNodes &out);
void getRedChildren(TNodes &out);
void getAllParents(TCNodes & out) const;
static PlayerColor retrieveNodeOwner(const CBonusSystemNode * node);
std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector & selector);
void attachTo(CBonusSystemNode & parent);
void detachFrom(CBonusSystemNode & parent);
void detachFromAll();
virtual void addNewBonus(const std::shared_ptr<Bonus>& b);
void accumulateBonus(const std::shared_ptr<Bonus>& b); //add value of bonus with same type/subtype or create new
void newChildAttached(CBonusSystemNode & child);
void childDetached(CBonusSystemNode & child);
void propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source);
void unpropagateBonus(const std::shared_ptr<Bonus> & b);
void removeBonus(const std::shared_ptr<Bonus>& b);
void removeBonuses(const CSelector & selector);
void removeBonusesRecursive(const CSelector & s);
void newRedDescendant(CBonusSystemNode & descendant); //propagation needed
void removedRedDescendant(CBonusSystemNode & descendant); //de-propagation needed
bool isIndependentNode() const; //node is independent when it has no parents nor children
bool actsAsBonusSourceOnly() const;
///updates count of remaining turns and removes outdated bonuses by selector
void reduceBonusDurations(const CSelector &s);
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
virtual std::string nodeName() const;
virtual std::string nodeShortInfo() const;
bool isHypothetic() const { return isHypotheticNode; }
void deserializationFix();
void exportBonus(const std::shared_ptr<Bonus> & b);
void exportBonuses();
const BonusList &getBonusList() const;
BonusList & getExportedBonusList();
const BonusList & getExportedBonusList() const;
CBonusSystemNode::ENodeTypes getNodeType() const;
void setNodeType(CBonusSystemNode::ENodeTypes type);
const TNodesVector &getParentNodes() const;
const TNodesVector &getChildrenNodes() const;
const std::string &getDescription() const;
void setDescription(const std::string &description);
static void treeHasChanged();
int64_t getTreeVersion() const override;
virtual PlayerColor getOwner() const
{
return PlayerColor::NEUTRAL;
}
template <typename Handler> void serialize(Handler &h, const int version)
{
// h & bonuses;
h & nodeType;
h & exportedBonuses;
h & description;
BONUS_TREE_DESERIALIZATION_FIX
//h & parents & children;
}
friend class CBonusProxy;
};
class DLL_LINKAGE IPropagator
{
public:
virtual ~IPropagator() = default;
virtual bool shouldBeAttached(CBonusSystemNode *dest);
virtual CBonusSystemNode::ENodeTypes getPropagatorType() const;
template <typename Handler> void serialize(Handler &h, const int version)
{}
};
class DLL_LINKAGE CPropagatorNodeType : public IPropagator
{
CBonusSystemNode::ENodeTypes nodeType;
public:
CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType = CBonusSystemNode::ENodeTypes::UNKNOWN);
bool shouldBeAttached(CBonusSystemNode *dest) override;
CBonusSystemNode::ENodeTypes getPropagatorType() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & nodeType;
}
};
template<typename T>
class CSelectFieldEqual
{
@ -880,7 +710,6 @@ extern DLL_LINKAGE const std::map<std::string, Bonus::ValueType> bonusValueMap;
extern DLL_LINKAGE const std::map<std::string, Bonus::BonusSource> bonusSourceMap;
extern DLL_LINKAGE const std::map<std::string, ui16> bonusDurationMap;
extern DLL_LINKAGE const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect;
extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
// BonusList template that requires full interface of CBonusSystemNode

View File

@ -0,0 +1,52 @@
/*
* Propagators.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "Propagators.h"
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
bool IPropagator::shouldBeAttached(CBonusSystemNode *dest)
{
return false;
}
CBonusSystemNode::ENodeTypes IPropagator::getPropagatorType() const
{
return CBonusSystemNode::ENodeTypes::NONE;
}
CPropagatorNodeType::CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType)
: nodeType(NodeType)
{
}
CBonusSystemNode::ENodeTypes CPropagatorNodeType::getPropagatorType() const
{
return nodeType;
}
bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
{
return nodeType == dest->getNodeType();
}
VCMI_LIB_NAMESPACE_END

45
lib/bonuses/Propagators.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Propagators.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "HeroBonus.h"
#include "CBonusSystemNode.h"
VCMI_LIB_NAMESPACE_BEGIN
extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
class DLL_LINKAGE IPropagator
{
public:
virtual ~IPropagator() = default;
virtual bool shouldBeAttached(CBonusSystemNode *dest);
virtual CBonusSystemNode::ENodeTypes getPropagatorType() const;
template <typename Handler> void serialize(Handler &h, const int version)
{}
};
class DLL_LINKAGE CPropagatorNodeType : public IPropagator
{
CBonusSystemNode::ENodeTypes nodeType;
public:
CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType = CBonusSystemNode::ENodeTypes::UNKNOWN);
bool shouldBeAttached(CBonusSystemNode *dest) override;
CBonusSystemNode::ENodeTypes getPropagatorType() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & nodeType;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -12,6 +12,7 @@
#include "CObjectHandler.h"
#include "../CCreatureSet.h"
#include "../bonuses/CBonusProxy.h"
#include "../bonuses/CBonusSystemNode.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -22,8 +22,10 @@
#include "../mapObjects/CommonConstructors.h"
#include "../mapObjects/MapObjects.h"
#include "../battle/CObstacleInstance.h"
#include "../bonuses/CBonusSystemNode.h"
#include "../bonuses/Limiters.h"
#include "../bonuses/Updaters.h"
#include "../bonuses/Propagators.h"
#include "../CStack.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -12,6 +12,7 @@
#include "../JsonNode.h"
#include "../bonuses/CBonusSystemNode.h"
#include "../bonuses/HeroBonus.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -15,6 +15,7 @@
#include "../CBonusTypeHandler.h"
#include "../battle/CBattleInfoCallback.h"
#include "../battle/Unit.h"
#include "../bonuses/BonusParams.h"
#include "../serializer/JsonSerializeFormat.h"
#include "../VCMI_Lib.h"