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:
parent
713e3004df
commit
1d34c73c2d
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <vcmi/ArtifactService.h>
|
||||
|
||||
#include "bonuses/HeroBonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "GameConstants.h"
|
||||
#include "IHandlerBase.h"
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "bonuses/HeroBonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "ConstTransitivePtr.h"
|
||||
#include "ResourceSet.h"
|
||||
#include "GameConstants.h"
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "bonuses/HeroBonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "GameConstants.h"
|
||||
#include "CArtHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <vcmi/Team.h>
|
||||
|
||||
#include "bonuses/HeroBonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "ResourceSet.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
259
lib/bonuses/BonusParams.cpp
Normal 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
41
lib/bonuses/BonusParams.h
Normal 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
|
689
lib/bonuses/CBonusSystemNode.cpp
Normal file
689
lib/bonuses/CBonusSystemNode.cpp
Normal 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
|
139
lib/bonuses/CBonusSystemNode.h
Normal file
139
lib/bonuses/CBonusSystemNode.h
Normal 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
|
@ -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)
|
||||
|
@ -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
|
||||
|
52
lib/bonuses/Propagators.cpp
Normal file
52
lib/bonuses/Propagators.cpp
Normal 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
45
lib/bonuses/Propagators.h
Normal 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
|
@ -12,6 +12,7 @@
|
||||
#include "CObjectHandler.h"
|
||||
#include "../CCreatureSet.h"
|
||||
#include "../bonuses/CBonusProxy.h"
|
||||
#include "../bonuses/CBonusSystemNode.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -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
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "../JsonNode.h"
|
||||
|
||||
#include "../bonuses/CBonusSystemNode.h"
|
||||
#include "../bonuses/HeroBonus.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user