Damage.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * Damage.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 "Damage.h"
  12. #include "Registry.h"
  13. #include "../ISpellMechanics.h"
  14. #include "../../NetPacks.h"
  15. #include "../../CStack.h"
  16. #include "../../battle/IBattleState.h"
  17. #include "../../battle/CBattleInfoCallback.h"
  18. #include "../../CGeneralTextHandler.h"
  19. #include "../../serializer/JsonSerializeFormat.h"
  20. VCMI_LIB_NAMESPACE_BEGIN
  21. namespace spells
  22. {
  23. namespace effects
  24. {
  25. void Damage::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const
  26. {
  27. StacksInjured stacksInjured;
  28. BattleLogMessage blm;
  29. size_t targetIndex = 0;
  30. const battle::Unit * firstTarget = nullptr;
  31. const bool describe = server->describeChanges();
  32. int64_t damageToDisplay = 0;
  33. uint32_t killed = 0;
  34. bool multiple = false;
  35. for(const auto & t : target)
  36. {
  37. const battle::Unit * unit = t.unitValue;
  38. if(unit && unit->alive())
  39. {
  40. BattleStackAttacked bsa;
  41. bsa.damageAmount = damageForTarget(targetIndex, m, unit);
  42. bsa.stackAttacked = unit->unitId();
  43. bsa.attackerID = -1;
  44. auto newState = unit->acquireState();
  45. CStack::prepareAttacked(bsa, *server->getRNG(), newState);
  46. if(describe)
  47. {
  48. if(!firstTarget)
  49. firstTarget = unit;
  50. else
  51. multiple = true;
  52. damageToDisplay += bsa.damageAmount;
  53. killed += bsa.killedAmount;
  54. }
  55. stacksInjured.stacks.push_back(bsa);
  56. }
  57. targetIndex++;
  58. }
  59. if(describe && firstTarget && damageToDisplay > 0)
  60. {
  61. describeEffect(blm.lines, m, firstTarget, killed, damageToDisplay, multiple);
  62. }
  63. if(!stacksInjured.stacks.empty())
  64. server->apply(&stacksInjured);
  65. if(!blm.lines.empty())
  66. server->apply(&blm);
  67. }
  68. bool Damage::isReceptive(const Mechanics * m, const battle::Unit * unit) const
  69. {
  70. if(!UnitEffect::isReceptive(m, unit))
  71. return false;
  72. //elemental immunity for damage
  73. auto filter = m->getElementalImmunity();
  74. for(auto element : filter)
  75. {
  76. if(!m->isPositiveSpell() && unit->hasBonusOfType(element, 2))
  77. return false;
  78. }
  79. return true;
  80. }
  81. void Damage::serializeJsonUnitEffect(JsonSerializeFormat & handler)
  82. {
  83. handler.serializeBool("killByPercentage", killByPercentage);
  84. handler.serializeBool("killByCount", killByCount);
  85. }
  86. int64_t Damage::damageForTarget(size_t targetIndex, const Mechanics * m, const battle::Unit * target) const
  87. {
  88. int64_t baseDamage;
  89. if(killByPercentage)
  90. {
  91. int64_t amountToKill = target->getCount() * m->getEffectValue() / 100;
  92. baseDamage = amountToKill * target->MaxHealth();
  93. }
  94. else if(killByCount)
  95. {
  96. baseDamage = m->getEffectValue() * target->MaxHealth();
  97. }
  98. else
  99. {
  100. baseDamage = m->adjustEffectValue(target);
  101. }
  102. if(chainLength > 1 && targetIndex > 0)
  103. {
  104. double indexedFactor = std::pow(chainFactor, static_cast<double>(targetIndex));
  105. return static_cast<int64_t>(indexedFactor * baseDamage);
  106. }
  107. return baseDamage;
  108. }
  109. void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m, const battle::Unit * firstTarget, uint32_t kills, int64_t damage, bool multiple) const
  110. {
  111. if(m->getSpellIndex() == SpellID::DEATH_STARE && !multiple)
  112. {
  113. MetaString line;
  114. if(kills > 1)
  115. {
  116. line.addTxt(MetaString::GENERAL_TXT, 119); //%d %s die under the terrible gaze of the %s.
  117. line.addReplacement(kills);
  118. firstTarget->addNameReplacement(line, true);
  119. }
  120. else
  121. {
  122. line.addTxt(MetaString::GENERAL_TXT, 118); //One %s dies under the terrible gaze of the %s.
  123. firstTarget->addNameReplacement(line, false);
  124. }
  125. m->caster->getCasterName(line);
  126. log.push_back(line);
  127. }
  128. else if(m->getSpellIndex() == SpellID::THUNDERBOLT && !multiple)
  129. {
  130. {
  131. MetaString line;
  132. firstTarget->addText(line, MetaString::GENERAL_TXT, -367, true);
  133. firstTarget->addNameReplacement(line, true);
  134. log.push_back(line);
  135. }
  136. {
  137. MetaString line;
  138. //todo: handle newlines in metastring
  139. std::string text = VLC->generaltexth->allTexts[343]; //Does %d points of damage.
  140. boost::algorithm::trim(text);
  141. line << text;
  142. line.addReplacement(static_cast<int>(damage)); //no more text afterwards
  143. log.push_back(line);
  144. }
  145. }
  146. else
  147. {
  148. {
  149. MetaString line;
  150. line.addTxt(MetaString::GENERAL_TXT, 376); // Spell %s does %d damage
  151. line.addReplacement(MetaString::SPELL_NAME, m->getSpellIndex());
  152. line.addReplacement(static_cast<int>(damage));
  153. log.push_back(line);
  154. }
  155. if (kills > 0)
  156. {
  157. MetaString line;
  158. if(kills > 1)
  159. {
  160. line.addTxt(MetaString::GENERAL_TXT, 379); // %d %s perishes
  161. line.addReplacement(kills);
  162. if(multiple)
  163. line.addReplacement(MetaString::GENERAL_TXT, 43); // creatures
  164. else
  165. firstTarget->addNameReplacement(line, true);
  166. }
  167. else // single creature killed
  168. {
  169. line.addTxt(MetaString::GENERAL_TXT, 378); // one %s perishes
  170. if(multiple)
  171. line.addReplacement(MetaString::GENERAL_TXT, 42); // creature
  172. else
  173. firstTarget->addNameReplacement(line, false);
  174. }
  175. log.push_back(line);
  176. }
  177. }
  178. }
  179. }
  180. }
  181. VCMI_LIB_NAMESPACE_END