1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

vcmi: move limiters outside of HeroBonus.cpp

This will help for recompilation.
This commit is contained in:
Konstantin
2023-04-28 00:47:29 +03:00
parent 6fa1b2b19f
commit 416faf521e
14 changed files with 834 additions and 774 deletions

View File

@@ -22,6 +22,7 @@
#include "../../lib/CGameState.h"
#include "../../lib/NetPacksBase.h"
#include "../../lib/NetPacks.h"
#include "../../lib/bonuses/ILimiter.h"
#include "../../lib/serializer/CTypeList.h"
#include "../../lib/serializer/BinarySerializer.h"
#include "../../lib/serializer/BinaryDeserializer.h"

View File

@@ -43,6 +43,7 @@
#include "../lib/CArtHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/bonuses/ILimiter.h"
#include "../lib/serializer/CTypeList.h"
#include "../lib/serializer/BinaryDeserializer.h"
#include "../lib/serializer/BinarySerializer.h"

View File

@@ -28,6 +28,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/battle/Unit.cpp
${MAIN_LIB_DIR}/bonuses/HeroBonus.cpp
${MAIN_LIB_DIR}/bonuses/ILimiter.cpp
${MAIN_LIB_DIR}/events/ApplyDamage.cpp
${MAIN_LIB_DIR}/events/GameResumed.cpp
@@ -302,6 +303,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/battle/Unit.h
${MAIN_LIB_DIR}/bonuses/HeroBonus.h
${MAIN_LIB_DIR}/bonuses/ILimiter.h
${MAIN_LIB_DIR}/events/ApplyDamage.h
${MAIN_LIB_DIR}/events/GameResumed.h

View File

@@ -19,6 +19,7 @@
#include "CModHandler.h"
#include "GameSettings.h"
#include "StringConstants.h"
#include "bonuses/ILimiter.h"
#include "serializer/JsonDeserializer.h"
#include "serializer/JsonUpdater.h"
#include "mapObjects/CObjectClassesHandler.h"

View File

@@ -24,6 +24,7 @@
#include "CSkillHandler.h"
#include "mapObjects/CObjectClassesHandler.h"
#include "BattleFieldHandler.h"
#include "bonuses/ILimiter.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@@ -18,6 +18,7 @@
#include "CModHandler.h"
#include "BattleFieldHandler.h"
#include "ObstacleHandler.h"
#include "bonuses/ILimiter.h"
#include "serializer/CSerializer.h" // for SAVEGAME_MAGIC
#include "serializer/BinaryDeserializer.h"

View File

@@ -14,6 +14,7 @@
#include "ScopeGuard.h"
#include "bonuses/HeroBonus.h"
#include "bonuses/ILimiter.h"
#include "filesystem/Filesystem.h"
#include "VCMI_Lib.h" //for identifier resolution
#include "CModHandler.h"

View File

@@ -9,6 +9,7 @@
*/
#include "StdInc.h"
#include "BattleInfo.h"
#include "../bonuses/ILimiter.h"
#include "../CStack.h"
#include "../CHeroHandler.h"
#include "../NetPacks.h"

View File

