DemonSummon.cpp 3.7 KB

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