| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /*
- * Bonus.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 "Bonus.h"
- #include "CBonusSystemNode.h"
- #include "Limiters.h"
- #include "Updaters.h"
- #include "Propagators.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
- #define BONUS_NAME(x) { #x, Bonus::x },
- const std::map<std::string, Bonus::BonusType> bonusNameMap = {
- BONUS_LIST
- };
- #undef BONUS_NAME
- #define BONUS_VALUE(x) { #x, Bonus::x },
- const std::map<std::string, Bonus::ValueType> bonusValueMap = { BONUS_VALUE_LIST };
- #undef BONUS_VALUE
- #define BONUS_SOURCE(x) { #x, Bonus::x },
- const std::map<std::string, Bonus::BonusSource> bonusSourceMap = { BONUS_SOURCE_LIST };
- #undef BONUS_SOURCE
- #define BONUS_ITEM(x) { #x, Bonus::x },
- const std::map<std::string, ui16> bonusDurationMap =
- {
- BONUS_ITEM(PERMANENT)
- BONUS_ITEM(ONE_BATTLE)
- BONUS_ITEM(ONE_DAY)
- BONUS_ITEM(ONE_WEEK)
- BONUS_ITEM(N_TURNS)
- BONUS_ITEM(N_DAYS)
- BONUS_ITEM(UNTIL_BEING_ATTACKED)
- BONUS_ITEM(UNTIL_ATTACK)
- BONUS_ITEM(STACK_GETS_TURN)
- BONUS_ITEM(COMMANDER_KILLED)
- { "UNITL_BEING_ATTACKED", Bonus::UNTIL_BEING_ATTACKED }//typo, but used in some mods
- };
- const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect =
- {
- BONUS_ITEM(NO_LIMIT)
- BONUS_ITEM(ONLY_DISTANCE_FIGHT)
- BONUS_ITEM(ONLY_MELEE_FIGHT)
- };
- const std::set<std::string> deprecatedBonusSet = {
- "SECONDARY_SKILL_PREMY",
- "SECONDARY_SKILL_VAL2",
- "MAXED_SPELL",
- "LAND_MOVEMENT",
- "SEA_MOVEMENT",
- "SIGHT_RADIOUS",
- "NO_TYPE",
- "SPECIAL_SECONDARY_SKILL",
- "FULL_HP_REGENERATION",
- "KING1",
- "KING2",
- "KING3",
- "BLOCK_MORALE",
- "BLOCK_LUCK",
- "SELF_MORALE",
- "SELF_LUCK",
- "DIRECT_DAMAGE_IMMUNITY"
- };
- //This constructor should be placed here to avoid side effects
- CAddInfo::CAddInfo() = default;
- CAddInfo::CAddInfo(si32 value)
- {
- if(value != CAddInfo::NONE)
- push_back(value);
- }
- bool CAddInfo::operator==(si32 value) const
- {
- switch(size())
- {
- case 0:
- return value == CAddInfo::NONE;
- case 1:
- return operator[](0) == value;
- default:
- return false;
- }
- }
- bool CAddInfo::operator!=(si32 value) const
- {
- return !operator==(value);
- }
- si32 & CAddInfo::operator[](size_type pos)
- {
- if(pos >= size())
- resize(pos + 1, CAddInfo::NONE);
- return vector::operator[](pos);
- }
- si32 CAddInfo::operator[](size_type pos) const
- {
- return pos < size() ? vector::operator[](pos) : CAddInfo::NONE;
- }
- std::string CAddInfo::toString() const
- {
- return toJsonNode().toJson(true);
- }
- JsonNode CAddInfo::toJsonNode() const
- {
- if(size() < 2)
- {
- return JsonUtils::intNode(operator[](0));
- }
- else
- {
- JsonNode node(JsonNode::JsonType::DATA_VECTOR);
- for(si32 value : *this)
- node.Vector().push_back(JsonUtils::intNode(value));
- return node;
- }
- }
- std::string Bonus::Description(std::optional<si32> customValue) const
- {
- std::ostringstream str;
- if(description.empty())
- {
- if(stacking.empty() || stacking == "ALWAYS")
- {
- switch(source)
- {
- case ARTIFACT:
- str << ArtifactID(sid).toArtifact(VLC->artifacts())->getNameTranslated();
- break;
- case SPELL_EFFECT:
- str << SpellID(sid).toSpell(VLC->spells())->getNameTranslated();
- break;
- case CREATURE_ABILITY:
- str << VLC->creh->objects[sid]->getNamePluralTranslated();
- break;
- case SECONDARY_SKILL:
- str << VLC->skillh->getByIndex(sid)->getNameTranslated();
- break;
- case HERO_SPECIAL:
- str << VLC->heroh->objects[sid]->getNameTranslated();
- break;
- default:
- //todo: handle all possible sources
- str << "Unknown";
- break;
- }
- }
- else
- str << stacking;
- }
- else
- {
- str << description;
- }
- if(auto value = customValue.value_or(val))
- str << " " << std::showpos << value;
- return str.str();
- }
- JsonNode subtypeToJson(Bonus::BonusType type, int subtype)
- {
- switch(type)
- {
- case Bonus::PRIMARY_SKILL:
- return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
- case Bonus::SPECIAL_SPELL_LEV:
- case Bonus::SPECIFIC_SPELL_DAMAGE:
- case Bonus::SPELL:
- case Bonus::SPECIAL_PECULIAR_ENCHANT:
- case Bonus::SPECIAL_ADD_VALUE_ENCHANT:
- case Bonus::SPECIAL_FIXED_VALUE_ENCHANT:
- return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "spell", SpellID::encode(subtype)));
- case Bonus::IMPROVED_NECROMANCY:
- case Bonus::SPECIAL_UPGRADE:
- return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(subtype)));
- case Bonus::GENERATE_RESOURCE:
- return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]);
- default:
- return JsonUtils::intNode(subtype);
- }
- }
- JsonNode additionalInfoToJson(Bonus::BonusType type, CAddInfo addInfo)
- {
- switch(type)
- {
- case Bonus::SPECIAL_UPGRADE:
- return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
- default:
- return addInfo.toJsonNode();
- }
- }
- JsonNode durationToJson(ui16 duration)
- {
- std::vector<std::string> durationNames;
- for(ui16 durBit = 1; durBit; durBit = durBit << 1)
- {
- if(duration & durBit)
- durationNames.push_back(vstd::findKey(bonusDurationMap, durBit));
- }
- if(durationNames.size() == 1)
- {
- return JsonUtils::stringNode(durationNames[0]);
- }
- else
- {
- JsonNode node(JsonNode::JsonType::DATA_VECTOR);
- for(const std::string & dur : durationNames)
- node.Vector().push_back(JsonUtils::stringNode(dur));
- return node;
- }
- }
- JsonNode Bonus::toJsonNode() const
- {
- JsonNode root(JsonNode::JsonType::DATA_STRUCT);
- // only add values that might reasonably be found in config files
- root["type"].String() = vstd::findKey(bonusNameMap, type);
- if(subtype != -1)
- root["subtype"] = subtypeToJson(type, subtype);
- if(additionalInfo != CAddInfo::NONE)
- root["addInfo"] = additionalInfoToJson(type, additionalInfo);
- if(duration != 0)
- {
- JsonNode durationVec(JsonNode::JsonType::DATA_VECTOR);
- for(const auto & kv : bonusDurationMap)
- {
- if(duration & kv.second)
- durationVec.Vector().push_back(JsonUtils::stringNode(kv.first));
- }
- root["duration"] = durationVec;
- }
- if(turnsRemain != 0)
- root["turns"].Integer() = turnsRemain;
- if(source != OTHER)
- root["sourceType"].String() = vstd::findKey(bonusSourceMap, source);
- if(targetSourceType != OTHER)
- root["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetSourceType);
- if(sid != 0)
- root["sourceID"].Integer() = sid;
- if(val != 0)
- root["val"].Integer() = val;
- if(valType != ADDITIVE_VALUE)
- root["valueType"].String() = vstd::findKey(bonusValueMap, valType);
- if(!stacking.empty())
- root["stacking"].String() = stacking;
- if(!description.empty())
- root["description"].String() = description;
- if(effectRange != NO_LIMIT)
- root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
- if(duration != PERMANENT)
- root["duration"] = durationToJson(duration);
- if(turnsRemain)
- root["turns"].Integer() = turnsRemain;
- if(limiter)
- root["limiters"] = limiter->toJsonNode();
- if(updater)
- root["updater"] = updater->toJsonNode();
- if(propagator)
- root["propagator"].String() = vstd::findKey(bonusPropagatorMap, propagator);
- return root;
- }
- std::string Bonus::nameForBonus() const
- {
- switch(type)
- {
- case Bonus::PRIMARY_SKILL:
- return PrimarySkill::names[subtype];
- case Bonus::SPECIAL_SPELL_LEV:
- case Bonus::SPECIFIC_SPELL_DAMAGE:
- case Bonus::SPELL:
- case Bonus::SPECIAL_PECULIAR_ENCHANT:
- case Bonus::SPECIAL_ADD_VALUE_ENCHANT:
- case Bonus::SPECIAL_FIXED_VALUE_ENCHANT:
- return VLC->spells()->getByIndex(subtype)->getJsonKey();
- case Bonus::SPECIAL_UPGRADE:
- return CreatureID::encode(subtype) + "2" + CreatureID::encode(additionalInfo[0]);
- case Bonus::GENERATE_RESOURCE:
- return GameConstants::RESOURCE_NAMES[subtype];
- case Bonus::STACKS_SPEED:
- return "speed";
- default:
- return vstd::findKey(bonusNameMap, type);
- }
- }
- 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),
- subtype(Subtype),
- source(Src),
- val(Val),
- sid(ID),
- description(std::move(Desc))
- {
- boost::algorithm::trim(description);
- targetSourceType = OTHER;
- }
- Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, ValueType ValType):
- duration(static_cast<ui16>(Duration)),
- type(Type),
- subtype(Subtype),
- source(Src),
- val(Val),
- sid(ID),
- valType(ValType)
- {
- turnsRemain = 0;
- effectRange = NO_LIMIT;
- targetSourceType = OTHER;
- }
- std::shared_ptr<Bonus> Bonus::addPropagator(const TPropagatorPtr & Propagator)
- {
- propagator = Propagator;
- return this->shared_from_this();
- }
- DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
- {
- for(const auto & i : bonusNameMap)
- if(i.second == bonus.type)
- out << "\tType: " << i.first << " \t";
- #define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
- printField(val);
- printField(subtype);
- printField(duration);
- printField(source);
- printField(sid);
- if(bonus.additionalInfo != CAddInfo::NONE)
- out << "\taddInfo: " << bonus.additionalInfo.toString() << "\n";
- printField(turnsRemain);
- printField(valType);
- if(!bonus.stacking.empty())
- out << "\tstacking: \"" << bonus.stacking << "\"\n";
- printField(effectRange);
- #undef printField
- if(bonus.limiter)
- out << "\tLimiter: " << bonus.limiter->toString() << "\n";
- if(bonus.updater)
- out << "\tUpdater: " << bonus.updater->toString() << "\n";
- return out;
- }
- std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
- {
- if (limiter)
- {
- //If we already have limiter list, retrieve it
- auto limiterList = std::dynamic_pointer_cast<AllOfLimiter>(limiter);
- if(!limiterList)
- {
- //Create a new limiter list with old limiter and the new one will be pushed later
- limiterList = std::make_shared<AllOfLimiter>();
- limiterList->add(limiter);
- limiter = limiterList;
- }
- limiterList->add(Limiter);
- }
- else
- {
- limiter = Limiter;
- }
- return this->shared_from_this();
- }
- // Updaters
- std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
- {
- updater = Updater;
- return this->shared_from_this();
- }
- VCMI_LIB_NAMESPACE_END
|