Reward.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. * Reward.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 "Reward.h"
  12. #include "../mapObjects/CGHeroInstance.h"
  13. #include "../serializer/JsonSerializeFormat.h"
  14. #include "../constants/StringConstants.h"
  15. #include "../CSkillHandler.h"
  16. VCMI_LIB_NAMESPACE_BEGIN
  17. void Rewardable::RewardRevealTiles::serializeJson(JsonSerializeFormat & handler)
  18. {
  19. handler.serializeBool("hide", hide);
  20. handler.serializeInt("scoreSurface", scoreSurface);
  21. handler.serializeInt("scoreSubterra", scoreSubterra);
  22. handler.serializeInt("scoreWater", scoreWater);
  23. handler.serializeInt("scoreRock", scoreRock);
  24. handler.serializeInt("radius", radius);
  25. }
  26. Rewardable::Reward::Reward()
  27. : heroExperience(0)
  28. , heroLevel(0)
  29. , manaDiff(0)
  30. , manaPercentage(-1)
  31. , manaOverflowFactor(0)
  32. , movePoints(0)
  33. , movePercentage(-1)
  34. , moveOverflowFactor(100)
  35. , primary(4, 0)
  36. , removeObject(false)
  37. , spellCast(SpellID::NONE, MasteryLevel::NONE)
  38. {
  39. }
  40. Rewardable::Reward::~Reward() = default;
  41. si32 Rewardable::Reward::calculateManaPoints(const CGHeroInstance * hero) const
  42. {
  43. si32 manaScaled = hero->mana;
  44. if (manaPercentage >= 0)
  45. manaScaled = hero->manaLimit() * manaPercentage / 100;
  46. si32 manaMissing = std::max(0, hero->manaLimit() - manaScaled);
  47. si32 manaGranted = std::min(manaMissing, manaDiff);
  48. si32 manaOverflow = manaDiff - manaGranted;
  49. si32 manaOverLimit = manaOverflow * manaOverflowFactor / 100;
  50. si32 manaOutput = manaScaled + manaGranted + manaOverLimit;
  51. return manaOutput;
  52. }
  53. si32 Rewardable::Reward::calculateMovePoints(const CGHeroInstance * hero) const
  54. {
  55. si32 moveScaled = hero->movementPointsRemaining();
  56. si32 moveLimit = hero->movementPointsLimit();
  57. if (movePercentage >= 0)
  58. moveScaled = moveLimit * movePercentage / 100;
  59. si32 moveMissing = std::max(0, moveLimit - moveScaled);
  60. si32 moveGranted = std::min(moveMissing, movePoints);
  61. si32 moveOverflow = movePoints - moveGranted;
  62. si32 moveOverLimit = moveOverflow * moveOverflowFactor / 100;
  63. si32 moveOutput = moveScaled + moveGranted + moveOverLimit;
  64. return std::max(0, moveOutput);
  65. }
  66. Component Rewardable::Reward::getDisplayedComponent(const CGHeroInstance * h) const
  67. {
  68. std::vector<Component> comps;
  69. loadComponents(comps, h);
  70. if (!comps.empty())
  71. return comps.front();
  72. // Rewardable requested component that represent such rewards, to be used as button in UI selection dialog, e.g. Chest with its experience / money pick
  73. // However reward is either completely empty OR has no rewards that target hero can receive OR these rewards have no visible component (e.g. movement)
  74. // Such cases are unreachable in H3, however can be reached by mods
  75. logMod->warn("Failed to find displayed component for reward!");
  76. return Component(ComponentType::NONE, 0);
  77. }
  78. void Rewardable::Reward::loadComponents(std::vector<Component> & comps, const CGHeroInstance * h) const
  79. {
  80. for (auto comp : extraComponents)
  81. comps.push_back(comp);
  82. for (auto & bonus : heroBonuses)
  83. {
  84. if (bonus->type == BonusType::MORALE)
  85. comps.emplace_back(ComponentType::MORALE, bonus->val);
  86. if (bonus->type == BonusType::LUCK)
  87. comps.emplace_back(ComponentType::LUCK, bonus->val);
  88. }
  89. if (heroExperience)
  90. comps.emplace_back(ComponentType::EXPERIENCE, static_cast<si32>(h ? h->calculateXp(heroExperience) : heroExperience));
  91. if (heroLevel)
  92. comps.emplace_back(ComponentType::LEVEL, heroLevel);
  93. if (manaDiff || manaPercentage >= 0)
  94. comps.emplace_back(ComponentType::MANA, h ? (calculateManaPoints(h) - h->mana) : manaDiff);
  95. for (size_t i=0; i<primary.size(); i++)
  96. {
  97. if (primary[i] != 0)
  98. comps.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill(i), primary[i]);
  99. }
  100. for(const auto & entry : secondary)
  101. {
  102. auto skillID = entry.first;
  103. int levelsGained = entry.second;
  104. int currentLevel = h ? h->getSecSkillLevel(skillID) : 0;
  105. int finalLevel = std::clamp<int>(currentLevel + levelsGained, MasteryLevel::NONE, MasteryLevel::EXPERT);
  106. if (finalLevel == MasteryLevel::NONE)
  107. comps.emplace_back(ComponentType::SEC_SKILL, entry.first);
  108. else
  109. comps.emplace_back(ComponentType::SEC_SKILL, entry.first, finalLevel);
  110. }
  111. for(const auto & entry : grantedArtifacts)
  112. comps.emplace_back(ComponentType::ARTIFACT, entry);
  113. for(const auto & entry : takenArtifacts)
  114. comps.emplace_back(ComponentType::ARTIFACT, entry);
  115. for(const auto & entry : takenArtifactSlots)
  116. {
  117. if (h)
  118. {
  119. const auto & slotContent = h->getSlot(entry);
  120. if (slotContent->artifactID.hasValue())
  121. comps.emplace_back(ComponentType::ARTIFACT, slotContent->getArt()->getTypeId());
  122. }
  123. }
  124. for(const SpellID & spell : grantedScrolls)
  125. comps.emplace_back(ComponentType::SPELL, spell);
  126. for(const SpellID & spell : takenScrolls)
  127. comps.emplace_back(ComponentType::SPELL, spell);
  128. for(const auto & entry : spells)
  129. {
  130. bool learnable = !h || h->canLearnSpell(entry.toEntity(LIBRARY), true);
  131. comps.emplace_back(ComponentType::SPELL, entry, learnable ? 0 : -1);
  132. }
  133. for(const auto & entry : creatures)
  134. comps.emplace_back(ComponentType::CREATURE, entry.getId(), entry.getCount());
  135. for (size_t i=0; i<resources.size(); i++)
  136. {
  137. if (resources[i] !=0)
  138. comps.emplace_back(ComponentType::RESOURCE, GameResID(i), resources[i]);
  139. }
  140. }
  141. void Rewardable::Reward::serializeJson(JsonSerializeFormat & handler)
  142. {
  143. resources.serializeJson(handler, "resources");
  144. handler.serializeBool("removeObject", removeObject);
  145. handler.serializeInt("manaPercentage", manaPercentage);
  146. handler.serializeInt("movePercentage", movePercentage);
  147. handler.serializeInt("heroExperience", heroExperience);
  148. handler.serializeInt("heroLevel", heroLevel);
  149. handler.serializeInt("manaDiff", manaDiff);
  150. handler.serializeInt("manaOverflowFactor", manaOverflowFactor);
  151. handler.serializeInt("movePoints", movePoints);
  152. handler.serializeInt("moveOverflowFactor", manaOverflowFactor);
  153. handler.serializeIdArray("artifacts", grantedArtifacts);
  154. handler.serializeIdArray("takenArtifacts", takenArtifacts);
  155. handler.serializeIdArray("takenArtifactSlots", takenArtifactSlots);
  156. handler.serializeIdArray("scrolls", grantedScrolls);
  157. handler.serializeIdArray("takenScrolls", takenScrolls);
  158. handler.serializeIdArray("spells", spells);
  159. handler.enterArray("creatures").serializeStruct(creatures);
  160. handler.enterArray("primary").serializeArray(primary);
  161. {
  162. auto a = handler.enterArray("secondary");
  163. std::vector<std::pair<SecondarySkill, si32>> fieldValue(secondary.begin(), secondary.end());
  164. a.serializeStruct<std::pair<SecondarySkill, si32>>(fieldValue, [](JsonSerializeFormat & h, std::pair<SecondarySkill, si32> & e)
  165. {
  166. h.serializeId("skill", e.first);
  167. h.serializeId("level", e.second, 0, [](const std::string & i){return vstd::find_pos(NSecondarySkill::levels, i);}, [](si32 i){return NSecondarySkill::levels.at(i);});
  168. });
  169. a.syncSize(fieldValue);
  170. secondary = std::map<SecondarySkill, si32>(fieldValue.begin(), fieldValue.end());
  171. }
  172. {
  173. auto a = handler.enterArray("creaturesChange");
  174. std::vector<std::pair<CreatureID, CreatureID>> fieldValue(creaturesChange.begin(), creaturesChange.end());
  175. a.serializeStruct<std::pair<CreatureID, CreatureID>>(fieldValue, [](JsonSerializeFormat & h, std::pair<CreatureID, CreatureID> & e)
  176. {
  177. h.serializeId("creature", e.first, CreatureID{});
  178. h.serializeId("amount", e.second, CreatureID{});
  179. });
  180. creaturesChange = std::map<CreatureID, CreatureID>(fieldValue.begin(), fieldValue.end());
  181. }
  182. {
  183. auto a = handler.enterStruct("spellCast");
  184. a->serializeId("spell", spellCast.first, SpellID{});
  185. a->serializeInt("level", spellCast.second);
  186. }
  187. }
  188. VCMI_LIB_NAMESPACE_END