Bonus.cpp 7.8 KB

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