| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /*
- * CustomSpellMechanics.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
- #include "StdInc.h"
- #include "CustomSpellMechanics.h"
- #include "CDefaultSpellMechanics.h"
- #include "../battle/IBattleState.h"
- #include "../battle/CBattleInfoCallback.h"
- #include "Problem.h"
- #include "CSpellHandler.h"
- #include "../CStack.h"
- namespace spells
- {
- CustomSpellMechanics::CustomSpellMechanics(const IBattleCast * event, std::shared_ptr<effects::Effects> e)
- : DefaultSpellMechanics(event),
- effects(e)
- {}
- CustomSpellMechanics::~CustomSpellMechanics() = default;
- void CustomSpellMechanics::applyEffects(const SpellCastEnvironment * env, const Target & targets) const
- {
- auto callback = [&](const effects::Effect * e, bool & stop)
- {
- EffectTarget target = e->filterTarget(this, targets);
- e->apply(env, env->getRandomGenerator(), this, target);
- };
- effects->forEachEffect(getEffectLevel(), callback);
- }
- void CustomSpellMechanics::applyIndirectEffects(const SpellCastEnvironment * env, const Target & targets) const
- {
- auto callback = [&](const effects::Effect * e, bool & stop)
- {
- if(!e->automatic)
- {
- EffectTarget target = e->filterTarget(this, targets);
- e->apply(env, env->getRandomGenerator(), this, target);
- }
- };
- effects->forEachEffect(getEffectLevel(), callback);
- }
- void CustomSpellMechanics::applyEffectsForced(const SpellCastEnvironment * env, const Target & targets) const
- {
- auto callback = [&](const effects::Effect * e, bool & stop)
- {
- e->apply(env, env->getRandomGenerator(), this, targets);
- };
- effects->forEachEffect(getEffectLevel(), callback);
- }
- bool CustomSpellMechanics::canBeCast(Problem & problem) const
- {
- return effects->applicable(problem, this);
- }
- bool CustomSpellMechanics::canBeCastAt(BattleHex destination) const
- {
- detail::ProblemImpl problem;
- //TODO: add support for secondary targets
- //TODO: send problem to caller (for battle log message in BattleInterface)
- Target tmp;
- tmp.push_back(Destination(destination));
- Target spellTarget = transformSpellTarget(tmp);
- return effects->applicable(problem, this, tmp, spellTarget);
- }
- std::vector<const CStack *> CustomSpellMechanics::getAffectedStacks(BattleHex destination) const
- {
- Target tmp;
- tmp.push_back(Destination(destination));
- Target spellTarget = transformSpellTarget(tmp);
- EffectTarget all;
- effects->forEachEffect(getEffectLevel(), [&all, &tmp, &spellTarget, this](const effects::Effect * e, bool & stop)
- {
- EffectTarget one = e->transformTarget(this, tmp, spellTarget);
- vstd::concatenate(all, one);
- });
- std::set<const CStack *> stacks;
- for(const Destination & dest : all)
- {
- if(dest.unitValue)
- {
- //FIXME: remove and return battle::Unit
- stacks.insert(cb->battleGetStackByID(dest.unitValue->unitId(), false));
- }
- }
- std::vector<const CStack *> res;
- std::copy(stacks.begin(), stacks.end(), std::back_inserter(res));
- return res;
- }
- void CustomSpellMechanics::beforeCast(vstd::RNG & rng, const Target & target, std::vector<const CStack*> & reflected)
- {
- reflected.clear();
- affectedUnits.clear();
- Target spellTarget = transformSpellTarget(target);
- std::vector <const CStack *> resisted;
- auto rangeGen = rng.getInt64Range(0, 99);
- auto filterReflected = [&, this](const CStack * s) -> bool
- {
- const bool tryMagicMirror = mode != Mode::MAGIC_MIRROR && isNegativeSpell() && owner->level && owner->getLevelInfo(0).range == "0";
- if(tryMagicMirror)
- {
- const int mirrorChance = s->valOfBonuses(Bonus::MAGIC_MIRROR);
- if(rangeGen() < mirrorChance)
- return true;
- }
- return false;
- };
- auto filterResisted = [&, this](const CStack * s) -> bool
- {
- if(isNegativeSpell())
- {
- //magic resistance
- const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
- if(rangeGen() < prob)
- return true;
- }
- return false;
- };
- auto filterStack = [&](const battle::Unit * st)
- {
- const CStack * s = dynamic_cast<const CStack *>(st);
- if(!s)
- s = cb->battleGetStackByID(st->unitId(), false);
- if(filterResisted(s))
- resisted.push_back(s);
- else if(filterReflected(s))
- reflected.push_back(s);
- else
- affectedUnits.push_back(s);
- };
- //prepare targets
- effectsToApply = effects->prepare(this, target, spellTarget);
- std::set<const battle::Unit *> stacks = collectTargets();
- //process them
- for(auto s : stacks)
- filterStack(s);
- //and update targets
- for(auto & p : effectsToApply)
- {
- vstd::erase_if(p.second, [&](const Destination & d)
- {
- if(!d.unitValue)
- return false;
- return vstd::contains(resisted, d.unitValue) || vstd::contains(reflected, d.unitValue);
- });
- }
- for(auto s : reflected)
- addCustomEffect(s, 3);
- for(auto s : resisted)
- addCustomEffect(s, 78);
- }
- void CustomSpellMechanics::applyCastEffects(const SpellCastEnvironment * env, const Target & target) const
- {
- for(auto & p : effectsToApply)
- p.first->apply(env, env->getRandomGenerator(), this, p.second);
- }
- void CustomSpellMechanics::cast(IBattleState * battleState, vstd::RNG & rng, const Target & target)
- {
- //TODO: evaluate caster updates (mana usage etc.)
- //TODO: evaluate random values
- Target spellTarget = transformSpellTarget(target);
- effectsToApply = effects->prepare(this, target, spellTarget);
- std::set<const battle::Unit *> stacks = collectTargets();
- for(const battle::Unit * one : stacks)
- {
- auto selector = std::bind(&DefaultSpellMechanics::counteringSelector, this, _1);
- std::vector<Bonus> buffer;
- auto bl = one->getBonuses(selector);
- for(auto item : *bl)
- buffer.emplace_back(*item);
- if(!buffer.empty())
- battleState->removeUnitBonus(one->unitId(), buffer);
- }
- for(auto & p : effectsToApply)
- p.first->apply(battleState, rng, this, p.second);
- }
- std::set<const battle::Unit *> CustomSpellMechanics::collectTargets() const
- {
- std::set<const battle::Unit *> result;
- for(const auto & p : effectsToApply)
- {
- for(const Destination & d : p.second)
- if(d.unitValue)
- result.insert(d.unitValue);
- }
- return result;
- }
- Target CustomSpellMechanics::transformSpellTarget(const Target & aimPoint) const
- {
- Target spellTarget;
- if(aimPoint.size() < 1)
- {
- logGlobal->error("Aimed spell cast with no destination.");
- }
- else
- {
- const Destination & primary = aimPoint.at(0);
- BattleHex aimPoint = primary.hexValue;
- //transform primary spell target with spell range (if it`s valid), leave anything else to effects
- if(aimPoint.isValid())
- {
- auto spellRange = spellRangeInHexes(aimPoint);
- for(auto & hex : spellRange)
- spellTarget.push_back(Destination(hex));
- }
- }
- if(spellTarget.empty())
- spellTarget.push_back(Destination(BattleHex::INVALID));
- return std::move(spellTarget);
- }
- std::vector<AimType> CustomSpellMechanics::getTargetTypes() const
- {
- auto ret = DefaultSpellMechanics::getTargetTypes();
- if(!ret.empty())
- {
- effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * e, bool & stop)
- {
- e->adjustTargetTypes(ret);
- stop = ret.empty();
- });
- }
- return ret;
- }
- std::vector<Destination> CustomSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current) const
- {
- //TODO: CustomSpellMechanics::getPossibleDestinations
- if(index != 0)
- return std::vector<Destination>();
- std::vector<Destination> ret;
- switch(aimType)
- {
- case AimType::CREATURE:
- case AimType::LOCATION:
- for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
- {
- BattleHex dest(i);
- if(dest.isAvailable())
- if(canBeCastAt(dest))
- ret.emplace_back(dest);
- }
- break;
- case AimType::NO_TARGET:
- ret.emplace_back();
- break;
- default:
- break;
- }
- return ret;
- }
- std::vector<BattleHex> CustomSpellMechanics::rangeInHexes(BattleHex centralHex, bool * outDroppedHexes) const
- {
- if(isMassive() || !centralHex.isValid())
- return std::vector<BattleHex>(1, BattleHex::INVALID);
- Target aimPoint;
- aimPoint.push_back(Destination(centralHex));
- Target spellTarget = transformSpellTarget(aimPoint);
- std::set<BattleHex> effectRange;
- effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * effect, bool & stop)
- {
- if(effect->automatic)
- {
- effect->adjustAffectedHexes(effectRange, this, spellTarget);
- }
- });
- std::vector<BattleHex> ret;
- ret.reserve(effectRange.size());
- std::copy(effectRange.begin(), effectRange.end(), std::back_inserter(ret));
- return ret;
- }
- } //namespace spells
|