diff --git a/client/BattleInterface/CBattleInterface.cpp b/client/BattleInterface/CBattleInterface.cpp index 21120278f..0a0bc568a 100644 --- a/client/BattleInterface/CBattleInterface.cpp +++ b/client/BattleInterface/CBattleInterface.cpp @@ -1607,6 +1607,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) case Spells::LIGHTNING_BOLT: case Spells::TITANS_LIGHTNING_BOLT: case Spells::THUNDERBOLT: + case Spells::CHAIN_LIGHTNING: //TODO: zigzag effect for (auto it = sc->affectedCres.begin(); it != sc->affectedCres.end(); ++it) //in case we have multiple targets { displayEffect(1, curInt->cb->battleGetStackByID(*it, false)->position); @@ -1630,7 +1631,6 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) case Spells::SUMMON_AIR_ELEMENTAL: case Spells::CLONE: case Spells::REMOVE_OBSTACLE: - case Spells::CHAIN_LIGHTNING: addNewAnim(new CDummyAnimation(this, 2)); //interface won't return until animation is played. TODO: make it smarter? break; } //switch(sc->id) diff --git a/config/spell_info.json b/config/spell_info.json index 188a5134b..9633547c7 100644 --- a/config/spell_info.json +++ b/config/spell_info.json @@ -27,7 +27,7 @@ { "id": 16, "effect": -1, "anim": 46, "ranges": [ "0", "0", "0", "0" ] }, { "id": 17, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] }, { "id": 18, "effect": -1, "anim": 10, "ranges": [ "0", "0", "0", "0" ] }, - { "id": 19, "effect": -1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] }, + { "id": 19, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] }, { "id": 20, "effect": -1, "anim": 45, "ranges": [ "1", "1", "1", "1" ] }, { "id": 21, "effect": -1, "anim": 53, "ranges": [ "0,1", "0,1", "0,1", "0,1" ] }, { "id": 22, "effect": -1, "anim": 9, "ranges": [ "0-2", "0-2", "0-2", "0-2" ] }, diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 483864318..ed574f062 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -385,7 +385,7 @@ BattleHex BattleInfo::getClosestTile (bool attackerOwned, int initialPos, std::s std::vector sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :( BattleHex initialHex = BattleHex(initialPos); - auto compareDistance = [initialPos, initialHex](const BattleHex left, const BattleHex right) -> bool + auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool { return initialHex.getDistance (initialHex, left) < initialHex.getDistance (initialHex, right); }; @@ -396,10 +396,12 @@ BattleHex BattleInfo::getClosestTile (bool attackerOwned, int initialPos, std::s auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool { - return closestDistance < here.getDistance (initialPos, here); + int debug = here.getDistance (initialPos, here); + bool debug2 = closestDistance < debug; + return debug2; }; - boost::remove_if (sortedTiles, notClosest); //only closest tiles are interesting + sortedTiles.erase (boost::remove_if (sortedTiles, notClosest), sortedTiles.end()); //only closest tiles are interesting auto compareHorizontal = [attackerOwned](const BattleHex left, const BattleHex right) -> bool { @@ -764,8 +766,8 @@ std::set BattleInfo::getAttackedCreatures(const CSpell * s, int skillLe const ui8 attackerSide = sides[1] == attackerOwner; const auto attackedHexes = s->rangeInHexes(destinationTile, skillLevel, attackerSide); - const bool onlyAlive = s->id != 38 && s->id != 39; //when casting resurrection or animate dead we should be allow to select dead stack - + const bool onlyAlive = s->id != Spells::RESURRECTION && s->id != Spells::ANIMATE_DEAD; //when casting resurrection or animate dead we should be allow to select dead stack + //fixme: what about other rising spells (Sacrifice) ? if(s->id == Spells::DEATH_RIPPLE || s->id == Spells::DESTROY_UNDEAD || s->id == Spells::ARMAGEDDON) { for(int it=0; it BattleInfo::getAttackedCreatures(const CSpell * s, int skillLe } } } + else if (s->id == Spells::CHAIN_LIGHTNING) + { + std::set possibleHexes; + BOOST_FOREACH (auto stack, stacks) + { + if (stack->isValidTarget()) + BOOST_FOREACH (auto hex, stack->getHexes()) + { + possibleHexes.insert (hex); + } + } + BattleHex lightningHex = destinationTile; + for (int i = 0; i < 5; ++i) //TODO: depends on spell school level + { + auto stack = getStackT (lightningHex, true); + if (!stack) + break; + attackedCres.insert (stack); + BOOST_FOREACH (auto hex, stack->getHexes()) + { + possibleHexes.erase (hex); //can't hit same place twice + } + lightningHex = getClosestTile (attackerOwner, destinationTile, possibleHexes); + } + } else if (s->range[skillLevel].size() > 1) //custom many-hex range { BOOST_FOREACH(BattleHex hex, attackedHexes) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index a4e20e5a0..1eb53da76 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3953,6 +3953,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest case Spells::ICE_BOLT: case Spells::LIGHTNING_BOLT: case Spells::IMPLOSION: + case Spells::CHAIN_LIGHTNING: case Spells::FROST_RING: case Spells::FIREBALL: case Spells::INFERNO: @@ -3975,6 +3976,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest sc.dmgToDisplay = 0; } } + int chainLightningModifier = 0; for(std::set::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) { if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell @@ -3988,10 +3990,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest bsa.effect = spell->mainEffectAnim; } if (spellDamage) - bsa.damageAmount = spellDamage; + bsa.damageAmount = spellDamage >> chainLightningModifier; else { - bsa.damageAmount = gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl, usedSpellPower); + bsa.damageAmount = gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl, usedSpellPower) >> chainLightningModifier; sc.dmgToDisplay += bsa.damageAmount; } bsa.stackAttacked = (*it)->ID; @@ -4001,6 +4003,9 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest bsa.attackerID = -1; (*it)->prepareAttacked(bsa); si.stacks.push_back(bsa); + + if (spellID == Spells::CHAIN_LIGHTNING) + ++chainLightningModifier; } break; }