From 4042757f934ebfd60aec7d861aa2f673226a5caf Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 13 Nov 2014 07:34:20 +0300 Subject: [PATCH] Move spell range calculation to Mechanics * made ISpellMechanics opaque --- lib/CSpellHandler.cpp | 215 ++-------------------------------------- lib/CSpellHandler.h | 34 ------- lib/SpellMechanics.cpp | 216 +++++++++++++++++++++++++++++++++++++++++ lib/SpellMechanics.h | 46 +++++++++ 4 files changed, 268 insertions(+), 243 deletions(-) diff --git a/lib/CSpellHandler.cpp b/lib/CSpellHandler.cpp index 8668bebd1..ee7ba2ee6 100644 --- a/lib/CSpellHandler.cpp +++ b/lib/CSpellHandler.cpp @@ -34,116 +34,6 @@ namespace SpellConfig } -namespace SRSLPraserHelpers -{ - static int XYToHex(int x, int y) - { - return x + 17 * y; - } - - static int XYToHex(std::pair xy) - { - return XYToHex(xy.first, xy.second); - } - - static int hexToY(int battleFieldPosition) - { - return battleFieldPosition/17; - } - - static int hexToX(int battleFieldPosition) - { - int pos = battleFieldPosition - hexToY(battleFieldPosition) * 17; - 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 < 17 && xy.second >= 0 && xy.second < 11; - } - - //helper function for std::set CSpell::rangeInHexes(unsigned int centralHex, ui8 schoolLvl ) const - 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; - } -} - - -///ISpellMechanics -ISpellMechanics::ISpellMechanics(CSpell * s): - owner(s) -{ - -} - ///CSpell::LevelInfo CSpell::LevelInfo::LevelInfo() :description(""),cost(0),power(0),AIValue(0),smartTarget(true),range("0") @@ -281,104 +171,7 @@ ui32 CSpell::calculateHealedHP(const CGHeroInstance* caster, const CStack* stack std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const { - using namespace SRSLPraserHelpers; - - std::vector ret; - - if(id == SpellID::FIRE_WALL || id == SpellID::FORCE_FIELD) - { - //Special case - shape of obstacle depends on caster's side - //TODO make it possible through spell_info config - - BattleHex::EDir firstStep, secondStep; - if(side) - { - firstStep = BattleHex::TOP_LEFT; - secondStep = BattleHex::TOP_RIGHT; - } - else - { - firstStep = BattleHex::TOP_RIGHT; - secondStep = BattleHex::TOP_LEFT; - } - - //Adds hex to the ret if it's valid. Otherwise sets output arg flag if given. - auto addIfValid = [&](BattleHex hex) - { - if(hex.isValid()) - ret.push_back(hex); - else if(outDroppedHexes) - *outDroppedHexes = true; - }; - - ret.push_back(centralHex); - addIfValid(centralHex.moveInDir(firstStep, false)); - if(schoolLvl >= 2) //advanced versions of fire wall / force field cotnains of 3 hexes - addIfValid(centralHex.moveInDir(secondStep, false)); //moveInDir function modifies subject hex - - return ret; - } - - - std::string rng = getLevelInfo(schoolLvl).range + ','; //copy + artificial comma for easier handling - - if(rng.size() >= 1 && rng[0] != 'X') //there is at lest one hex in range - { - 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 abtained hexes - for(auto & curLayer_it : curLayer) - { - ret.push_back(curLayer_it); - } - - } - else if(elem == '-') //dash - { - beg = atoi(number1.c_str()); - number1 = ""; - readingFirst = false; - } - } - } - - //remove duplicates (TODO check if actually needed) - range::unique(ret); - return ret; + return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes); } std::set CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster) const @@ -769,7 +562,11 @@ void CSpell::setupMechanics() break; case SpellID::CHAIN_LIGHTNING: mechanics = new ChainLightningMechanics(this); - break; + break; + case SpellID::FIRE_WALL: + case SpellID::FORCE_FIELD: + mechanics = new WallMechanics(this); + break; default: if(isRisingSpell()) mechanics = new SpecialRisingSpellMechanics(this); diff --git a/lib/CSpellHandler.h b/lib/CSpellHandler.h index 6c1172b64..fde8fb406 100644 --- a/lib/CSpellHandler.h +++ b/lib/CSpellHandler.h @@ -295,40 +295,6 @@ private: ISpellMechanics * mechanics;//(!) do not serialize }; -class DLL_LINKAGE ISpellMechanics -{ -public: - - struct SpellTargetingContext - { - CBattleInfoCallback * cb; - - CSpell::TargetInfo ti; - }; - -public: - ISpellMechanics(CSpell * s); - virtual ~ISpellMechanics(){}; - - virtual std::set getAffectedStacks(SpellTargetingContext & ctx) const = 0; - - virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0; - - - /** \brief - * - * \param - * \return true if no error - * - */ - virtual bool adventureCast(SpellCastContext & context) const = 0; - virtual bool battleCast(SpellCastContext & context) const = 0; - -protected: - CSpell * owner; -}; - - bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door class DLL_LINKAGE CSpellHandler: public CHandlerBase diff --git a/lib/SpellMechanics.cpp b/lib/SpellMechanics.cpp index 24f0232a4..75131409e 100644 --- a/lib/SpellMechanics.cpp +++ b/lib/SpellMechanics.cpp @@ -16,6 +16,117 @@ #include "NetPacks.h" +namespace SRSLPraserHelpers +{ + static int XYToHex(int x, int y) + { + return x + 17 * y; + } + + static int XYToHex(std::pair xy) + { + return XYToHex(xy.first, xy.second); + } + + static int hexToY(int battleFieldPosition) + { + return battleFieldPosition/17; + } + + static int hexToX(int battleFieldPosition) + { + int pos = battleFieldPosition - hexToY(battleFieldPosition) * 17; + 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 < 17 && xy.second >= 0 && xy.second < 11; + } + + //helper function for std::set CSpell::rangeInHexes(unsigned int centralHex, ui8 schoolLvl ) const + 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; + } +} + + +///ISpellMechanics +ISpellMechanics::ISpellMechanics(CSpell * s): + owner(s) +{ + +} + + ///DefaultSpellMechanics bool DefaultSpellMechanics::adventureCast(SpellCastContext& context) const @@ -28,6 +139,73 @@ bool DefaultSpellMechanics::battleCast(SpellCastContext& context) const return false; //todo; DefaultSpellMechanics::battleCast } +std::vector DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const +{ + using namespace SRSLPraserHelpers; + + std::vector ret; + std::string rng = owner->getLevelInfo(schoolLvl).range + ','; //copy + artificial comma for easier handling + + if(rng.size() >= 1 && rng[0] != 'X') //there is at lest one hex in range + { + 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 abtained hexes + for(auto & curLayer_it : curLayer) + { + ret.push_back(curLayer_it); + } + + } + else if(elem == '-') //dash + { + beg = atoi(number1.c_str()); + number1 = ""; + readingFirst = false; + } + } + } + + //remove duplicates (TODO check if actually needed) + range::unique(ret); + return ret; +} + + std::set DefaultSpellMechanics::getAffectedStacks(SpellTargetingContext & ctx) const { @@ -40,6 +218,44 @@ ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(cons return owner->isImmuneBy(obj); } +std::vector WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool* outDroppedHexes) const +{ + using namespace SRSLPraserHelpers; + + std::vector ret; + + //Special case - shape of obstacle depends on caster's side + //TODO make it possible through spell_info config + + BattleHex::EDir firstStep, secondStep; + if(side) + { + firstStep = BattleHex::TOP_LEFT; + secondStep = BattleHex::TOP_RIGHT; + } + else + { + firstStep = BattleHex::TOP_RIGHT; + secondStep = BattleHex::TOP_LEFT; + } + + //Adds hex to the ret if it's valid. Otherwise sets output arg flag if given. + auto addIfValid = [&](BattleHex hex) + { + if(hex.isValid()) + ret.push_back(hex); + else if(outDroppedHexes) + *outDroppedHexes = true; + }; + + ret.push_back(centralHex); + addIfValid(centralHex.moveInDir(firstStep, false)); + if(schoolLvl >= 2) //advanced versions of fire wall / force field cotnains of 3 hexes + addIfValid(centralHex.moveInDir(secondStep, false)); //moveInDir function modifies subject hex + + return ret; +} + ///CloneMechanics diff --git a/lib/SpellMechanics.h b/lib/SpellMechanics.h index 351434511..fd7f7a9f3 100644 --- a/lib/SpellMechanics.h +++ b/lib/SpellMechanics.h @@ -12,11 +12,48 @@ #include "CSpellHandler.h" +class DLL_LINKAGE ISpellMechanics +{ +public: + + struct SpellTargetingContext + { + CBattleInfoCallback * cb; + + CSpell::TargetInfo ti; + + int schoolLvl; + }; + +public: + ISpellMechanics(CSpell * s); + virtual ~ISpellMechanics(){}; + + virtual std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const = 0; + virtual std::set getAffectedStacks(SpellTargetingContext & ctx) const = 0; + + virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0; + + + /** \brief + * + * \param + * \return true if no error + * + */ + virtual bool adventureCast(SpellCastContext & context) const = 0; + virtual bool battleCast(SpellCastContext & context) const = 0; + +protected: + CSpell * owner; +}; + class DefaultSpellMechanics: public ISpellMechanics { public: DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; + std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override; std::set getAffectedStacks(SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; @@ -27,6 +64,15 @@ public: +class WallMechanics: public DefaultSpellMechanics +{ +public: + WallMechanics(CSpell * s): DefaultSpellMechanics(s){}; + std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override; + +}; + + class ChainLightningMechanics: public DefaultSpellMechanics { public: