2
0

Bonus.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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 "Limiters.h"
  13. #include "Updaters.h"
  14. #include "Propagators.h"
  15. #include "../CArtHandler.h"
  16. #include "../CCreatureHandler.h"
  17. #include "../CCreatureSet.h"
  18. #include "../CSkillHandler.h"
  19. #include "../TerrainHandler.h"
  20. #include "../VCMI_Lib.h"
  21. #include "../battle/BattleInfo.h"
  22. #include "../constants/StringConstants.h"
  23. #include "../entities/hero/CHero.h"
  24. #include "../modding/ModUtility.h"
  25. #include "../spells/CSpellHandler.h"
  26. #include "../texts/CGeneralTextHandler.h"
  27. VCMI_LIB_NAMESPACE_BEGIN
  28. //This constructor should be placed here to avoid side effects
  29. CAddInfo::CAddInfo() = default;
  30. CAddInfo::CAddInfo(si32 value)
  31. {
  32. if(value != CAddInfo::NONE)
  33. push_back(value);
  34. }
  35. bool CAddInfo::operator==(si32 value) const
  36. {
  37. switch(size())
  38. {
  39. case 0:
  40. return value == CAddInfo::NONE;
  41. case 1:
  42. return operator[](0) == value;
  43. default:
  44. return false;
  45. }
  46. }
  47. bool CAddInfo::operator!=(si32 value) const
  48. {
  49. return !operator==(value);
  50. }
  51. si32 & CAddInfo::operator[](size_type pos)
  52. {
  53. if(pos >= size())
  54. resize(pos + 1, CAddInfo::NONE);
  55. return vector::operator[](pos);
  56. }
  57. si32 CAddInfo::operator[](size_type pos) const
  58. {
  59. return pos < size() ? vector::operator[](pos) : CAddInfo::NONE;
  60. }
  61. std::string CAddInfo::toString() const
  62. {
  63. return toJsonNode().toCompactString();
  64. }
  65. JsonNode CAddInfo::toJsonNode() const
  66. {
  67. if(size() < 2)
  68. {
  69. return JsonNode(operator[](0));
  70. }
  71. else
  72. {
  73. JsonNode node;
  74. for(si32 value : *this)
  75. node.Vector().emplace_back(value);
  76. return node;
  77. }
  78. }
  79. std::string Bonus::Description(std::optional<si32> customValue) const
  80. {
  81. MetaString descriptionHelper = description;
  82. auto valueToShow = customValue.value_or(val);
  83. if(descriptionHelper.empty())
  84. {
  85. // no custom description - try to generate one based on bonus source
  86. switch(source)
  87. {
  88. case BonusSource::ARTIFACT:
  89. descriptionHelper.appendName(sid.as<ArtifactID>());
  90. break;
  91. case BonusSource::SPELL_EFFECT:
  92. descriptionHelper.appendName(sid.as<SpellID>());
  93. break;
  94. case BonusSource::CREATURE_ABILITY:
  95. descriptionHelper.appendNamePlural(sid.as<CreatureID>());
  96. break;
  97. case BonusSource::SECONDARY_SKILL:
  98. descriptionHelper.appendTextID(sid.as<SecondarySkill>().toEntity(VLC)->getNameTextID());
  99. break;
  100. case BonusSource::HERO_SPECIAL:
  101. descriptionHelper.appendTextID(sid.as<HeroTypeID>().toEntity(VLC)->getNameTextID());
  102. break;
  103. }
  104. }
  105. if(descriptionHelper.empty())
  106. {
  107. // still no description - try to generate one based on duration
  108. if ((duration & BonusDuration::ONE_BATTLE) != 0)
  109. {
  110. if (val > 0)
  111. descriptionHelper.appendTextID("core.arraytxt.110"); //+%d Temporary until next battle"
  112. else
  113. descriptionHelper.appendTextID("core.arraytxt.109"); //-%d Temporary until next battle"
  114. // erase sign - already present in description string
  115. valueToShow = std::abs(valueToShow);
  116. }
  117. }
  118. if(descriptionHelper.empty())
  119. {
  120. // still no description - generate placeholder one
  121. descriptionHelper.appendRawString("Unknown");
  122. }
  123. if(valueToShow != 0)
  124. {
  125. descriptionHelper.replacePositiveNumber(valueToShow);
  126. // there is one known string that uses '%s' placeholder for bonus value:
  127. // "core.arraytxt.69" : "\nFountain of Fortune Visited %s",
  128. // So also add string replacement to handle this case
  129. if (valueToShow > 0)
  130. descriptionHelper.replaceRawString(std::to_string(valueToShow));
  131. else
  132. descriptionHelper.replaceRawString("-" + std::to_string(valueToShow));
  133. if(type == BonusType::CREATURE_GROWTH_PERCENT)
  134. descriptionHelper.appendRawString(" +" + std::to_string(valueToShow));
  135. }
  136. return descriptionHelper.toString();
  137. }
  138. static JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
  139. {
  140. switch(type)
  141. {
  142. case BonusType::SPECIAL_UPGRADE:
  143. return JsonNode(ModUtility::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
  144. default:
  145. return addInfo.toJsonNode();
  146. }
  147. }
  148. JsonNode Bonus::toJsonNode() const
  149. {
  150. JsonNode root;
  151. // only add values that might reasonably be found in config files
  152. root["type"].String() = vstd::findKey(bonusNameMap, type);
  153. if(subtype != BonusSubtypeID())
  154. root["subtype"].String() = subtype.toString();
  155. if(additionalInfo != CAddInfo::NONE)
  156. root["addInfo"] = additionalInfoToJson(type, additionalInfo);
  157. if(source != BonusSource::OTHER)
  158. root["sourceType"].String() = vstd::findKey(bonusSourceMap, source);
  159. if(targetSourceType != BonusSource::OTHER)
  160. root["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetSourceType);
  161. if(sid != BonusSourceID())
  162. root["sourceID"].String() = sid.toString();
  163. if(val != 0)
  164. root["val"].Integer() = val;
  165. if(valType != BonusValueType::ADDITIVE_VALUE)
  166. root["valueType"].String() = vstd::findKey(bonusValueMap, valType);
  167. if(!stacking.empty())
  168. root["stacking"].String() = stacking;
  169. if(!description.empty())
  170. root["description"].String() = description.toString();
  171. if(effectRange != BonusLimitEffect::NO_LIMIT)
  172. root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
  173. if(duration != BonusDuration::PERMANENT)
  174. root["duration"] = BonusDuration::toJson(duration);
  175. if(turnsRemain)
  176. root["turns"].Integer() = turnsRemain;
  177. if(limiter)
  178. root["limiters"] = limiter->toJsonNode();
  179. if(updater)
  180. root["updater"] = updater->toJsonNode();
  181. if(propagator)
  182. root["propagator"].String() = vstd::findKey(bonusPropagatorMap, propagator);
  183. return root;
  184. }
  185. Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID)
  186. : Bonus(Duration, Type, Src, Val, ID, BonusSubtypeID())
  187. {}
  188. Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID, BonusSubtypeID Subtype):
  189. duration(Duration),
  190. type(Type),
  191. subtype(Subtype),
  192. source(Src),
  193. val(Val),
  194. sid(ID)
  195. {
  196. targetSourceType = BonusSource::OTHER;
  197. }
  198. Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID, BonusSubtypeID Subtype, BonusValueType ValType):
  199. duration(Duration),
  200. type(Type),
  201. subtype(Subtype),
  202. source(Src),
  203. val(Val),
  204. sid(ID),
  205. valType(ValType)
  206. {
  207. turnsRemain = 0;
  208. effectRange = BonusLimitEffect::NO_LIMIT;
  209. targetSourceType = BonusSource::OTHER;
  210. }
  211. std::shared_ptr<Bonus> Bonus::addPropagator(const TPropagatorPtr & Propagator)
  212. {
  213. propagator = Propagator;
  214. return this->shared_from_this();
  215. }
  216. DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
  217. {
  218. for(const auto & i : bonusNameMap)
  219. if(i.second == bonus.type)
  220. out << "\tType: " << i.first << " \t";
  221. #define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
  222. printField(val);
  223. out << "\tSubtype: " << bonus.subtype.toString() << "\n";
  224. printField(duration);
  225. printField(source);
  226. out << "\tSource ID: " << bonus.sid.toString() << "\n";
  227. if(bonus.additionalInfo != CAddInfo::NONE)
  228. out << "\taddInfo: " << bonus.additionalInfo.toString() << "\n";
  229. printField(turnsRemain);
  230. printField(valType);
  231. if(!bonus.stacking.empty())
  232. out << "\tstacking: \"" << bonus.stacking << "\"\n";
  233. printField(effectRange);
  234. #undef printField
  235. if(bonus.limiter)
  236. out << "\tLimiter: " << bonus.limiter->toString() << "\n";
  237. if(bonus.updater)
  238. out << "\tUpdater: " << bonus.updater->toString() << "\n";
  239. return out;
  240. }
  241. std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
  242. {
  243. if (limiter)
  244. {
  245. //If we already have limiter list, retrieve it
  246. auto limiterList = std::dynamic_pointer_cast<AllOfLimiter>(limiter);
  247. if(!limiterList)
  248. {
  249. //Create a new limiter list with old limiter and the new one will be pushed later
  250. limiterList = std::make_shared<AllOfLimiter>();
  251. limiterList->add(limiter);
  252. limiter = limiterList;
  253. }
  254. limiterList->add(Limiter);
  255. }
  256. else
  257. {
  258. limiter = Limiter;
  259. }
  260. return this->shared_from_this();
  261. }
  262. // Updaters
  263. std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
  264. {
  265. updater = Updater;
  266. return this->shared_from_this();
  267. }
  268. VCMI_LIB_NAMESPACE_END