Bonus.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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 "../CStack.h"
  25. #include "../CArtHandler.h"
  26. #include "../CModHandler.h"
  27. #include "../TerrainHandler.h"
  28. #include "../StringConstants.h"
  29. #include "../battle/BattleInfo.h"
  30. VCMI_LIB_NAMESPACE_BEGIN
  31. #define BONUS_NAME(x) { #x, Bonus::x },
  32. const std::map<std::string, Bonus::BonusType> bonusNameMap = {
  33. BONUS_LIST
  34. };
  35. #undef BONUS_NAME
  36. #define BONUS_VALUE(x) { #x, Bonus::x },
  37. const std::map<std::string, Bonus::ValueType> bonusValueMap = { BONUS_VALUE_LIST };
  38. #undef BONUS_VALUE
  39. #define BONUS_SOURCE(x) { #x, Bonus::x },
  40. const std::map<std::string, Bonus::BonusSource> bonusSourceMap = { BONUS_SOURCE_LIST };
  41. #undef BONUS_SOURCE
  42. #define BONUS_ITEM(x) { #x, Bonus::x },
  43. const std::map<std::string, ui16> bonusDurationMap =
  44. {
  45. BONUS_ITEM(PERMANENT)
  46. BONUS_ITEM(ONE_BATTLE)
  47. BONUS_ITEM(ONE_DAY)
  48. BONUS_ITEM(ONE_WEEK)
  49. BONUS_ITEM(N_TURNS)
  50. BONUS_ITEM(N_DAYS)
  51. BONUS_ITEM(UNTIL_BEING_ATTACKED)
  52. BONUS_ITEM(UNTIL_ATTACK)
  53. BONUS_ITEM(STACK_GETS_TURN)
  54. BONUS_ITEM(COMMANDER_KILLED)
  55. { "UNITL_BEING_ATTACKED", Bonus::UNTIL_BEING_ATTACKED }//typo, but used in some mods
  56. };
  57. const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect =
  58. {
  59. BONUS_ITEM(NO_LIMIT)
  60. BONUS_ITEM(ONLY_DISTANCE_FIGHT)
  61. BONUS_ITEM(ONLY_MELEE_FIGHT)
  62. };
  63. const std::set<std::string> deprecatedBonusSet = {
  64. "SECONDARY_SKILL_PREMY",
  65. "SECONDARY_SKILL_VAL2",
  66. "MAXED_SPELL",
  67. "LAND_MOVEMENT",
  68. "SEA_MOVEMENT",
  69. "SIGHT_RADIOUS",
  70. "NO_TYPE",
  71. "SPECIAL_SECONDARY_SKILL",
  72. "FULL_HP_REGENERATION",
  73. "KING1",
  74. "KING2",
  75. "KING3",
  76. "BLOCK_MORALE",
  77. "BLOCK_LUCK",
  78. "SELF_MORALE",
  79. "SELF_LUCK",
  80. "DIRECT_DAMAGE_IMMUNITY"
  81. };
  82. //This constructor should be placed here to avoid side effects
  83. CAddInfo::CAddInfo() = default;
  84. CAddInfo::CAddInfo(si32 value)
  85. {
  86. if(value != CAddInfo::NONE)
  87. push_back(value);
  88. }
  89. bool CAddInfo::operator==(si32 value) const
  90. {
  91. switch(size())
  92. {
  93. case 0:
  94. return value == CAddInfo::NONE;
  95. case 1:
  96. return operator[](0) == value;
  97. default:
  98. return false;
  99. }
  100. }
  101. bool CAddInfo::operator!=(si32 value) const
  102. {
  103. return !operator==(value);
  104. }
  105. si32 & CAddInfo::operator[](size_type pos)
  106. {
  107. if(pos >= size())
  108. resize(pos + 1, CAddInfo::NONE);
  109. return vector::operator[](pos);
  110. }
  111. si32 CAddInfo::operator[](size_type pos) const
  112. {
  113. return pos < size() ? vector::operator[](pos) : CAddInfo::NONE;
  114. }
  115. std::string CAddInfo::toString() const
  116. {
  117. return toJsonNode().toJson(true);
  118. }
  119. JsonNode CAddInfo::toJsonNode() const
  120. {
  121. if(size() < 2)
  122. {
  123. return JsonUtils::intNode(operator[](0));
  124. }
  125. else
  126. {
  127. JsonNode node(JsonNode::JsonType::DATA_VECTOR);
  128. for(si32 value : *this)
  129. node.Vector().push_back(JsonUtils::intNode(value));
  130. return node;
  131. }
  132. }
  133. std::string Bonus::Description(std::optional<si32> customValue) const
  134. {
  135. std::ostringstream str;
  136. if(description.empty())
  137. {
  138. if(stacking.empty() || stacking == "ALWAYS")
  139. {
  140. switch(source)
  141. {
  142. case ARTIFACT:
  143. str << ArtifactID(sid).toArtifact(VLC->artifacts())->getNameTranslated();
  144. break;
  145. case SPELL_EFFECT:
  146. str << SpellID(sid).toSpell(VLC->spells())->getNameTranslated();
  147. break;
  148. case CREATURE_ABILITY:
  149. str << VLC->creh->objects[sid]->getNamePluralTranslated();
  150. break;
  151. case SECONDARY_SKILL:
  152. str << VLC->skillh->getByIndex(sid)->getNameTranslated();
  153. break;
  154. case HERO_SPECIAL:
  155. str << VLC->heroh->objects[sid]->getNameTranslated();
  156. break;
  157. default:
  158. //todo: handle all possible sources
  159. str << "Unknown";
  160. break;
  161. }
  162. }
  163. else
  164. str << stacking;
  165. }
  166. else
  167. {
  168. str << description;
  169. }
  170. if(auto value = customValue.value_or(val))
  171. str << " " << std::showpos << value;
  172. return str.str();
  173. }
  174. JsonNode subtypeToJson(Bonus::BonusType type, int subtype)
  175. {
  176. switch(type)
  177. {
  178. case Bonus::PRIMARY_SKILL:
  179. return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
  180. case Bonus::SPECIAL_SPELL_LEV:
  181. case Bonus::SPECIFIC_SPELL_DAMAGE:
  182. case Bonus::SPELL:
  183. case Bonus::SPECIAL_PECULIAR_ENCHANT:
  184. case Bonus::SPECIAL_ADD_VALUE_ENCHANT:
  185. case Bonus::SPECIAL_FIXED_VALUE_ENCHANT:
  186. return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "spell", SpellID::encode(subtype)));
  187. case Bonus::IMPROVED_NECROMANCY:
  188. case Bonus::SPECIAL_UPGRADE:
  189. return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(subtype)));
  190. case Bonus::GENERATE_RESOURCE:
  191. return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]);
  192. default:
  193. return JsonUtils::intNode(subtype);
  194. }
  195. }
  196. JsonNode additionalInfoToJson(Bonus::BonusType type, CAddInfo addInfo)
  197. {
  198. switch(type)
  199. {
  200. case Bonus::SPECIAL_UPGRADE:
  201. return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
  202. default:
  203. return addInfo.toJsonNode();
  204. }
  205. }
  206. JsonNode durationToJson(ui16 duration)
  207. {
  208. std::vector<std::string> durationNames;
  209. for(ui16 durBit = 1; durBit; durBit = durBit << 1)
  210. {
  211. if(duration & durBit)
  212. durationNames.push_back(vstd::findKey(bonusDurationMap, durBit));
  213. }
  214. if(durationNames.size() == 1)
  215. {
  216. return JsonUtils::stringNode(durationNames[0]);
  217. }
  218. else
  219. {
  220. JsonNode node(JsonNode::JsonType::DATA_VECTOR);
  221. for(const std::string & dur : durationNames)
  222. node.Vector().push_back(JsonUtils::stringNode(dur));
  223. return node;
  224. }
  225. }
  226. JsonNode Bonus::toJsonNode() const
  227. {
  228. JsonNode root(JsonNode::JsonType::DATA_STRUCT);
  229. // only add values that might reasonably be found in config files
  230. root["type"].String() = vstd::findKey(bonusNameMap, type);
  231. if(subtype != -1)
  232. root["subtype"] = subtypeToJson(type, subtype);
  233. if(additionalInfo != CAddInfo::NONE)
  234. root["addInfo"] = additionalInfoToJson(type, additionalInfo);
  235. if(duration != 0)
  236. {
  237. JsonNode durationVec(JsonNode::JsonType::DATA_VECTOR);
  238. for(const auto & kv : bonusDurationMap)
  239. {
  240. if(duration & kv.second)
  241. durationVec.Vector().push_back(JsonUtils::stringNode(kv.first));
  242. }
  243. root["duration"] = durationVec;
  244. }
  245. if(turnsRemain != 0)
  246. root["turns"].Integer() = turnsRemain;
  247. if(source != OTHER)
  248. root["sourceType"].String() = vstd::findKey(bonusSourceMap, source);
  249. if(targetSourceType != OTHER)
  250. root["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetSourceType);
  251. if(sid != 0)
  252. root["sourceID"].Integer() = sid;
  253. if(val != 0)
  254. root["val"].Integer() = val;
  255. if(valType != ADDITIVE_VALUE)
  256. root["valueType"].String() = vstd::findKey(bonusValueMap, valType);
  257. if(!stacking.empty())
  258. root["stacking"].String() = stacking;
  259. if(!description.empty())
  260. root["description"].String() = description;
  261. if(effectRange != NO_LIMIT)
  262. root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
  263. if(duration != PERMANENT)
  264. root["duration"] = durationToJson(duration);
  265. if(turnsRemain)
  266. root["turns"].Integer() = turnsRemain;
  267. if(limiter)
  268. root["limiters"] = limiter->toJsonNode();
  269. if(updater)
  270. root["updater"] = updater->toJsonNode();
  271. if(propagator)
  272. root["propagator"].String() = vstd::findKey(bonusPropagatorMap, propagator);
  273. return root;
  274. }
  275. std::string Bonus::nameForBonus() const
  276. {
  277. switch(type)
  278. {
  279. case Bonus::PRIMARY_SKILL:
  280. return PrimarySkill::names[subtype];
  281. case Bonus::SPECIAL_SPELL_LEV:
  282. case Bonus::SPECIFIC_SPELL_DAMAGE:
  283. case Bonus::SPELL:
  284. case Bonus::SPECIAL_PECULIAR_ENCHANT:
  285. case Bonus::SPECIAL_ADD_VALUE_ENCHANT:
  286. case Bonus::SPECIAL_FIXED_VALUE_ENCHANT:
  287. return VLC->spells()->getByIndex(subtype)->getJsonKey();
  288. case Bonus::SPECIAL_UPGRADE:
  289. return CreatureID::encode(subtype) + "2" + CreatureID::encode(additionalInfo[0]);
  290. case Bonus::GENERATE_RESOURCE:
  291. return GameConstants::RESOURCE_NAMES[subtype];
  292. case Bonus::STACKS_SPEED:
  293. return "speed";
  294. default:
  295. return vstd::findKey(bonusNameMap, type);
  296. }
  297. }
  298. Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
  299. duration(static_cast<ui16>(Duration)),
  300. type(Type),
  301. subtype(Subtype),
  302. source(Src),
  303. val(Val),
  304. sid(ID),
  305. description(std::move(Desc))
  306. {
  307. boost::algorithm::trim(description);
  308. targetSourceType = OTHER;
  309. }
  310. Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, ValueType ValType):
  311. duration(static_cast<ui16>(Duration)),
  312. type(Type),
  313. subtype(Subtype),
  314. source(Src),
  315. val(Val),
  316. sid(ID),
  317. valType(ValType)
  318. {
  319. turnsRemain = 0;
  320. effectRange = NO_LIMIT;
  321. targetSourceType = OTHER;
  322. }
  323. std::shared_ptr<Bonus> Bonus::addPropagator(const TPropagatorPtr & Propagator)
  324. {
  325. propagator = Propagator;
  326. return this->shared_from_this();
  327. }
  328. DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
  329. {
  330. for(const auto & i : bonusNameMap)
  331. if(i.second == bonus.type)
  332. out << "\tType: " << i.first << " \t";
  333. #define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
  334. printField(val);
  335. printField(subtype);
  336. printField(duration);
  337. printField(source);
  338. printField(sid);
  339. if(bonus.additionalInfo != CAddInfo::NONE)
  340. out << "\taddInfo: " << bonus.additionalInfo.toString() << "\n";
  341. printField(turnsRemain);
  342. printField(valType);
  343. if(!bonus.stacking.empty())
  344. out << "\tstacking: \"" << bonus.stacking << "\"\n";
  345. printField(effectRange);
  346. #undef printField
  347. if(bonus.limiter)
  348. out << "\tLimiter: " << bonus.limiter->toString() << "\n";
  349. if(bonus.updater)
  350. out << "\tUpdater: " << bonus.updater->toString() << "\n";
  351. return out;
  352. }
  353. std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
  354. {
  355. if (limiter)
  356. {
  357. //If we already have limiter list, retrieve it
  358. auto limiterList = std::dynamic_pointer_cast<AllOfLimiter>(limiter);
  359. if(!limiterList)
  360. {
  361. //Create a new limiter list with old limiter and the new one will be pushed later
  362. limiterList = std::make_shared<AllOfLimiter>();
  363. limiterList->add(limiter);
  364. limiter = limiterList;
  365. }
  366. limiterList->add(Limiter);
  367. }
  368. else
  369. {
  370. limiter = Limiter;
  371. }
  372. return this->shared_from_this();
  373. }
  374. // Updaters
  375. std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
  376. {
  377. updater = Updater;
  378. return this->shared_from_this();
  379. }
  380. VCMI_LIB_NAMESPACE_END