diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp deleted file mode 100644 index ed967674a..000000000 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * CDefaultSpellMechanics.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 "CDefaultSpellMechanics.h" - -#include "CSpellHandler.h" - -#include "../CStack.h" -#include "../battle/BattleInfo.h" - -#include "../CGeneralTextHandler.h" - -namespace spells -{ -namespace SRSLPraserHelpers -{ - static int XYToHex(int x, int y) - { - return x + GameConstants::BFIELD_WIDTH * y; - } - - static int XYToHex(std::pair xy) - { - return XYToHex(xy.first, xy.second); - } - - static int hexToY(int battleFieldPosition) - { - return battleFieldPosition/GameConstants::BFIELD_WIDTH; - } - - static int hexToX(int battleFieldPosition) - { - int pos = battleFieldPosition - hexToY(battleFieldPosition) * GameConstants::BFIELD_WIDTH; - return pos; - } - - static std::pair hexToPair(int battleFieldPosition) - { - return std::make_pair(hexToX(battleFieldPosition), hexToY(battleFieldPosition)); - } - - //moves hex by one hex in given direction - //0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left - static std::pair gotoDir(int x, int y, int direction) - { - switch(direction) - { - case 0: //top left - return std::make_pair((y%2) ? x-1 : x, y-1); - case 1: //top right - return std::make_pair((y%2) ? x : x+1, y-1); - case 2: //right - return std::make_pair(x+1, y); - case 3: //right bottom - return std::make_pair((y%2) ? x : x+1, y+1); - case 4: //left bottom - return std::make_pair((y%2) ? x-1 : x, y+1); - case 5: //left - return std::make_pair(x-1, y); - default: - throw std::runtime_error("Disaster: wrong direction in SRSLPraserHelpers::gotoDir!\n"); - } - } - - static std::pair gotoDir(std::pair xy, int direction) - { - return gotoDir(xy.first, xy.second, direction); - } - - static bool isGoodHex(std::pair xy) - { - return xy.first >=0 && xy.first < GameConstants::BFIELD_WIDTH && xy.second >= 0 && xy.second < GameConstants::BFIELD_HEIGHT; - } - - //helper function for rangeInHexes - static std::set getInRange(unsigned int center, int low, int high) - { - std::set ret; - if(low == 0) - { - ret.insert(center); - } - - std::pair mainPointForLayer[6]; //A, B, C, D, E, F points - for(auto & elem : mainPointForLayer) - elem = hexToPair(center); - - for(int it=1; it<=high; ++it) //it - distance to the center - { - for(int b=0; b<6; ++b) - mainPointForLayer[b] = gotoDir(mainPointForLayer[b], b); - - if(it>=low) - { - std::pair curHex; - - //adding lines (A-b, B-c, C-d, etc) - for(int v=0; v<6; ++v) - { - curHex = mainPointForLayer[v]; - for(int h=0; h=low) - } - - return ret; - } -} - -///DefaultSpellMechanics -DefaultSpellMechanics::DefaultSpellMechanics(const IBattleCast * event) - : BaseMechanics(event) -{ -}; - -std::vector DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, bool * outDroppedHexes) const -{ - auto spellRange = spellRangeInHexes(centralHex); - - std::vector ret; - ret.reserve(spellRange.size()); - - std::copy(spellRange.begin(), spellRange.end(), std::back_inserter(ret)); - - return ret; -} - -std::set DefaultSpellMechanics::spellRangeInHexes(BattleHex centralHex) const -{ - using namespace SRSLPraserHelpers; - - std::set ret; - std::string rng = owner->getLevelInfo(getRangeLevel()).range + ','; //copy + artificial comma for easier handling - - if(rng.size() >= 2 && rng[0] != 'X') //there is at least one hex in range (+artificial comma) - { - std::string number1, number2; - int beg, end; - bool readingFirst = true; - for(auto & elem : rng) - { - if(std::isdigit(elem) ) //reading number - { - if(readingFirst) - number1 += elem; - else - number2 += elem; - } - else if(elem == ',') //comma - { - //calculating variables - if(readingFirst) - { - beg = atoi(number1.c_str()); - number1 = ""; - } - else - { - end = atoi(number2.c_str()); - number2 = ""; - } - //obtaining new hexes - std::set curLayer; - if(readingFirst) - { - curLayer = getInRange(centralHex, beg, beg); - } - else - { - curLayer = getInRange(centralHex, beg, end); - readingFirst = true; - } - //adding obtained hexes - for(auto & curLayer_it : curLayer) - { - ret.insert(curLayer_it); - } - - } - else if(elem == '-') //dash - { - beg = atoi(number1.c_str()); - number1 = ""; - readingFirst = false; - } - } - } - - return ret; -} - - -void DefaultSpellMechanics::addBattleLog(MetaString && line) -{ - sc.battleLog.push_back(line); -} - -void DefaultSpellMechanics::addCustomEffect(const battle::Unit * target, ui32 effect) -{ - CustomEffectInfo customEffect; - customEffect.effect = effect; - customEffect.stack = target->unitId(); - sc.customEffects.push_back(customEffect); -} - - -void DefaultSpellMechanics::afterCast() const -{ - -} - -void DefaultSpellMechanics::cast(const SpellCastEnvironment * env, const Target & target, std::vector & reflected) -{ - sc.side = casterSide; - sc.spellID = getSpellId(); - sc.tile = target.at(0).hexValue; - - sc.castByHero = mode == Mode::HERO; - sc.casterStack = (casterUnit ? casterUnit->unitId() : -1); - sc.manaGained = 0; - - sc.activeCast = false; - affectedUnits.clear(); - - const CGHeroInstance * otherHero = nullptr; - { - //check it there is opponent hero - const ui8 otherSide = cb->otherSide(casterSide); - - if(cb->battleHasHero(otherSide)) - otherHero = cb->battleGetFightingHero(otherSide); - } - - //calculate spell cost - if(mode == Mode::HERO) - { - auto casterHero = dynamic_cast(caster); - spellCost = cb->battleGetSpellCost(owner, casterHero); - - if(nullptr != otherHero) //handle mana channel - { - int manaChannel = 0; - for(const CStack * stack : cb->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow? - { - if(stack->owner == otherHero->tempOwner) - { - vstd::amax(manaChannel, stack->valOfBonuses(Bonus::MANA_CHANNELING)); - } - } - sc.manaGained = (manaChannel * spellCost) / 100; - } - sc.activeCast = true; - } - else if(mode == Mode::CREATURE_ACTIVE || mode == Mode::ENCHANTER) - { - spellCost = 1; - sc.activeCast = true; - } - - beforeCast(env->getRandomGenerator(), target, reflected); - - - switch (mode) - { - case Mode::CREATURE_ACTIVE: - case Mode::ENCHANTER: - case Mode::HERO: - case Mode::PASSIVE: - { - MetaString line; - caster->getCastDescription(owner, affectedUnits, line); - addBattleLog(std::move(line)); - } - break; - - default: - break; - } - - doRemoveEffects(env, affectedUnits, std::bind(&DefaultSpellMechanics::counteringSelector, this, _1)); - - for(auto & unit : affectedUnits) - sc.affectedCres.insert(unit->unitId()); - - env->sendAndApply(&sc); - - applyCastEffects(env, target); - - afterCast(); - - if(sc.activeCast) - { - caster->spendMana(mode, owner, env, spellCost); - if(sc.manaGained > 0) - { - assert(otherHero); - otherHero->spendMana(Mode::HERO, owner, env, -sc.manaGained); - } - } -} - -bool DefaultSpellMechanics::counteringSelector(const Bonus * bonus) const -{ - if(bonus->source != Bonus::SPELL_EFFECT) - return false; - - for(const SpellID & id : owner->counteredSpells) - { - if(bonus->sid == id.toEnum()) - return true; - } - - return false; -} - -void DefaultSpellMechanics::doRemoveEffects(const SpellCastEnvironment * env, const std::vector & targets, const CSelector & selector) -{ - SetStackEffect sse; - - for(auto unit : targets) - { - std::vector buffer; - auto bl = unit->getBonuses(selector); - - for(auto item : *bl) - buffer.emplace_back(*item); - - if(!buffer.empty()) - sse.toRemove.push_back(std::make_pair(unit->unitId(), buffer)); - } - - if(!sse.toRemove.empty()) - env->sendAndApply(&sse); -} - -} // namespace spells diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h deleted file mode 100644 index 1c82378f3..000000000 --- a/lib/spells/CDefaultSpellMechanics.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * CDefaultSpellMechanics.h, 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 - * - */ - -#pragma once - -#include "ISpellMechanics.h" -#include "../NetPacks.h" -#include "../battle/CBattleInfoEssentials.h" - -namespace spells -{ - -///all combat spells -class DLL_LINKAGE DefaultSpellMechanics : public BaseMechanics -{ -public: - DefaultSpellMechanics(const IBattleCast * event); - - std::vector rangeInHexes(BattleHex centralHex, bool * outDroppedHexes = nullptr) const override; - - void cast(const SpellCastEnvironment * env, const Target & target, std::vector & reflected) override final; - - bool counteringSelector(const Bonus * bonus) const; - -protected: - std::vector affectedUnits; - - static void doRemoveEffects(const SpellCastEnvironment * env, const std::vector & targets, const CSelector & selector); - - virtual void beforeCast(vstd::RNG & rng, const Target & target, std::vector & reflected) = 0; - - virtual void applyCastEffects(const SpellCastEnvironment * env, const Target & target) const = 0; - - virtual void afterCast() const; - - void addBattleLog(MetaString && line); - void addCustomEffect(const battle::Unit * target, ui32 effect); - - std::set spellRangeInHexes(BattleHex centralHex) const; - -private: - BattleSpellCast sc; - int spellCost; -}; - -} //namespace spells - diff --git a/lib/spells/CustomSpellMechanics.cpp b/lib/spells/CustomSpellMechanics.cpp deleted file mode 100644 index 720ba7fa9..000000000 --- a/lib/spells/CustomSpellMechanics.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* - * 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 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 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 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 res; - std::copy(stacks.begin(), stacks.end(), std::back_inserter(res)); - return res; -} - -void CustomSpellMechanics::beforeCast(vstd::RNG & rng, const Target & target, std::vector & reflected) -{ - reflected.clear(); - affectedUnits.clear(); - - Target spellTarget = transformSpellTarget(target); - - std::vector 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(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 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 stacks = collectTargets(); - - for(const battle::Unit * one : stacks) - { - auto selector = std::bind(&DefaultSpellMechanics::counteringSelector, this, _1); - - std::vector 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 CustomSpellMechanics::collectTargets() const -{ - std::set 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 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 CustomSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current) const -{ - //TODO: CustomSpellMechanics::getPossibleDestinations - - if(index != 0) - return std::vector(); - - std::vector 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 CustomSpellMechanics::rangeInHexes(BattleHex centralHex, bool * outDroppedHexes) const -{ - if(isMassive() || !centralHex.isValid()) - return std::vector(1, BattleHex::INVALID); - - Target aimPoint; - aimPoint.push_back(Destination(centralHex)); - - Target spellTarget = transformSpellTarget(aimPoint); - - std::set effectRange; - - effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * effect, bool & stop) - { - if(effect->automatic) - { - effect->adjustAffectedHexes(effectRange, this, spellTarget); - } - }); - - std::vector ret; - ret.reserve(effectRange.size()); - - std::copy(effectRange.begin(), effectRange.end(), std::back_inserter(ret)); - - return ret; -} - - -} //namespace spells - diff --git a/lib/spells/CustomSpellMechanics.h b/lib/spells/CustomSpellMechanics.h deleted file mode 100644 index 9f584e0e9..000000000 --- a/lib/spells/CustomSpellMechanics.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * CustomSpellMechanics.h, 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 - * - */ - -#pragma once - -#include "ISpellMechanics.h" -#include "CDefaultSpellMechanics.h" - -#include "effects/Effects.h" - -namespace spells -{ - -class CustomSpellMechanics : public DefaultSpellMechanics -{ -public: - CustomSpellMechanics(const IBattleCast * event, std::shared_ptr e); - virtual ~CustomSpellMechanics(); - - void applyEffects(const SpellCastEnvironment * env, const Target & targets) const override; - void applyIndirectEffects(const SpellCastEnvironment * env, const Target & targets) const override; - - void applyEffectsForced(const SpellCastEnvironment * env, const Target & targets) const override; - - bool canBeCast(Problem & problem) const override; - bool canBeCastAt(BattleHex destination) const override; - - void beforeCast(vstd::RNG & rng, const Target & target, std::vector & reflected) override; - - void applyCastEffects(const SpellCastEnvironment * env, const Target & target) const override; - - void cast(IBattleState * battleState, vstd::RNG & rng, const Target & target) override; - - std::vector getAffectedStacks(BattleHex destination) const override final; - - std::vector getTargetTypes() const override final; - std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current) const override final; - - std::vector rangeInHexes(BattleHex centralHex, bool * outDroppedHexes = nullptr) const override; - -private: - effects::Effects::EffectsToApply effectsToApply; - - std::shared_ptr effects; - - std::set collectTargets() const; - - Target transformSpellTarget(const Target & aimPoint) const; -}; - -} //namespace spells diff --git a/test/mock/mock_UnitHealthInfo.h b/test/mock/mock_UnitHealthInfo.h deleted file mode 100644 index 14490f4b0..000000000 --- a/test/mock/mock_UnitHealthInfo.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * mock_UnitHealthInfo.h, 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 - * - */ - -#pragma once - -#include "../../lib/battle/IUnitInfo.h" - -class UnitHealthInfoMock : public battle::IUnitHealthInfo -{ -public: - MOCK_CONST_METHOD0(unitMaxHealth, int32_t()); - MOCK_CONST_METHOD0(unitBaseAmount, int32_t()); -}; - - -