DemonSummon.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /*
  2. * DemonSummon.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 "DemonSummon.h"
  12. #include "Registry.h"
  13. #include "../ISpellMechanics.h"
  14. #include "../../battle/CBattleInfoCallback.h"
  15. #include "../../battle/BattleInfo.h"
  16. #include "../../battle/CUnitState.h"
  17. #include "../../networkPacks/PacksForClientBattle.h"
  18. #include "../../serializer/JsonSerializeFormat.h"
  19. VCMI_LIB_NAMESPACE_BEGIN
  20. namespace spells
  21. {
  22. namespace effects
  23. {
  24. int DemonSummon::raisedCreatureAmount(const Mechanics * m, const battle::Unit * unit) const
  25. {
  26. if(!unit || unit->alive() || unit->isGhost())
  27. return 0;
  28. const auto *creatureType = creature.toEntity(m->creatures());
  29. int32_t deadCount = unit->unitBaseAmount();
  30. int32_t deadTotalHealth = unit->getTotalHealth();
  31. int32_t raisedMaxHealth = creatureType->getMaxHealth();
  32. int32_t raisedTotalHealth = m->applySpellBonus(m->getEffectValue(), unit);
  33. // Can't raise stack with more HP than original stack
  34. int32_t maxAmountFromHealth = deadTotalHealth / raisedMaxHealth;
  35. // Can't raise stack with more creatures than original stack
  36. int32_t maxAmountFromAmount = deadCount;
  37. // Can't raise stack with more HP than our spellpower
  38. int32_t maxAmountFromSpellpower = raisedTotalHealth / raisedMaxHealth;
  39. int32_t finalAmount = std::min( { maxAmountFromHealth, maxAmountFromAmount, maxAmountFromSpellpower } );
  40. return finalAmount;
  41. }
  42. void DemonSummon::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const
  43. {
  44. BattleUnitsChanged pack;
  45. pack.battleID = m->battle()->getBattle()->getBattleID();
  46. for(const Destination & dest : target)
  47. {
  48. const battle::Unit * targetStack = dest.unitValue;
  49. //we shall have all targets to be stacks
  50. if(!targetStack || targetStack->alive() || targetStack->isGhost())
  51. {
  52. server->complain("No corpse to demonize! Invalid effect target transformation.");
  53. continue;
  54. }
  55. auto hex = m->battle()->getAvailableHex(targetStack->creatureId(), m->casterSide, targetStack->getPosition().toInt());
  56. if(!hex.isValid())
  57. {
  58. server->complain("No place to put new summon!");
  59. break;
  60. }
  61. int32_t finalAmount = raisedCreatureAmount(m, targetStack);
  62. if(finalAmount < 1)
  63. {
  64. server->complain("Summoning didn't summon any!");
  65. continue;
  66. }
  67. battle::UnitInfo info;
  68. info.id = m->battle()->battleNextUnitId();
  69. info.count = finalAmount;
  70. info.type = creature;
  71. info.side = m->casterSide;
  72. info.position = dest.hexValue;
  73. info.summoned = !permanent;
  74. // add newly created creature
  75. pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
  76. info.save(pack.changedStacks.back().data);
  77. // and remove corpse to prevent second raising or resurrection
  78. pack.changedStacks.emplace_back(targetStack->unitId(), UnitChanges::EOperation::REMOVE);
  79. }
  80. if(!pack.changedStacks.empty())
  81. server->apply(pack);
  82. }
  83. bool DemonSummon::isValidTarget(const Mechanics * m, const battle::Unit * unit) const
  84. {
  85. if(!unit->isDead())
  86. return false;
  87. //check if alive unit blocks rising
  88. for(const BattleHex & hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide()))
  89. {
  90. auto blocking = m->battle()->battleGetUnitsIf([hex, unit](const battle::Unit * other)
  91. {
  92. return other->isValidTarget(false) && other->coversPos(hex) && other != unit;
  93. });
  94. if(!blocking.empty())
  95. return false;
  96. }
  97. if (unit->isGhost())
  98. return false;
  99. if (raisedCreatureAmount(m, unit) == 0)
  100. return false;
  101. return m->isReceptive(unit);
  102. }
  103. void DemonSummon::serializeJsonUnitEffect(JsonSerializeFormat & handler)
  104. {
  105. handler.serializeId("id", creature, CreatureID());
  106. handler.serializeBool("permanent", permanent, false);
  107. }
  108. }
  109. }
  110. VCMI_LIB_NAMESPACE_END