@@ -10,21 +10,22 @@
#include "StdInc.h"
#include "HeroBonus.h"
#include "ILimiter.h"
#include "VCMI_Lib.h"
#include "spells/CSpellHandler.h"
#include "CCreatureHandler.h"
#include "CCreatureSet.h"
#include "CHeroHandler.h"
#include "CTownHandler.h"
#include "CGeneralTextHandler.h"
#include "CSkillHandler.h"
#include "CStack.h"
#include "CArtHandler.h"
#include "CModHandler.h"
#include "TerrainHandler.h"
#include "StringConstants.h"
#include "battle/BattleInfo.h"
#include "../VCMI_Lib.h"
#include "../spells/CSpellHandler.h"
#include "../CCreatureHandler.h"
#include "../CCreatureSet.h"
#include "../CHeroHandler.h"
#include "../CTownHandler.h"
#include "../CGeneralTextHandler.h"
#include "../CSkillHandler.h"
#include "../CStack.h"
#include "../CArtHandler.h"
#include "../CModHandler.h"
#include "../TerrainHandler.h"
#include "../StringConstants.h"
#include "../battle/BattleInfo.h"
VCMI_LIB_NAMESPACE_BEGIN
@@ -69,18 +70,6 @@ const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect =
BONUS_ITEM(ONLY_MELEE_FIGHT)
};
const std::map<std::string, TLimiterPtr> bonusLimiterMap =
{
{"SHOOTER_ONLY", std::make_shared<HasAnotherBonusLimiter>(Bonus::SHOOTER)},
{"DRAGON_NATURE", std::make_shared<HasAnotherBonusLimiter>(Bonus::DRAGON_NATURE)},
{"IS_UNDEAD", std::make_shared<HasAnotherBonusLimiter>(Bonus::UNDEAD)},
{"CREATURE_NATIVE_TERRAIN", std::make_shared<CreatureTerrainLimiter>()},
{"CREATURE_FACTION", std::make_shared<AllOfLimiter>(std::initializer_list<TLimiterPtr>{std::make_shared<CreatureLevelLimiter>(), std::make_shared<FactionLimiter>()})},
{"SAME_FACTION", std::make_shared<FactionLimiter>()},
{"CREATURES_ONLY", std::make_shared<CreatureLevelLimiter>()},
{"OPPOSITE_SIDE", std::make_shared<OppositeSideLimiter>()},
};
const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
{
{"BATTLE_WIDE", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE)},
@@ -750,30 +739,6 @@ std::shared_ptr<const Bonus> IBonusBearer::getBonus(const CSelector &selector) c
return bonuses->getFirst(Selector::all);
}
const CStack * retrieveStackBattle(const CBonusSystemNode * node)
{
switch(node->getNodeType())
{
case CBonusSystemNode::STACK_BATTLE:
return dynamic_cast<const CStack *>(node);
default:
return nullptr;
}
}
const CStackInstance * retrieveStackInstance(const CBonusSystemNode * node)
{
switch(node->getNodeType())
{
case CBonusSystemNode::STACK_INSTANCE:
return (dynamic_cast<const CStackInstance *>(node));
case CBonusSystemNode::STACK_BATTLE:
return (dynamic_cast<const CStack *>(node))->base;
default:
return nullptr;
}
}
PlayerColor CBonusSystemNode::retrieveNodeOwner(const CBonusSystemNode * node)
{
return node ? node->getOwner() : PlayerColor::CANNOT_DETERMINE;
@@ -1398,7 +1363,7 @@ void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out)
for(int i = 0; i < undecided.size(); i++)
{
auto b = undecided[i];
BonusLimitationContext context = {b, *this, out, undecided};
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)
{
@@ -1957,22 +1922,6 @@ namespace Selector
DLL_LINKAGE CSelector none([](const Bonus * b){return false;});
}
const CCreature * retrieveCreature(const CBonusSystemNode *node)
{
switch(node->getNodeType())
{
case CBonusSystemNode::CREATURE:
return (dynamic_cast<const CCreature *>(node));
case CBonusSystemNode::STACK_BATTLE:
return (dynamic_cast<const CStack *>(node))->unitType();
default:
const CStackInstance * csi = retrieveStackInstance(node);
if(csi)
return csi->type;
return nullptr;
}
}
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const BonusList &bonusList)
{
for (ui32 i = 0; i < bonusList.size(); i++)
@@ -2035,168 +1984,6 @@ std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
return this->shared_from_this();
}
ILimiter::EDecision ILimiter::limit(const BonusLimitationContext &context) const /*return true to drop the bonus */
{
return ILimiter::EDecision::ACCEPT;
}
std::string ILimiter::toString() const
{
return typeid(*this).name();
}
JsonNode ILimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = toString();
return root;
}
ILimiter::EDecision CCreatureTypeLimiter::limit(const BonusLimitationContext &context) const
{
const CCreature *c = retrieveCreature(&context.node);
if(!c)
return ILimiter::EDecision::DISCARD;
auto accept = c->getId() == creature->getId() || (includeUpgrades && creature->isMyUpgrade(c));
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
//drop bonus if it's not our creature and (we don`t check upgrades or its not our upgrade)
}
CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades)
: creature(&creature_), includeUpgrades(IncludeUpgrades)
{
}
void CCreatureTypeLimiter::setCreature(const CreatureID & id)
{
creature = VLC->creh->objects[id];
}
std::string CCreatureTypeLimiter::toString() const
{
boost::format fmt("CCreatureTypeLimiter(creature=%s, includeUpgrades=%s)");
fmt % creature->getJsonKey() % (includeUpgrades ? "true" : "false");
return fmt.str();
}
JsonNode CCreatureTypeLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_TYPE_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(creature->getJsonKey()));
root["parameters"].Vector().push_back(JsonUtils::boolNode(includeUpgrades));
return root;
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter( Bonus::BonusType bonus )
: type(bonus), subtype(0), isSubtypeRelevant(false), isSourceRelevant(false), isSourceIDRelevant(false)
{
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter( Bonus::BonusType bonus, TBonusSubtype _subtype )
: type(bonus), subtype(_subtype), isSubtypeRelevant(true), isSourceRelevant(false), isSourceIDRelevant(false)
{
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter(Bonus::BonusType bonus, Bonus::BonusSource src)
: type(bonus), source(src), isSubtypeRelevant(false), isSourceRelevant(true), isSourceIDRelevant(false)
{
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype, Bonus::BonusSource src)
: type(bonus), subtype(_subtype), isSubtypeRelevant(true), source(src), isSourceRelevant(true), isSourceIDRelevant(false)
{
}
ILimiter::EDecision HasAnotherBonusLimiter::limit(const BonusLimitationContext &context) const
{
//TODO: proper selector config with parsing of JSON
auto mySelector = Selector::type()(type);
if(isSubtypeRelevant)
mySelector = mySelector.And(Selector::subtype()(subtype));
if(isSourceRelevant && isSourceIDRelevant)
mySelector = mySelector.And(Selector::source(source, sid));
else if (isSourceRelevant)
mySelector = mySelector.And(Selector::sourceTypeSel(source));
//if we have a bonus of required type accepted, limiter should accept also this bonus
if(context.alreadyAccepted.getFirst(mySelector))
return ILimiter::EDecision::ACCEPT;
//if there are no matching bonuses pending, we can (and must) reject right away
if(!context.stillUndecided.getFirst(mySelector))
return ILimiter::EDecision::DISCARD;
//do not accept for now but it may change if more bonuses gets included
return ILimiter::EDecision::NOT_SURE;
}
std::string HasAnotherBonusLimiter::toString() const
{
std::string typeName = vstd::findKey(bonusNameMap, type);
if(isSubtypeRelevant)
{
boost::format fmt("HasAnotherBonusLimiter(type=%s, subtype=%d)");
fmt % typeName % subtype;
return fmt.str();
}
else
{
boost::format fmt("HasAnotherBonusLimiter(type=%s)");
fmt % typeName;
return fmt.str();
}
}
JsonNode HasAnotherBonusLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
std::string typeName = vstd::findKey(bonusNameMap, type);
auto sourceTypeName = vstd::findKey(bonusSourceMap, source);
root["type"].String() = "HAS_ANOTHER_BONUS_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(typeName));
if(isSubtypeRelevant)
root["parameters"].Vector().push_back(JsonUtils::intNode(subtype));
if(isSourceRelevant)
root["parameters"].Vector().push_back(JsonUtils::stringNode(sourceTypeName));
return root;
}
ILimiter::EDecision UnitOnHexLimiter::limit(const BonusLimitationContext &context) const
{
const auto * stack = retrieveStackBattle(&context.node);
if(!stack)
return ILimiter::EDecision::DISCARD;
auto accept = false;
for (const auto & hex : stack->getHexes())
accept |= !!applicableHexes.count(hex);
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
}
UnitOnHexLimiter::UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes):
applicableHexes(applicableHexes)
{
}
JsonNode UnitOnHexLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "UNIT_ON_HEXES";
for(const auto & hex : applicableHexes)
root["parameters"].Vector().push_back(JsonUtils::intNode(hex));
return root;
}
bool IPropagator::shouldBeAttached(CBonusSystemNode *dest)
{
return false;
@@ -2222,302 +2009,6 @@ bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
return nodeType == dest->getNodeType();
}
CreatureTerrainLimiter::CreatureTerrainLimiter()
: terrainType(ETerrainId::NATIVE_TERRAIN)
{
}
CreatureTerrainLimiter::CreatureTerrainLimiter(TerrainId terrain):
terrainType(terrain)
{
}
ILimiter::EDecision CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const
{
const CStack *stack = retrieveStackBattle(&context.node);
if(stack)
{
if (terrainType == ETerrainId::NATIVE_TERRAIN && stack->isOnNativeTerrain())//terrainType not specified = native
return ILimiter::EDecision::ACCEPT;
if(terrainType != ETerrainId::NATIVE_TERRAIN && stack->isOnTerrain(terrainType))
return ILimiter::EDecision::ACCEPT;
}
return ILimiter::EDecision::DISCARD;
//TODO neutral creatues
}
std::string CreatureTerrainLimiter::toString() const
{
boost::format fmt("CreatureTerrainLimiter(terrainType=%s)");
auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->getJsonKey();
fmt % (terrainType == ETerrainId::NATIVE_TERRAIN ? "native" : terrainName);
return fmt.str();
}
JsonNode CreatureTerrainLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_TERRAIN_LIMITER";
auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->getJsonKey();
root["parameters"].Vector().push_back(JsonUtils::stringNode(terrainName));
return root;
}
FactionLimiter::FactionLimiter(FactionID creatureFaction)
: faction(creatureFaction)
{
}
ILimiter::EDecision FactionLimiter::limit(const BonusLimitationContext &context) const
{
const auto * bearer = dynamic_cast<const INativeTerrainProvider*>(&context.node);
if(bearer)
{
if(faction != FactionID::DEFAULT)
return bearer->getFaction() == faction ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
switch(context.b->source)
{
case Bonus::CREATURE_ABILITY:
return faction == CreatureID(context.b->sid).toCreature()->getFaction() ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
case Bonus::TOWN_STRUCTURE:
return faction == FactionID(Bonus::getHighFromSid32(context.b->sid)) ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
//TODO: other sources of bonuses
}
}
return ILimiter::EDecision::DISCARD; //Discard by default
}
std::string FactionLimiter::toString() const
{
boost::format fmt("FactionLimiter(faction=%s)");
fmt % VLC->factions()->getByIndex(faction)->getJsonKey();
return fmt.str();
}
JsonNode FactionLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "FACTION_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->factions()->getByIndex(faction)->getJsonKey()));
return root;
}
CreatureLevelLimiter::CreatureLevelLimiter(uint32_t minLevel, uint32_t maxLevel) :
minLevel(minLevel),
maxLevel(maxLevel)
{
}
ILimiter::EDecision CreatureLevelLimiter::limit(const BonusLimitationContext &context) const
{
const auto *c = retrieveCreature(&context.node);
auto accept = c && (c->getLevel() < maxLevel && c->getLevel() >= minLevel);
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD; //drop bonus for non-creatures or non-native residents
}
std::string CreatureLevelLimiter::toString() const
{
boost::format fmt("CreatureLevelLimiter(minLevel=%d,maxLevel=%d)");
fmt % minLevel % maxLevel;
return fmt.str();
}
JsonNode CreatureLevelLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_LEVEL_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::intNode(minLevel));
root["parameters"].Vector().push_back(JsonUtils::intNode(maxLevel));
return root;
}
CreatureAlignmentLimiter::CreatureAlignmentLimiter(EAlignment Alignment)
: alignment(Alignment)
{
}
ILimiter::EDecision CreatureAlignmentLimiter::limit(const BonusLimitationContext &context) const
{
const auto * c = retrieveCreature(&context.node);
if(c) {
if(alignment == EAlignment::GOOD && c->isGood())
return ILimiter::EDecision::ACCEPT;
if(alignment == EAlignment::EVIL && c->isEvil())
return ILimiter::EDecision::ACCEPT;
if(alignment == EAlignment::NEUTRAL && !c->isEvil() && !c->isGood())
return ILimiter::EDecision::ACCEPT;
}
return ILimiter::EDecision::DISCARD;
}
std::string CreatureAlignmentLimiter::toString() const
{
boost::format fmt("CreatureAlignmentLimiter(alignment=%s)");
fmt % GameConstants::ALIGNMENT_NAMES[vstd::to_underlying(alignment)];
return fmt.str();
}
JsonNode CreatureAlignmentLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_ALIGNMENT_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(GameConstants::ALIGNMENT_NAMES[vstd::to_underlying(alignment)]));
return root;
}
RankRangeLimiter::RankRangeLimiter(ui8 Min, ui8 Max)
:minRank(Min), maxRank(Max)
{
}
RankRangeLimiter::RankRangeLimiter()
{
minRank = maxRank = -1;
}
ILimiter::EDecision RankRangeLimiter::limit(const BonusLimitationContext &context) const
{
const CStackInstance * csi = retrieveStackInstance(&context.node);
if(csi)
{
if (csi->getNodeType() == CBonusSystemNode::COMMANDER) //no stack exp bonuses for commander creatures
return ILimiter::EDecision::DISCARD;
if (csi->getExpRank() > minRank && csi->getExpRank() < maxRank)
return ILimiter::EDecision::ACCEPT;
}
return ILimiter::EDecision::DISCARD;
}
OppositeSideLimiter::OppositeSideLimiter(PlayerColor Owner):
owner(std::move(Owner))
{
}
ILimiter::EDecision OppositeSideLimiter::limit(const BonusLimitationContext & context) const
{
auto contextOwner = CBonusSystemNode::retrieveNodeOwner(& context.node);
auto decision = (owner == contextOwner || owner == PlayerColor::CANNOT_DETERMINE) ? ILimiter::EDecision::DISCARD : ILimiter::EDecision::ACCEPT;
return decision;
}
// Aggregate/Boolean Limiters
AggregateLimiter::AggregateLimiter(std::vector<TLimiterPtr> limiters):
limiters(std::move(limiters))
{
}
void AggregateLimiter::add(const TLimiterPtr & limiter)
{
if(limiter)
limiters.push_back(limiter);
}
JsonNode AggregateLimiter::toJsonNode() const
{
JsonNode result(JsonNode::JsonType::DATA_VECTOR);
result.Vector().push_back(JsonUtils::stringNode(getAggregator()));
for(const auto & l : limiters)
result.Vector().push_back(l->toJsonNode());
return result;
}
const std::string AllOfLimiter::aggregator = "allOf";
const std::string & AllOfLimiter::getAggregator() const
{
return aggregator;
}
AllOfLimiter::AllOfLimiter(std::vector<TLimiterPtr> limiters):
AggregateLimiter(limiters)
{
}
ILimiter::EDecision AllOfLimiter::limit(const BonusLimitationContext & context) const
{
bool wasntSure = false;
for(const auto & limiter : limiters)
{
auto result = limiter->limit(context);
if(result == ILimiter::EDecision::DISCARD)
return result;
if(result == ILimiter::EDecision::NOT_SURE)
wasntSure = true;
}
return wasntSure ? ILimiter::EDecision::NOT_SURE : ILimiter::EDecision::ACCEPT;
}
const std::string AnyOfLimiter::aggregator = "anyOf";
const std::string & AnyOfLimiter::getAggregator() const
{
return aggregator;
}
AnyOfLimiter::AnyOfLimiter(std::vector<TLimiterPtr> limiters):
AggregateLimiter(limiters)
{
}
ILimiter::EDecision AnyOfLimiter::limit(const BonusLimitationContext & context) const
{
bool wasntSure = false;
for(const auto & limiter : limiters)
{
auto result = limiter->limit(context);
if(result == ILimiter::EDecision::ACCEPT)
return result;
if(result == ILimiter::EDecision::NOT_SURE)
wasntSure = true;
}
return wasntSure ? ILimiter::EDecision::NOT_SURE : ILimiter::EDecision::DISCARD;
}
const std::string NoneOfLimiter::aggregator = "noneOf";
const std::string & NoneOfLimiter::getAggregator() const
{
return aggregator;
}
NoneOfLimiter::NoneOfLimiter(std::vector<TLimiterPtr> limiters):
AggregateLimiter(limiters)
{
}
ILimiter::EDecision NoneOfLimiter::limit(const BonusLimitationContext & context) const
{
bool wasntSure = false;
for(const auto & limiter : limiters)
{
auto result = limiter->limit(context);
if(result == ILimiter::EDecision::ACCEPT)
return ILimiter::EDecision::DISCARD;
if(result == ILimiter::EDecision::NOT_SURE)
wasntSure = true;
}
return wasntSure ? ILimiter::EDecision::NOT_SURE : ILimiter::EDecision::ACCEPT;
}
// Updaters
std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)

