Bonus.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. * Bonus.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "Bonus.h"
  12. #include "CBonusSystemNode.h"
  13. #include "Limiters.h"
  14. #include "Updaters.h"
  15. #include "Propagators.h"
  16. #include "../VCMI_Lib.h"
  17. #include "../spells/CSpellHandler.h"
  18. #include "../CCreatureHandler.h"
  19. #include "../CCreatureSet.h"
  20. #include "../CHeroHandler.h"
  21. #include "../CTownHandler.h"
  22. #include "../CGeneralTextHandler.h"
  23. #include "../CSkillHandler.h"
  24. #include "../CArtHandler.h"
  25. #include "../CModHandler.h"
  26. #include "../TerrainHandler.h"
  27. #include "../StringConstants.h"
  28. #include "../battle/BattleInfo.h"
  29. VCMI_LIB_NAMESPACE_BEGIN
  30. const std::set<std::string> deprecatedBonusSet = {
  31. "SECONDARY_SKILL_PREMY",
  32. "SECONDARY_SKILL_VAL2",
  33. "MAXED_SPELL",
  34. "LAND_MOVEMENT",
  35. "SEA_MOVEMENT",
  36. "SIGHT_RADIOUS",
  37. "NO_TYPE",
  38. "SPECIAL_SECONDARY_SKILL",
  39. "FULL_HP_REGENERATION",
  40. "KING1",
  41. "KING2",
  42. "KING3",
  43. "BLOCK_MORALE",
  44. "BLOCK_LUCK",
  45. "SELF_MORALE",
  46. "SELF_LUCK",
  47. "DIRECT_DAMAGE_IMMUNITY"
  48. };
  49. //This constructor should be placed here to avoid side effects
  50. CAddInfo::CAddInfo() = default;
  51. CAddInfo::CAddInfo(si32 value)
  52. {
  53. if(value != CAddInfo::NONE)
  54. push_back(value);
  55. }
  56. bool CAddInfo::operator==(si32 value) const
  57. {
  58. switch(size())
  59. {
  60. case 0:
  61. return value == CAddInfo::NONE;
  62. case 1:
  63. return operator[](0) == value;
  64. default:
  65. return false;
  66. }
  67. }
  68. bool CAddInfo::operator!=(si32 value) const
  69. {
  70. return !operator==(value);
  71. }
  72. si32 & CAddInfo::operator[](size_type pos)
  73. {
  74. if(pos >= size())
  75. resize(pos + 1, CAddInfo::NONE);
  76. return vector::operator[](pos);
  77. }
  78. si32 CAddInfo::operator[](size_type pos) const
  79. {
  80. return pos < size() ? vector::operator[](pos) : CAddInfo::NONE;
  81. }
  82. std::string CAddInfo::toString() const
  83. {
  84. return toJsonNode().toJson(true);
  85. }
  86. JsonNode CAddInfo::toJsonNode() const
  87. {
  88. if(size() < 2)
  89. {
  90. return JsonUtils::intNode(operator[](0));
  91. }
  92. else
  93. {
  94. JsonNode node(JsonNode::JsonType::DATA_VECTOR);
  95. for(si32 value : *this)
  96. node.Vector().push_back(JsonUtils::intNode(value));
  97. return node;
  98. }
  99. }
  100. std::string Bonus::Description(std::optional<si32> customValue) const
  101. {
  102. std::ostringstream str;
  103. if(description.empty())
  104. {
  105. if(stacking.empty() || stacking == "ALWAYS")
  106. {
  107. switch(source)
  108. {
  109. case BonusSource::ARTIFACT:
  110. str << ArtifactID(sid).toArtifact(VLC->artifacts())->getNameTranslated();
  111. break;
  112. case BonusSource::SPELL_EFFECT:
  113. str << SpellID(sid).toSpell(VLC->spells())->getNameTranslated();
  114. break;
  115. case BonusSource::CREATURE_ABILITY:
  116. str << CreatureID(sid).toCreature(VLC->creatures())->getNamePluralTranslated();
  117. break;
  118. case BonusSource::SECONDARY_SKILL:
  119. str << VLC->skills()->getByIndex(sid)->getNameTranslated();
  120. break;
  121. case BonusSource::HERO_SPECIAL:
  122. str << VLC->heroTypes()->getByIndex(sid)->getNameTranslated();
  123. break;
  124. default:
  125. //todo: handle all possible sources
  126. str << "Unknown";
  127. break;
  128. }
  129. }
  130. else
  131. str << stacking;
  132. }
  133. else
  134. {
  135. str << description;
  136. }
  137. if(auto value = customValue.value_or(val))
  138. str << " " << std::showpos << value;
  139. return str.str();
  140. }
  141. JsonNode subtypeToJson(BonusType type, int subtype)
  142. {
  143. switch(type)
  144. {
  145. case BonusType::PRIMARY_SKILL:
  146. return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
  147. case BonusType::SPECIAL_SPELL_LEV:
  148. case BonusType::SPECIFIC_SPELL_DAMAGE:
  149. case BonusType::SPELL:
  150. case BonusType::SPECIAL_PECULIAR_ENCHANT:
  151. case BonusType::SPECIAL_ADD_VALUE_ENCHANT:
  152. case BonusType::SPECIAL_FIXED_VALUE_ENCHANT:
  153. return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "spell", SpellID::encode(subtype)));
  154. case BonusType::IMPROVED_NECROMANCY:
  155. case BonusType::SPECIAL_UPGRADE:
  156. return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(subtype)));
  157. case BonusType::GENERATE_RESOURCE:
  158. return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]);
  159. default:
  160. return JsonUtils::intNode(subtype);
  161. }
  162. }
  163. JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
  164. {
  165. switch(type)
  166. {
  167. case BonusType::SPECIAL_UPGRADE:
  168. return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
  169. default:
  170. return addInfo.toJsonNode();
  171. }
  172. }
  173. JsonNode Bonus::toJsonNode() const
  174. {
  175. JsonNode root(JsonNode::JsonType::DATA_STRUCT);
  176. // only add values that might reasonably be found in config files
  177. root["type"].String() = vstd::findKey(bonusNameMap, type);
  178. if(subtype != -1)
  179. root["subtype"] = subtypeToJson(type, subtype);
  180. if(additionalInfo != CAddInfo::NONE)
  181. root["addInfo"] = additionalInfoToJson(type, additionalInfo);
  182. if(turnsRemain != 0)
  183. root["turns"].Integer() = turnsRemain;
  184. if(source != BonusSource::OTHER)
  185. root["sourceType"].String() = vstd::findKey(bonusSourceMap, source);
  186. if(targetSourceType != BonusSource::OTHER)
  187. root["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetSourceType);
  188. if(sid != 0)
  189. root["sourceID"].Integer() = sid;
  190. if(val != 0)
  191. root["val"].Integer() = val;
  192. if(valType != BonusValueType::ADDITIVE_VALUE)
  193. root["valueType"].String() = vstd::findKey(bonusValueMap, valType);
  194. if(!stacking.empty())
  195. root["stacking"].String() = stacking;
  196. if(!description.empty())
  197. root["description"].String() = description;
  198. if(effectRange != BonusLimitEffect::NO_LIMIT)
  199. root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
  200. if(duration != BonusDuration::PERMANENT)
  201. root["duration"].String() = vstd::findKey(bonusDurationMap, duration);
  202. if(turnsRemain)
  203. root["turns"].Integer() = turnsRemain;
  204. if(limiter)
  205. root["limiters"] = limiter->toJsonNode();
  206. if(updater)
  207. root["updater"] = updater->toJsonNode();
  208. if(propagator)
  209. root["propagator"].String() = vstd::findKey(bonusPropagatorMap, propagator);
  210. return root;
  211. }
  212. Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
  213. duration(Duration),
  214. type(Type),
  215. subtype(Subtype),
  216. source(Src),
  217. val(Val),
  218. sid(ID),
  219. description(std::move(Desc))
  220. {
  221. boost::algorithm::trim(description);
  222. targetSourceType = BonusSource::OTHER;
  223. }
  224. Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, BonusValueType ValType):
  225. duration(Duration),
  226. type(Type),
  227. subtype(Subtype),
  228. source(Src),
  229. val(Val),
  230. sid(ID),
  231. valType(ValType)
  232. {
  233. turnsRemain = 0;
  234. effectRange = BonusLimitEffect::NO_LIMIT;
  235. targetSourceType = BonusSource::OTHER;
  236. }
  237. std::shared_ptr<Bonus> Bonus::addPropagator(const TPropagatorPtr & Propagator)
  238. {
  239. propagator = Propagator;
  240. return this->shared_from_this();
  241. }
  242. DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
  243. {
  244. for(const auto & i : bonusNameMap)
  245. if(i.second == bonus.type)
  246. out << "\tType: " << i.first << " \t";
  247. #define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
  248. printField(val);
  249. printField(subtype);
  250. printField(duration);
  251. printField(source);
  252. printField(sid);
  253. if(bonus.additionalInfo != CAddInfo::NONE)
  254. out << "\taddInfo: " << bonus.additionalInfo.toString() << "\n";
  255. printField(turnsRemain);
  256. printField(valType);
  257. if(!bonus.stacking.empty())
  258. out << "\tstacking: \"" << bonus.stacking << "\"\n";
  259. printField(effectRange);
  260. #undef printField
  261. if(bonus.limiter)
  262. out << "\tLimiter: " << bonus.limiter->toString() << "\n";
  263. if(bonus.updater)
  264. out << "\tUpdater: " << bonus.updater->toString() << "\n";
  265. return out;
  266. }
  267. std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
  268. {
  269. if (limiter)
  270. {
  271. //If we already have limiter list, retrieve it
  272. auto limiterList = std::dynamic_pointer_cast<AllOfLimiter>(limiter);
  273. if(!limiterList)
  274. {
  275. //Create a new limiter list with old limiter and the new one will be pushed later
  276. limiterList = std::make_shared<AllOfLimiter>();
  277. limiterList->add(limiter);
  278. limiter = limiterList;
  279. }
  280. limiterList->add(Limiter);
  281. }
  282. else
  283. {
  284. limiter = Limiter;
  285. }
  286. return this->shared_from_this();
  287. }
  288. // Updaters
  289. std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
  290. {
  291. updater = Updater;
  292. return this->shared_from_this();
  293. }
  294. VCMI_LIB_NAMESPACE_END