CStackInstance.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*
  2. * CStackInstance.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 "CStackInstance.h"
  12. #include "CArmedInstance.h"
  13. #include "../../CConfigHandler.h"
  14. #include "../../GameLibrary.h"
  15. #include "../../IGameSettings.h"
  16. #include "../../callback/IGameInfoCallback.h"
  17. #include "../../entities/faction/CFaction.h"
  18. #include "../../texts/CGeneralTextHandler.h"
  19. #include "../../IBonusTypeHandler.h"
  20. #include "../../serializer/JsonSerializeFormat.h"
  21. VCMI_LIB_NAMESPACE_BEGIN
  22. CStackInstance::CStackInstance(IGameInfoCallback * cb)
  23. : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false)
  24. {
  25. }
  26. CStackInstance::CStackInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic)
  27. : CBonusSystemNode(nodeType, isHypothetic)
  28. , CStackBasicDescriptor(nullptr, 0)
  29. , CArtifactSet(cb)
  30. , GameCallbackHolder(cb)
  31. , nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE))
  32. , initiative(this, Selector::type()(BonusType::STACKS_SPEED))
  33. , totalExperience(0)
  34. {
  35. }
  36. CStackInstance::CStackInstance(IGameInfoCallback * cb, const CreatureID & id, TQuantity Count, bool isHypothetic)
  37. : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false)
  38. {
  39. setType(id);
  40. setCount(Count);
  41. }
  42. CCreature::CreatureQuantityId CStackInstance::getQuantityID() const
  43. {
  44. return CCreature::getQuantityID(getCount());
  45. }
  46. int CStackInstance::getExpRank() const
  47. {
  48. if(!LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
  49. return 0;
  50. int tier = getType()->getLevel();
  51. if(vstd::iswithin(tier, 1, 7))
  52. {
  53. for(int i = static_cast<int>(LIBRARY->creh->expRanks[tier].size()) - 2; i > -1; --i) //sic!
  54. { //exp values vary from 1st level to max exp at 11th level
  55. if(getAverageExperience() >= LIBRARY->creh->expRanks[tier][i])
  56. return ++i; //faster, but confusing - 0 index mean 1st level of experience
  57. }
  58. return 0;
  59. }
  60. else //higher tier
  61. {
  62. for(int i = static_cast<int>(LIBRARY->creh->expRanks[0].size()) - 2; i > -1; --i)
  63. {
  64. if(getAverageExperience() >= LIBRARY->creh->expRanks[0][i])
  65. return ++i;
  66. }
  67. return 0;
  68. }
  69. }
  70. int CStackInstance::getLevel() const
  71. {
  72. return std::max(1, getType()->getLevel());
  73. }
  74. void CStackInstance::giveAverageStackExperience(TExpType desiredAmountPerUnit)
  75. {
  76. if(!canGainExperience())
  77. return;
  78. int level = std::clamp(getLevel(), 1, 7);
  79. TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back();
  80. TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level] / 100);
  81. TExpType maxExperience = maxAmountPerUnit * getCount();
  82. TExpType maxExperienceToGain = maxExperience - totalExperience;
  83. TExpType actualGainedExperience = std::min(maxExperienceToGain, actualAmountPerUnit * getCount());
  84. totalExperience += actualGainedExperience;
  85. }
  86. void CStackInstance::giveTotalStackExperience(TExpType experienceToGive)
  87. {
  88. if(!canGainExperience())
  89. return;
  90. totalExperience += experienceToGive;
  91. }
  92. TExpType CStackInstance::getTotalExperience() const
  93. {
  94. return totalExperience;
  95. }
  96. TExpType CStackInstance::getAverageExperience() const
  97. {
  98. return totalExperience / getCount();
  99. }
  100. bool CStackInstance::canGainExperience() const
  101. {
  102. return cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE);
  103. }
  104. void CStackInstance::setType(const CreatureID & creID)
  105. {
  106. if(creID == CreatureID::NONE)
  107. setType(nullptr); //FIXME: unused branch?
  108. else
  109. setType(creID.toCreature());
  110. }
  111. void CStackInstance::setType(const CCreature * c)
  112. {
  113. if(getCreature())
  114. {
  115. detachFromSource(*getCreature());
  116. if(LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
  117. totalExperience = totalExperience * LIBRARY->creh->expAfterUpgrade / 100;
  118. }
  119. CStackBasicDescriptor::setType(c);
  120. if(getCreature())
  121. attachToSource(*getCreature());
  122. }
  123. void CStackInstance::setCount(TQuantity newCount)
  124. {
  125. assert(newCount >= 0);
  126. if(newCount < getCount())
  127. {
  128. TExpType averageExperience = totalExperience / getCount();
  129. totalExperience = averageExperience * newCount;
  130. }
  131. CStackBasicDescriptor::setCount(newCount);
  132. nodeHasChanged();
  133. }
  134. std::string CStackInstance::bonusToString(const std::shared_ptr<Bonus> & bonus) const
  135. {
  136. if(!bonus->description.empty())
  137. return bonus->description.toString();
  138. else
  139. return LIBRARY->getBth()->bonusToString(bonus, this);
  140. }
  141. ImagePath CStackInstance::bonusToGraphics(const std::shared_ptr<Bonus> & bonus) const
  142. {
  143. if(!bonus->customIconPath.empty())
  144. return bonus->customIconPath;
  145. return LIBRARY->getBth()->bonusToGraphics(bonus);
  146. }
  147. CArmedInstance * CStackInstance::getArmy()
  148. {
  149. return armyInstance;
  150. }
  151. const CArmedInstance * CStackInstance::getArmy() const
  152. {
  153. return armyInstance;
  154. }
  155. void CStackInstance::setArmy(CArmedInstance * ArmyObj)
  156. {
  157. auto oldArmy = getArmy();
  158. if(oldArmy)
  159. {
  160. detachFrom(*oldArmy);
  161. armyInstance = nullptr;
  162. }
  163. if(ArmyObj)
  164. {
  165. attachTo(const_cast<CArmedInstance &>(*ArmyObj));
  166. armyInstance = ArmyObj;
  167. }
  168. }
  169. std::string CStackInstance::getQuantityTXT(bool capitalized) const
  170. {
  171. CCreature::CreatureQuantityId quantity = getQuantityID();
  172. if((int)quantity)
  173. {
  174. if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
  175. return CCreature::getQuantityRangeStringForId(quantity);
  176. return LIBRARY->generaltexth->arraytxt[174 + (int)quantity * 3 - 1 - capitalized];
  177. }
  178. else
  179. return "";
  180. }
  181. bool CStackInstance::valid(bool allowUnrandomized) const
  182. {
  183. if(!randomStack)
  184. {
  185. return (getType() && getType() == getId().toEntity(LIBRARY));
  186. }
  187. else
  188. return allowUnrandomized;
  189. }
  190. std::string CStackInstance::nodeName() const
  191. {
  192. std::ostringstream oss;
  193. oss << "Stack of " << getCount() << " of ";
  194. if(getType())
  195. oss << getType()->getNamePluralTextID();
  196. else
  197. oss << "[UNDEFINED TYPE]";
  198. return oss.str();
  199. }
  200. PlayerColor CStackInstance::getOwner() const
  201. {
  202. auto army = getArmy();
  203. return army ? army->getOwner() : PlayerColor::NEUTRAL;
  204. }
  205. int32_t CStackInstance::getInitiative(int turn) const
  206. {
  207. if(turn == 0)
  208. return initiative.getValue();
  209. return ACreature::getInitiative(turn);
  210. }
  211. TerrainId CStackInstance::getNativeTerrain() const
  212. {
  213. if(nativeTerrain.hasBonus())
  214. return TerrainId::ANY_TERRAIN;
  215. return getFactionID().toEntity(LIBRARY)->getNativeTerrain();
  216. }
  217. TerrainId CStackInstance::getCurrentTerrain() const
  218. {
  219. assert(getArmy() != nullptr);
  220. return getArmy()->getCurrentTerrain();
  221. }
  222. CreatureID CStackInstance::getCreatureID() const
  223. {
  224. if(getType())
  225. return getType()->getId();
  226. else
  227. return CreatureID::NONE;
  228. }
  229. std::string CStackInstance::getName() const
  230. {
  231. return (getCount() > 1) ? getType()->getNamePluralTranslated() : getType()->getNameSingularTranslated();
  232. }
  233. ui64 CStackInstance::getPower() const
  234. {
  235. assert(getType());
  236. return static_cast<ui64>(getType()->getAIValue()) * getCount();
  237. }
  238. ui64 CStackInstance::getMarketValue() const
  239. {
  240. assert(getType());
  241. return getType()->getFullRecruitCost().marketValue() * getCount();
  242. }
  243. ArtBearer CStackInstance::bearerType() const
  244. {
  245. return ArtBearer::CREATURE;
  246. }
  247. CStackInstance::ArtPlacementMap CStackInstance::putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art)
  248. {
  249. assert(!getArt(pos));
  250. assert(art->canBePutAt(this, pos));
  251. attachToSource(*art);
  252. return CArtifactSet::putArtifact(pos, art);
  253. }
  254. void CStackInstance::removeArtifact(const ArtifactPosition & pos)
  255. {
  256. assert(getArt(pos));
  257. detachFromSource(*getArt(pos));
  258. CArtifactSet::removeArtifact(pos);
  259. }
  260. void CStackInstance::serializeJson(JsonSerializeFormat & handler)
  261. {
  262. //todo: artifacts
  263. CStackBasicDescriptor::serializeJson(handler); //must be first
  264. if(handler.saving)
  265. {
  266. if(randomStack)
  267. {
  268. int level = randomStack->level;
  269. int upgrade = randomStack->upgrade;
  270. handler.serializeInt("level", level, 0);
  271. handler.serializeInt("upgraded", upgrade, 0);
  272. }
  273. }
  274. else
  275. {
  276. //type set by CStackBasicDescriptor::serializeJson
  277. if(getType() == nullptr)
  278. {
  279. uint8_t level = 0;
  280. uint8_t upgrade = 0;
  281. handler.serializeInt("level", level, 0);
  282. handler.serializeInt("upgrade", upgrade, 0);
  283. randomStack = RandomStackInfo{level, upgrade};
  284. }
  285. }
  286. }
  287. FactionID CStackInstance::getFactionID() const
  288. {
  289. if(getType())
  290. return getType()->getFactionID();
  291. return FactionID::NEUTRAL;
  292. }
  293. const IBonusBearer * CStackInstance::getBonusBearer() const
  294. {
  295. return this;
  296. }
  297. VCMI_LIB_NAMESPACE_END