View File

@@ -9,10 +9,8 @@
*/
#pragma once
#include "GameConstants.h"
#include "JsonNode.h"
#include "battle/BattleHex.h"
#include <limits>
#include "../GameConstants.h"
#include "../JsonNode.h"
VCMI_LIB_NAMESPACE_BEGIN
@@ -662,29 +660,6 @@ public:
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const BonusList &bonusList);
struct BonusLimitationContext
{
std::shared_ptr<const Bonus> b;
const CBonusSystemNode & node;
const BonusList & alreadyAccepted;
const BonusList & stillUndecided;
};
class DLL_LINKAGE ILimiter
{
public:
enum class EDecision : uint8_t {ACCEPT, DISCARD, NOT_SURE};
virtual ~ILimiter() = default;
virtual EDecision limit(const BonusLimitationContext &context) const; //0 - accept bonus; 1 - drop bonus; 2 - delay (drops eventually)
virtual std::string toString() const;
virtual JsonNode toJsonNode() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
}
};
class DLL_LINKAGE IBonusBearer
{
@@ -964,227 +939,6 @@ public:
}
};
class DLL_LINKAGE AggregateLimiter : public ILimiter
{
protected:
std::vector<TLimiterPtr> limiters;
virtual const std::string & getAggregator() const = 0;
AggregateLimiter(std::vector<TLimiterPtr> limiters = {});
public:
void add(const TLimiterPtr & limiter);
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & limiters;
}
};
class DLL_LINKAGE AllOfLimiter : public AggregateLimiter
{
protected:
const std::string & getAggregator() const override;
public:
AllOfLimiter(std::vector<TLimiterPtr> limiters = {});
static const std::string aggregator;
EDecision limit(const BonusLimitationContext & context) const override;
};
class DLL_LINKAGE AnyOfLimiter : public AggregateLimiter
{
protected:
const std::string & getAggregator() const override;
public:
AnyOfLimiter(std::vector<TLimiterPtr> limiters = {});
static const std::string aggregator;
EDecision limit(const BonusLimitationContext & context) const override;
};
class DLL_LINKAGE NoneOfLimiter : public AggregateLimiter
{
protected:
const std::string & getAggregator() const override;
public:
NoneOfLimiter(std::vector<TLimiterPtr> limiters = {});
static const std::string aggregator;
EDecision limit(const BonusLimitationContext & context) const override;
};
class DLL_LINKAGE CCreatureTypeLimiter : public ILimiter //affect only stacks of given creature (and optionally it's upgrades)
{
public:
const CCreature * creature = nullptr;
bool includeUpgrades = false;
CCreatureTypeLimiter() = default;
CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades);
void setCreature(const CreatureID & id);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & creature;
h & includeUpgrades;
}
};
class DLL_LINKAGE HasAnotherBonusLimiter : public ILimiter //applies only to nodes that have another bonus working
{
public:
Bonus::BonusType type;
TBonusSubtype subtype;
Bonus::BonusSource source;
si32 sid;
bool isSubtypeRelevant; //check for subtype only if this is true
bool isSourceRelevant; //check for bonus source only if this is true
bool isSourceIDRelevant; //check for bonus source only if this is true
HasAnotherBonusLimiter(Bonus::BonusType bonus = Bonus::NONE);
HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype);
HasAnotherBonusLimiter(Bonus::BonusType bonus, Bonus::BonusSource src);
HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype, Bonus::BonusSource src);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & type;
h & subtype;
h & isSubtypeRelevant;
h & source;
h & isSourceRelevant;
h & sid;
h & isSourceIDRelevant;
}
};
class DLL_LINKAGE CreatureTerrainLimiter : public ILimiter //applies only to creatures that are on specified terrain, default native terrain
{
public:
TerrainId terrainType;
CreatureTerrainLimiter();
CreatureTerrainLimiter(TerrainId terrain);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & terrainType;
}
};
class DLL_LINKAGE CreatureLevelLimiter : public ILimiter //applies only to creatures of given faction
{
public:
uint32_t minLevel;
uint32_t maxLevel;
//accept all levels by default, accept creatures of minLevel <= creature->getLevel() < maxLevel
CreatureLevelLimiter(uint32_t minLevel = std::numeric_limits<uint32_t>::min(), uint32_t maxLevel = std::numeric_limits<uint32_t>::max());
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & minLevel;
h & maxLevel;
}
};
class DLL_LINKAGE FactionLimiter : public ILimiter //applies only to creatures of given faction
{
public:
FactionID faction;
FactionLimiter(FactionID faction = FactionID::DEFAULT);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & faction;
}
};
class DLL_LINKAGE CreatureAlignmentLimiter : public ILimiter //applies only to creatures of given alignment
{
public:
EAlignment alignment;
CreatureAlignmentLimiter(EAlignment Alignment = EAlignment::NEUTRAL);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & alignment;
}
};
class DLL_LINKAGE OppositeSideLimiter : public ILimiter //applies only to creatures of enemy army during combat
{
public:
PlayerColor owner;
OppositeSideLimiter(PlayerColor Owner = PlayerColor::CANNOT_DETERMINE);
EDecision limit(const BonusLimitationContext &context) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & owner;
}
};
class DLL_LINKAGE RankRangeLimiter : public ILimiter //applies to creatures with min <= Rank <= max
{
public:
ui8 minRank, maxRank;
RankRangeLimiter();
RankRangeLimiter(ui8 Min, ui8 Max = 255);
EDecision limit(const BonusLimitationContext &context) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & minRank;
h & maxRank;
}
};
class DLL_LINKAGE UnitOnHexLimiter : public ILimiter //works only on selected hexes
{
public:
std::set<BattleHex> applicableHexes;
UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes = {});
EDecision limit(const BonusLimitationContext &context) const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & applicableHexes;
}
};
namespace Selector
{
@@ -1221,7 +975,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, TLimiterPtr> bonusLimiterMap;
extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
extern DLL_LINKAGE const std::map<std::string, TUpdaterPtr> bonusUpdaterMap;
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;

541
lib/bonuses/ILimiter.cpp Normal file
View File

@@ -0,0 +1,541 @@
/*
* ILimiter.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 "ILimiter.h"
#include "../VCMI_Lib.h"
#include "../spells/CSpellHandler.h"
#include "../CCreatureHandler.h"
#include "../CCreatureSet.h"
#include "../CHeroHandler.h"
#include "../CTownHandler.h"
#include "../CGeneralTextHandler.h"
#include "../CSkillHandler.h"
#include "../CStack.h"
#include "../CArtHandler.h"
#include "../CModHandler.h"
#include "../TerrainHandler.h"
#include "../StringConstants.h"
#include "../battle/BattleInfo.h"
VCMI_LIB_NAMESPACE_BEGIN
const std::map<std::string, TLimiterPtr> bonusLimiterMap =
{
{"SHOOTER_ONLY", std::make_shared<HasAnotherBonusLimiter>(Bonus::SHOOTER)},
{"DRAGON_NATURE", std::make_shared<HasAnotherBonusLimiter>(Bonus::DRAGON_NATURE)},
{"IS_UNDEAD", std::make_shared<HasAnotherBonusLimiter>(Bonus::UNDEAD)},
{"CREATURE_NATIVE_TERRAIN", std::make_shared<CreatureTerrainLimiter>()},
{"CREATURE_FACTION", std::make_shared<AllOfLimiter>(std::initializer_list<TLimiterPtr>{std::make_shared<CreatureLevelLimiter>(), std::make_shared<FactionLimiter>()})},
{"SAME_FACTION", std::make_shared<FactionLimiter>()},
{"CREATURES_ONLY", std::make_shared<CreatureLevelLimiter>()},
{"OPPOSITE_SIDE", std::make_shared<OppositeSideLimiter>()},
};
static const CStack * retrieveStackBattle(const CBonusSystemNode * node)
{
switch(node->getNodeType())
{
case CBonusSystemNode::STACK_BATTLE:
return dynamic_cast<const CStack *>(node);
default:
return nullptr;
}
}
static const CStackInstance * retrieveStackInstance(const CBonusSystemNode * node)
{
switch(node->getNodeType())
{
case CBonusSystemNode::STACK_INSTANCE:
return (dynamic_cast<const CStackInstance *>(node));
case CBonusSystemNode::STACK_BATTLE:
return (dynamic_cast<const CStack *>(node))->base;
default:
return nullptr;
}
}
static const CCreature * retrieveCreature(const CBonusSystemNode *node)
{
switch(node->getNodeType())
{
case CBonusSystemNode::CREATURE:
return (dynamic_cast<const CCreature *>(node));
case CBonusSystemNode::STACK_BATTLE:
return (dynamic_cast<const CStack *>(node))->unitType();
default:
const CStackInstance * csi = retrieveStackInstance(node);
if(csi)
return csi->type;
return nullptr;
}
}
ILimiter::EDecision ILimiter::limit(const BonusLimitationContext &context) const /*return true to drop the bonus */
{
return ILimiter::EDecision::ACCEPT;
}
std::string ILimiter::toString() const
{
return typeid(*this).name();
}
JsonNode ILimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = toString();
return root;
}
ILimiter::EDecision CCreatureTypeLimiter::limit(const BonusLimitationContext &context) const
{
const CCreature *c = retrieveCreature(&context.node);
if(!c)
return ILimiter::EDecision::DISCARD;
auto accept = c->getId() == creature->getId() || (includeUpgrades && creature->isMyUpgrade(c));
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
//drop bonus if it's not our creature and (we don`t check upgrades or its not our upgrade)
}
CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades)
: creature(&creature_), includeUpgrades(IncludeUpgrades)
{
}
void CCreatureTypeLimiter::setCreature(const CreatureID & id)
{
creature = VLC->creh->objects[id];
}
std::string CCreatureTypeLimiter::toString() const
{
boost::format fmt("CCreatureTypeLimiter(creature=%s, includeUpgrades=%s)");
fmt % creature->getJsonKey() % (includeUpgrades ? "true" : "false");
return fmt.str();
}
JsonNode CCreatureTypeLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_TYPE_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(creature->getJsonKey()));
root["parameters"].Vector().push_back(JsonUtils::boolNode(includeUpgrades));
return root;
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter( Bonus::BonusType bonus )
: type(bonus), subtype(0), isSubtypeRelevant(false), isSourceRelevant(false), isSourceIDRelevant(false)
{
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter( Bonus::BonusType bonus, TBonusSubtype _subtype )
: type(bonus), subtype(_subtype), isSubtypeRelevant(true), isSourceRelevant(false), isSourceIDRelevant(false)
{
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter(Bonus::BonusType bonus, Bonus::BonusSource src)
: type(bonus), source(src), isSubtypeRelevant(false), isSourceRelevant(true), isSourceIDRelevant(false)
{
}
HasAnotherBonusLimiter::HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype, Bonus::BonusSource src)
: type(bonus), subtype(_subtype), isSubtypeRelevant(true), source(src), isSourceRelevant(true), isSourceIDRelevant(false)
{
}
ILimiter::EDecision HasAnotherBonusLimiter::limit(const BonusLimitationContext &context) const
{
//TODO: proper selector config with parsing of JSON
auto mySelector = Selector::type()(type);
if(isSubtypeRelevant)
mySelector = mySelector.And(Selector::subtype()(subtype));
if(isSourceRelevant && isSourceIDRelevant)
mySelector = mySelector.And(Selector::source(source, sid));
else if (isSourceRelevant)
mySelector = mySelector.And(Selector::sourceTypeSel(source));
//if we have a bonus of required type accepted, limiter should accept also this bonus
if(context.alreadyAccepted.getFirst(mySelector))
return ILimiter::EDecision::ACCEPT;
//if there are no matching bonuses pending, we can (and must) reject right away
if(!context.stillUndecided.getFirst(mySelector))
return ILimiter::EDecision::DISCARD;
//do not accept for now but it may change if more bonuses gets included
return ILimiter::EDecision::NOT_SURE;
}
std::string HasAnotherBonusLimiter::toString() const
{
std::string typeName = vstd::findKey(bonusNameMap, type);
if(isSubtypeRelevant)
{
boost::format fmt("HasAnotherBonusLimiter(type=%s, subtype=%d)");
fmt % typeName % subtype;
return fmt.str();
}
else
{
boost::format fmt("HasAnotherBonusLimiter(type=%s)");
fmt % typeName;
return fmt.str();
}
}
JsonNode HasAnotherBonusLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
std::string typeName = vstd::findKey(bonusNameMap, type);
auto sourceTypeName = vstd::findKey(bonusSourceMap, source);
root["type"].String() = "HAS_ANOTHER_BONUS_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(typeName));
if(isSubtypeRelevant)
root["parameters"].Vector().push_back(JsonUtils::intNode(subtype));
if(isSourceRelevant)
root["parameters"].Vector().push_back(JsonUtils::stringNode(sourceTypeName));
return root;
}
ILimiter::EDecision UnitOnHexLimiter::limit(const BonusLimitationContext &context) const
{
const auto * stack = retrieveStackBattle(&context.node);
if(!stack)
return ILimiter::EDecision::DISCARD;
auto accept = false;
for (const auto & hex : stack->getHexes())
accept |= !!applicableHexes.count(hex);
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
}
UnitOnHexLimiter::UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes):
applicableHexes(applicableHexes)
{
}
JsonNode UnitOnHexLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "UNIT_ON_HEXES";
for(const auto & hex : applicableHexes)
root["parameters"].Vector().push_back(JsonUtils::intNode(hex));
return root;
}
CreatureTerrainLimiter::CreatureTerrainLimiter()
: terrainType(ETerrainId::NATIVE_TERRAIN)
{
}
CreatureTerrainLimiter::CreatureTerrainLimiter(TerrainId terrain):
terrainType(terrain)
{
}
ILimiter::EDecision CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const
{
const CStack *stack = retrieveStackBattle(&context.node);
if(stack)
{
if (terrainType == ETerrainId::NATIVE_TERRAIN && stack->isOnNativeTerrain())//terrainType not specified = native
return ILimiter::EDecision::ACCEPT;
if(terrainType != ETerrainId::NATIVE_TERRAIN && stack->isOnTerrain(terrainType))
return ILimiter::EDecision::ACCEPT;
}
return ILimiter::EDecision::DISCARD;
//TODO neutral creatues
}
std::string CreatureTerrainLimiter::toString() const
{
boost::format fmt("CreatureTerrainLimiter(terrainType=%s)");
auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->getJsonKey();
fmt % (terrainType == ETerrainId::NATIVE_TERRAIN ? "native" : terrainName);
return fmt.str();
}
JsonNode CreatureTerrainLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_TERRAIN_LIMITER";
auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->getJsonKey();
root["parameters"].Vector().push_back(JsonUtils::stringNode(terrainName));
return root;
}
FactionLimiter::FactionLimiter(FactionID creatureFaction)
: faction(creatureFaction)
{
}
ILimiter::EDecision FactionLimiter::limit(const BonusLimitationContext &context) const
{
const auto * bearer = dynamic_cast<const INativeTerrainProvider*>(&context.node);
if(bearer)
{
if(faction != FactionID::DEFAULT)
return bearer->getFaction() == faction ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
switch(context.b.source)
{
case Bonus::CREATURE_ABILITY:
return bearer->getFaction() == CreatureID(context.b.sid).toCreature()->getFaction() ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
case Bonus::TOWN_STRUCTURE:
return bearer->getFaction() == FactionID(Bonus::getHighFromSid32(context.b.sid)) ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
//TODO: other sources of bonuses
}
}
return ILimiter::EDecision::DISCARD; //Discard by default
}
std::string FactionLimiter::toString() const
{
boost::format fmt("FactionLimiter(faction=%s)");
fmt % VLC->factions()->getByIndex(faction)->getJsonKey();
return fmt.str();
}
JsonNode FactionLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "FACTION_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->factions()->getByIndex(faction)->getJsonKey()));
return root;
}
CreatureLevelLimiter::CreatureLevelLimiter(uint32_t minLevel, uint32_t maxLevel) :
minLevel(minLevel),
maxLevel(maxLevel)
{
}
ILimiter::EDecision CreatureLevelLimiter::limit(const BonusLimitationContext &context) const
{
const auto *c = retrieveCreature(&context.node);
auto accept = c && (c->getLevel() < maxLevel && c->getLevel() >= minLevel);
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD; //drop bonus for non-creatures or non-native residents
}
std::string CreatureLevelLimiter::toString() const
{
boost::format fmt("CreatureLevelLimiter(minLevel=%d,maxLevel=%d)");
fmt % minLevel % maxLevel;
return fmt.str();
}
JsonNode CreatureLevelLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_LEVEL_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::intNode(minLevel));
root["parameters"].Vector().push_back(JsonUtils::intNode(maxLevel));
return root;
}
CreatureAlignmentLimiter::CreatureAlignmentLimiter(EAlignment Alignment)
: alignment(Alignment)
{
}
ILimiter::EDecision CreatureAlignmentLimiter::limit(const BonusLimitationContext &context) const
{
const auto * c = retrieveCreature(&context.node);
if(c) {
if(alignment == EAlignment::GOOD && c->isGood())
return ILimiter::EDecision::ACCEPT;
if(alignment == EAlignment::EVIL && c->isEvil())
return ILimiter::EDecision::ACCEPT;
if(alignment == EAlignment::NEUTRAL && !c->isEvil() && !c->isGood())
return ILimiter::EDecision::ACCEPT;
}
return ILimiter::EDecision::DISCARD;
}
std::string CreatureAlignmentLimiter::toString() const
{
boost::format fmt("CreatureAlignmentLimiter(alignment=%s)");
fmt % GameConstants::ALIGNMENT_NAMES[vstd::to_underlying(alignment)];
return fmt.str();
}
JsonNode CreatureAlignmentLimiter::toJsonNode() const
{
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
root["type"].String() = "CREATURE_ALIGNMENT_LIMITER";
root["parameters"].Vector().push_back(JsonUtils::stringNode(GameConstants::ALIGNMENT_NAMES[vstd::to_underlying(alignment)]));
return root;
}
RankRangeLimiter::RankRangeLimiter(ui8 Min, ui8 Max)
:minRank(Min), maxRank(Max)
{
}
RankRangeLimiter::RankRangeLimiter()
{
minRank = maxRank = -1;
}
ILimiter::EDecision RankRangeLimiter::limit(const BonusLimitationContext &context) const
{
const CStackInstance * csi = retrieveStackInstance(&context.node);
if(csi)
{
if (csi->getNodeType() == CBonusSystemNode::COMMANDER) //no stack exp bonuses for commander creatures
return ILimiter::EDecision::DISCARD;
if (csi->getExpRank() > minRank && csi->getExpRank() < maxRank)
return ILimiter::EDecision::ACCEPT;
}
return ILimiter::EDecision::DISCARD;
}
OppositeSideLimiter::OppositeSideLimiter(PlayerColor Owner):
owner(std::move(Owner))
{
}
ILimiter::EDecision OppositeSideLimiter::limit(const BonusLimitationContext & context) const
{
auto contextOwner = CBonusSystemNode::retrieveNodeOwner(& context.node);
auto decision = (owner == contextOwner || owner == PlayerColor::CANNOT_DETERMINE) ? ILimiter::EDecision::DISCARD : ILimiter::EDecision::ACCEPT;
return decision;
}
// Aggregate/Boolean Limiters
AggregateLimiter::AggregateLimiter(std::vector<TLimiterPtr> limiters):
limiters(std::move(limiters))
{
}
void AggregateLimiter::add(const TLimiterPtr & limiter)
{
if(limiter)
limiters.push_back(limiter);
}
JsonNode AggregateLimiter::toJsonNode() const
{
JsonNode result(JsonNode::JsonType::DATA_VECTOR);
result.Vector().push_back(JsonUtils::stringNode(getAggregator()));
for(const auto & l : limiters)
result.Vector().push_back(l->toJsonNode());
return result;
}
const std::string AllOfLimiter::aggregator = "allOf";
const std::string & AllOfLimiter::getAggregator() const
{
return aggregator;
}
AllOfLimiter::AllOfLimiter(std::vector<TLimiterPtr> limiters):
AggregateLimiter(limiters)
{
}
ILimiter::EDecision AllOfLimiter::limit(const BonusLimitationContext & context) const
{
bool wasntSure = false;
for(const auto & limiter : limiters)
{
auto result = limiter->limit(context);
if(result == ILimiter::EDecision::DISCARD)
return result;
if(result == ILimiter::EDecision::NOT_SURE)
wasntSure = true;
}
return wasntSure ? ILimiter::EDecision::NOT_SURE : ILimiter::EDecision::ACCEPT;
}
const std::string AnyOfLimiter::aggregator = "anyOf";
const std::string & AnyOfLimiter::getAggregator() const
{
return aggregator;
}
AnyOfLimiter::AnyOfLimiter(std::vector<TLimiterPtr> limiters):
AggregateLimiter(limiters)
{
}
ILimiter::EDecision AnyOfLimiter::limit(const BonusLimitationContext & context) const
{
bool wasntSure = false;
for(const auto & limiter : limiters)
{
auto result = limiter->limit(context);
if(result == ILimiter::EDecision::ACCEPT)
return result;
if(result == ILimiter::EDecision::NOT_SURE)
wasntSure = true;
}
return wasntSure ? ILimiter::EDecision::NOT_SURE : ILimiter::EDecision::DISCARD;
}
const std::string NoneOfLimiter::aggregator = "noneOf";
const std::string & NoneOfLimiter::getAggregator() const
{
return aggregator;
}
NoneOfLimiter::NoneOfLimiter(std::vector<TLimiterPtr> limiters):
AggregateLimiter(limiters)
{
}
ILimiter::EDecision NoneOfLimiter::limit(const BonusLimitationContext & context) const
{
bool wasntSure = false;
for(const auto & limiter : limiters)
{
auto result = limiter->limit(context);
if(result == ILimiter::EDecision::ACCEPT)
return ILimiter::EDecision::DISCARD;
if(result == ILimiter::EDecision::NOT_SURE)
wasntSure = true;
}
return wasntSure ? ILimiter::EDecision::NOT_SURE : ILimiter::EDecision::ACCEPT;
}
VCMI_LIB_NAMESPACE_END

