CustomSpellMechanics.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * CustomSpellMechanics.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 "CustomSpellMechanics.h"
  12. #include "CDefaultSpellMechanics.h"
  13. #include "../battle/IBattleState.h"
  14. #include "../battle/CBattleInfoCallback.h"
  15. #include "Problem.h"
  16. #include "CSpellHandler.h"
  17. #include "../CStack.h"
  18. namespace spells
  19. {
  20. CustomSpellMechanics::CustomSpellMechanics(const IBattleCast * event, std::shared_ptr<effects::Effects> e)
  21. : DefaultSpellMechanics(event),
  22. effects(e)
  23. {}
  24. CustomSpellMechanics::~CustomSpellMechanics() = default;
  25. void CustomSpellMechanics::applyEffects(const SpellCastEnvironment * env, const Target & targets) const
  26. {
  27. auto callback = [&](const effects::Effect * e, bool & stop)
  28. {
  29. EffectTarget target = e->filterTarget(this, targets);
  30. e->apply(env, env->getRandomGenerator(), this, target);
  31. };
  32. effects->forEachEffect(getEffectLevel(), callback);
  33. }
  34. void CustomSpellMechanics::applyIndirectEffects(const SpellCastEnvironment * env, const Target & targets) const
  35. {
  36. auto callback = [&](const effects::Effect * e, bool & stop)
  37. {
  38. if(!e->automatic)
  39. {
  40. EffectTarget target = e->filterTarget(this, targets);
  41. e->apply(env, env->getRandomGenerator(), this, target);
  42. }
  43. };
  44. effects->forEachEffect(getEffectLevel(), callback);
  45. }
  46. void CustomSpellMechanics::applyEffectsForced(const SpellCastEnvironment * env, const Target & targets) const
  47. {
  48. auto callback = [&](const effects::Effect * e, bool & stop)
  49. {
  50. e->apply(env, env->getRandomGenerator(), this, targets);
  51. };
  52. effects->forEachEffect(getEffectLevel(), callback);
  53. }
  54. bool CustomSpellMechanics::canBeCast(Problem & problem) const
  55. {
  56. return effects->applicable(problem, this);
  57. }
  58. bool CustomSpellMechanics::canBeCastAt(BattleHex destination) const
  59. {
  60. detail::ProblemImpl problem;
  61. //TODO: add support for secondary targets
  62. //TODO: send problem to caller (for battle log message in BattleInterface)
  63. Target tmp;
  64. tmp.push_back(Destination(destination));
  65. Target spellTarget = transformSpellTarget(tmp);
  66. return effects->applicable(problem, this, tmp, spellTarget);
  67. }
  68. std::vector<const CStack *> CustomSpellMechanics::getAffectedStacks(BattleHex destination) const
  69. {
  70. Target tmp;
  71. tmp.push_back(Destination(destination));
  72. Target spellTarget = transformSpellTarget(tmp);
  73. EffectTarget all;
  74. effects->forEachEffect(getEffectLevel(), [&all, &tmp, &spellTarget, this](const effects::Effect * e, bool & stop)
  75. {
  76. EffectTarget one = e->transformTarget(this, tmp, spellTarget);
  77. vstd::concatenate(all, one);
  78. });
  79. std::set<const CStack *> stacks;
  80. for(const Destination & dest : all)
  81. {
  82. if(dest.unitValue)
  83. {
  84. //FIXME: remove and return battle::Unit
  85. stacks.insert(cb->battleGetStackByID(dest.unitValue->unitId(), false));
  86. }
  87. }
  88. std::vector<const CStack *> res;
  89. std::copy(stacks.begin(), stacks.end(), std::back_inserter(res));
  90. return res;
  91. }
  92. void CustomSpellMechanics::beforeCast(vstd::RNG & rng, const Target & target, std::vector<const CStack*> & reflected)
  93. {
  94. reflected.clear();
  95. affectedUnits.clear();
  96. Target spellTarget = transformSpellTarget(target);
  97. std::vector <const CStack *> resisted;
  98. auto rangeGen = rng.getInt64Range(0, 99);
  99. auto filterReflected = [&, this](const CStack * s) -> bool
  100. {
  101. const bool tryMagicMirror = mode != Mode::MAGIC_MIRROR && isNegativeSpell() && owner->level && owner->getLevelInfo(0).range == "0";
  102. if(tryMagicMirror)
  103. {
  104. const int mirrorChance = s->valOfBonuses(Bonus::MAGIC_MIRROR);
  105. if(rangeGen() < mirrorChance)
  106. return true;
  107. }
  108. return false;
  109. };
  110. auto filterResisted = [&, this](const CStack * s) -> bool
  111. {
  112. if(isNegativeSpell())
  113. {
  114. //magic resistance
  115. const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
  116. if(rangeGen() < prob)
  117. return true;
  118. }
  119. return false;
  120. };
  121. auto filterStack = [&](const battle::Unit * st)
  122. {
  123. const CStack * s = dynamic_cast<const CStack *>(st);
  124. if(!s)
  125. s = cb->battleGetStackByID(st->unitId(), false);
  126. if(filterResisted(s))
  127. resisted.push_back(s);
  128. else if(filterReflected(s))
  129. reflected.push_back(s);
  130. else
  131. affectedUnits.push_back(s);
  132. };
  133. //prepare targets
  134. effectsToApply = effects->prepare(this, target, spellTarget);
  135. std::set<const battle::Unit *> stacks = collectTargets();
  136. //process them
  137. for(auto s : stacks)
  138. filterStack(s);
  139. //and update targets
  140. for(auto & p : effectsToApply)
  141. {
  142. vstd::erase_if(p.second, [&](const Destination & d)
  143. {
  144. if(!d.unitValue)
  145. return false;
  146. return vstd::contains(resisted, d.unitValue) || vstd::contains(reflected, d.unitValue);
  147. });
  148. }
  149. for(auto s : reflected)
  150. addCustomEffect(s, 3);
  151. for(auto s : resisted)
  152. addCustomEffect(s, 78);
  153. }
  154. void CustomSpellMechanics::applyCastEffects(const SpellCastEnvironment * env, const Target & target) const
  155. {
  156. for(auto & p : effectsToApply)
  157. p.first->apply(env, env->getRandomGenerator(), this, p.second);
  158. }
  159. void CustomSpellMechanics::cast(IBattleState * battleState, vstd::RNG & rng, const Target & target)
  160. {
  161. //TODO: evaluate caster updates (mana usage etc.)
  162. //TODO: evaluate random values
  163. Target spellTarget = transformSpellTarget(target);
  164. effectsToApply = effects->prepare(this, target, spellTarget);
  165. std::set<const battle::Unit *> stacks = collectTargets();
  166. for(const battle::Unit * one : stacks)
  167. {
  168. auto selector = std::bind(&DefaultSpellMechanics::counteringSelector, this, _1);
  169. std::vector<Bonus> buffer;
  170. auto bl = one->getBonuses(selector);
  171. for(auto item : *bl)
  172. buffer.emplace_back(*item);
  173. if(!buffer.empty())
  174. battleState->removeUnitBonus(one->unitId(), buffer);
  175. }
  176. for(auto & p : effectsToApply)
  177. p.first->apply(battleState, rng, this, p.second);
  178. }
  179. std::set<const battle::Unit *> CustomSpellMechanics::collectTargets() const
  180. {
  181. std::set<const battle::Unit *> result;
  182. for(const auto & p : effectsToApply)
  183. {
  184. for(const Destination & d : p.second)
  185. if(d.unitValue)
  186. result.insert(d.unitValue);
  187. }
  188. return result;
  189. }
  190. Target CustomSpellMechanics::transformSpellTarget(const Target & aimPoint) const
  191. {
  192. Target spellTarget;
  193. if(aimPoint.size() < 1)
  194. {
  195. logGlobal->error("Aimed spell cast with no destination.");
  196. }
  197. else
  198. {
  199. const Destination & primary = aimPoint.at(0);
  200. BattleHex aimPoint = primary.hexValue;
  201. //transform primary spell target with spell range (if it`s valid), leave anything else to effects
  202. if(aimPoint.isValid())
  203. {
  204. auto spellRange = spellRangeInHexes(aimPoint);
  205. for(auto & hex : spellRange)
  206. spellTarget.push_back(Destination(hex));
  207. }
  208. }
  209. if(spellTarget.empty())
  210. spellTarget.push_back(Destination(BattleHex::INVALID));
  211. return std::move(spellTarget);
  212. }
  213. std::vector<AimType> CustomSpellMechanics::getTargetTypes() const
  214. {
  215. auto ret = DefaultSpellMechanics::getTargetTypes();
  216. if(!ret.empty())
  217. {
  218. effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * e, bool & stop)
  219. {
  220. e->adjustTargetTypes(ret);
  221. stop = ret.empty();
  222. });
  223. }
  224. return ret;
  225. }
  226. std::vector<Destination> CustomSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current) const
  227. {
  228. //TODO: CustomSpellMechanics::getPossibleDestinations
  229. if(index != 0)
  230. return std::vector<Destination>();
  231. std::vector<Destination> ret;
  232. switch(aimType)
  233. {
  234. case AimType::CREATURE:
  235. case AimType::LOCATION:
  236. for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
  237. {
  238. BattleHex dest(i);
  239. if(dest.isAvailable())
  240. if(canBeCastAt(dest))
  241. ret.emplace_back(dest);
  242. }
  243. break;
  244. case AimType::NO_TARGET:
  245. ret.emplace_back();
  246. break;
  247. default:
  248. break;
  249. }
  250. return ret;
  251. }
  252. std::vector<BattleHex> CustomSpellMechanics::rangeInHexes(BattleHex centralHex, bool * outDroppedHexes) const
  253. {
  254. if(isMassive() || !centralHex.isValid())
  255. return std::vector<BattleHex>(1, BattleHex::INVALID);
  256. Target aimPoint;
  257. aimPoint.push_back(Destination(centralHex));
  258. Target spellTarget = transformSpellTarget(aimPoint);
  259. std::set<BattleHex> effectRange;
  260. effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * effect, bool & stop)
  261. {
  262. if(effect->automatic)
  263. {
  264. effect->adjustAffectedHexes(effectRange, this, spellTarget);
  265. }
  266. });
  267. std::vector<BattleHex> ret;
  268. ret.reserve(effectRange.size());
  269. std::copy(effectRange.begin(), effectRange.end(), std::back_inserter(ret));
  270. return ret;
  271. }
  272. } //namespace spells