AdventureSpellMechanics.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * AdventureSpellMechanics.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 "AdventureSpellMechanics.h"
  12. #include "AdventureSpellEffect.h"
  13. #include "DimensionDoorEffect.h"
  14. #include "RemoveObjectEffect.h"
  15. #include "SummonBoatEffect.h"
  16. #include "TownPortalEffect.h"
  17. #include "ViewWorldEffect.h"
  18. #include "../CSpellHandler.h"
  19. #include "../Problem.h"
  20. #include "../../json/JsonBonus.h"
  21. #include "../../mapObjects/CGHeroInstance.h"
  22. #include "../../networkPacks/PacksForClient.h"
  23. #include "../../callback/IGameInfoCallback.h"
  24. VCMI_LIB_NAMESPACE_BEGIN
  25. std::unique_ptr<IAdventureSpellEffect> AdventureSpellMechanics::createAdventureEffect(const CSpell * s, const JsonNode & node)
  26. {
  27. const std::string & typeID = node["type"].String();
  28. if(typeID == "generic")
  29. return std::make_unique<AdventureSpellEffect>();
  30. if(typeID == "dimensionDoor")
  31. return std::make_unique<DimensionDoorEffect>(s, node);
  32. if(typeID == "removeObject")
  33. return std::make_unique<RemoveObjectEffect>(s, node);
  34. if(typeID == "summonBoat")
  35. return std::make_unique<SummonBoatEffect>(s, node);
  36. if(typeID == "townPortal")
  37. return std::make_unique<TownPortalEffect>(s, node);
  38. if(typeID == "viewWorld")
  39. return std::make_unique<ViewWorldEffect>(s, node);
  40. return std::make_unique<AdventureSpellEffect>();
  41. }
  42. AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s)
  43. : IAdventureSpellMechanics(s)
  44. {
  45. for(int level = 0; level < GameConstants::SPELL_SCHOOL_LEVELS; level++)
  46. {
  47. const JsonNode & config = s->getLevelInfo(level).adventureEffect;
  48. levelOptions[level].effect = createAdventureEffect(s, config);
  49. levelOptions[level].castsPerDay = config["castsPerDay"].Integer();
  50. levelOptions[level].castsPerDayXL = config["castsPerDayXL"].Integer();
  51. levelOptions[level].bonuses = s->getLevelInfo(level).effects;
  52. for(const auto & elem : config["bonuses"].Struct())
  53. {
  54. auto b = JsonUtils::parseBonus(elem.second);
  55. b->sid = BonusSourceID(s->id);
  56. b->source = BonusSource::SPELL_EFFECT;
  57. levelOptions[level].bonuses.push_back(b);
  58. }
  59. }
  60. }
  61. AdventureSpellMechanics::~AdventureSpellMechanics() = default;
  62. const AdventureSpellMechanics::LevelOptions & AdventureSpellMechanics::getLevel(const spells::Caster * caster) const
  63. {
  64. int schoolLevel = caster->getSpellSchoolLevel(owner);
  65. return levelOptions.at(schoolLevel);
  66. }
  67. const IAdventureSpellEffect * AdventureSpellMechanics::getEffect(const spells::Caster * caster) const
  68. {
  69. return getLevel(caster).effect.get();
  70. }
  71. bool AdventureSpellMechanics::givesBonus(const spells::Caster * caster, BonusType which) const
  72. {
  73. for (const auto & bonus : getLevel(caster).bonuses)
  74. if (bonus->type == which)
  75. return true;
  76. return false;
  77. }
  78. bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const
  79. {
  80. if(!owner->isAdventure())
  81. return false;
  82. const auto * heroCaster = dynamic_cast<const CGHeroInstance *>(caster);
  83. if(heroCaster)
  84. {
  85. if(heroCaster->isGarrisoned())
  86. return false;
  87. const auto level = heroCaster->getSpellSchoolLevel(owner);
  88. const auto cost = owner->getCost(level);
  89. if(!heroCaster->canCastThisSpell(owner))
  90. return false;
  91. if(heroCaster->mana < cost)
  92. return false;
  93. std::stringstream cachingStr;
  94. cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << owner->id.num;
  95. int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), cachingStr.str())->size();
  96. int3 mapSize = cb->getMapSize();
  97. bool mapSizeIsAtLeastXL = mapSize.x * mapSize.y * mapSize.z >= GameConstants::TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD;
  98. bool useAlternativeLimit = mapSizeIsAtLeastXL && getLevel(caster).castsPerDayXL != 0;
  99. int castsLimit = useAlternativeLimit ? getLevel(caster).castsPerDayXL : getLevel(caster).castsPerDay;
  100. if(castsLimit > 0 && castsLimit <= castsAlreadyPerformedThisTurn ) //limit casts per turn
  101. {
  102. MetaString message = MetaString::createFromTextID("core.genrltxt.338");
  103. caster->getCasterName(message);
  104. problem.add(std::move(message));
  105. return false;
  106. }
  107. }
  108. return getLevel(caster).effect->canBeCastImpl(problem, cb, caster);
  109. }
  110. bool AdventureSpellMechanics::canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
  111. {
  112. return canBeCast(problem, cb, caster) && getLevel(caster).effect->canBeCastAtImpl(problem, cb, caster, pos);
  113. }
  114. bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  115. {
  116. spells::detail::ProblemImpl problem;
  117. if(!canBeCastAt(problem, env->getCb(), parameters.caster, parameters.pos))
  118. return false;
  119. ESpellCastResult result = getLevel(parameters.caster).effect->beginCast(env, parameters, *this);
  120. if(result == ESpellCastResult::OK)
  121. performCast(env, parameters);
  122. return result != ESpellCastResult::ERROR;
  123. }
  124. void AdventureSpellMechanics::giveBonuses(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  125. {
  126. for(const auto & b : getLevel(parameters.caster).bonuses)
  127. {
  128. GiveBonus gb;
  129. gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
  130. gb.bonus = *b;
  131. gb.bonus.duration = parameters.caster->getEnchantPower(owner);
  132. env->apply(gb);
  133. }
  134. GiveBonus gb;
  135. gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
  136. gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id));
  137. env->apply(gb);
  138. }
  139. void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  140. {
  141. const auto level = parameters.caster->getSpellSchoolLevel(owner);
  142. const auto cost = owner->getCost(level);
  143. AdvmapSpellCast asc;
  144. asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId());
  145. asc.spellID = owner->id;
  146. env->apply(asc);
  147. ESpellCastResult result = getLevel(parameters.caster).effect->applyAdventureEffects(env, parameters);
  148. if (result == ESpellCastResult::OK)
  149. {
  150. giveBonuses(env, parameters);
  151. parameters.caster->spendMana(env, cost);
  152. getLevel(parameters.caster).effect->endCast(env, parameters);
  153. }
  154. }
  155. VCMI_LIB_NAMESPACE_END