264
lib/bonuses/ILimiter.h Normal file
View File

@@ -0,0 +1,264 @@
/*
* ILimiter.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
*
*/
#include "HeroBonus.h"
#include "battle/BattleHex.h"
VCMI_LIB_NAMESPACE_BEGIN
extern DLL_LINKAGE const std::map<std::string, TLimiterPtr> bonusLimiterMap;
struct BonusLimitationContext
{
const Bonus & b;
const CBonusSystemNode & node;
const BonusList & alreadyAccepted;
const BonusList & stillUndecided;
};
class DLL_LINKAGE ILimiter
{
public:
enum class EDecision : uint8_t {ACCEPT, DISCARD, NOT_SURE};
virtual ~ILimiter() = default;
virtual EDecision limit(const BonusLimitationContext &context) const; //0 - accept bonus; 1 - drop bonus; 2 - delay (drops eventually)
virtual std::string toString() const;
virtual JsonNode toJsonNode() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
}
};
class DLL_LINKAGE AggregateLimiter : public ILimiter
{
protected:
std::vector<TLimiterPtr> limiters;
virtual const std::string & getAggregator() const = 0;
AggregateLimiter(std::vector<TLimiterPtr> limiters = {});
public:
void add(const TLimiterPtr & limiter);
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & limiters;
}
};
class DLL_LINKAGE AllOfLimiter : public AggregateLimiter
{
protected:
const std::string & getAggregator() const override;
public:
AllOfLimiter(std::vector<TLimiterPtr> limiters = {});
static const std::string aggregator;
EDecision limit(const BonusLimitationContext & context) const override;
};
class DLL_LINKAGE AnyOfLimiter : public AggregateLimiter
{
protected:
const std::string & getAggregator() const override;
public:
AnyOfLimiter(std::vector<TLimiterPtr> limiters = {});
static const std::string aggregator;
EDecision limit(const BonusLimitationContext & context) const override;
};
class DLL_LINKAGE NoneOfLimiter : public AggregateLimiter
{
protected:
const std::string & getAggregator() const override;
public:
NoneOfLimiter(std::vector<TLimiterPtr> limiters = {});
static const std::string aggregator;
EDecision limit(const BonusLimitationContext & context) const override;
};
class DLL_LINKAGE CCreatureTypeLimiter : public ILimiter //affect only stacks of given creature (and optionally it's upgrades)
{
public:
const CCreature * creature = nullptr;
bool includeUpgrades = false;
CCreatureTypeLimiter() = default;
CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades);
void setCreature(const CreatureID & id);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & creature;
h & includeUpgrades;
}
};
class DLL_LINKAGE HasAnotherBonusLimiter : public ILimiter //applies only to nodes that have another bonus working
{
public:
Bonus::BonusType type;
TBonusSubtype subtype;
Bonus::BonusSource source;
si32 sid;
bool isSubtypeRelevant; //check for subtype only if this is true
bool isSourceRelevant; //check for bonus source only if this is true
bool isSourceIDRelevant; //check for bonus source only if this is true
HasAnotherBonusLimiter(Bonus::BonusType bonus = Bonus::NONE);
HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype);
HasAnotherBonusLimiter(Bonus::BonusType bonus, Bonus::BonusSource src);
HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype, Bonus::BonusSource src);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & type;
h & subtype;
h & isSubtypeRelevant;
h & source;
h & isSourceRelevant;
h & sid;
h & isSourceIDRelevant;
}
};
class DLL_LINKAGE CreatureTerrainLimiter : public ILimiter //applies only to creatures that are on specified terrain, default native terrain
{
public:
TerrainId terrainType;
CreatureTerrainLimiter();
CreatureTerrainLimiter(TerrainId terrain);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & terrainType;
}
};
class DLL_LINKAGE CreatureLevelLimiter : public ILimiter //applies only to creatures of given faction
{
public:
uint32_t minLevel;
uint32_t maxLevel;
//accept all levels by default, accept creatures of minLevel <= creature->getLevel() < maxLevel
CreatureLevelLimiter(uint32_t minLevel = std::numeric_limits<uint32_t>::min(), uint32_t maxLevel = std::numeric_limits<uint32_t>::max());
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & minLevel;
h & maxLevel;
}
};
class DLL_LINKAGE FactionLimiter : public ILimiter //applies only to creatures of given faction
{
public:
FactionID faction;
FactionLimiter(FactionID faction = FactionID::DEFAULT);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & faction;
}
};
class DLL_LINKAGE CreatureAlignmentLimiter : public ILimiter //applies only to creatures of given alignment
{
public:
EAlignment alignment;
CreatureAlignmentLimiter(EAlignment Alignment = EAlignment::NEUTRAL);
EDecision limit(const BonusLimitationContext &context) const override;
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & alignment;
}
};
class DLL_LINKAGE OppositeSideLimiter : public ILimiter //applies only to creatures of enemy army during combat
{
public:
PlayerColor owner;
OppositeSideLimiter(PlayerColor Owner = PlayerColor::CANNOT_DETERMINE);
EDecision limit(const BonusLimitationContext &context) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & owner;
}
};
class DLL_LINKAGE RankRangeLimiter : public ILimiter //applies to creatures with min <= Rank <= max
{
public:
ui8 minRank, maxRank;
RankRangeLimiter();
RankRangeLimiter(ui8 Min, ui8 Max = 255);
EDecision limit(const BonusLimitationContext &context) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & minRank;
h & maxRank;
}
};
class DLL_LINKAGE UnitOnHexLimiter : public ILimiter //works only on selected hexes
{
public:
std::set<BattleHex> applicableHexes;
UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes = {});
EDecision limit(const BonusLimitationContext &context) const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<ILimiter&>(*this);
h & applicableHexes;
}
};
VCMI_LIB_NAMESPACE_END

View File

@@ -22,6 +22,7 @@
#include "../mapObjects/CommonConstructors.h"
#include "../mapObjects/MapObjects.h"
#include "../battle/CObstacleInstance.h"
#include "../bonuses/ILimiter.h"
#include "../CStack.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@@ -16,6 +16,7 @@
#include "../../NetPacks.h"
#include "../../mapObjects/CGTownInstance.h"
#include "../../bonuses/ILimiter.h"
#include "../../battle/IBattleState.h"
#include "../../battle/CBattleInfoCallback.h"
#include "../../serializer/JsonSerializeFormat.h"