diff --git a/CGameInterface.h b/CGameInterface.h index 5d41a58bb..964f03131 100644 --- a/CGameInterface.h +++ b/CGameInterface.h @@ -118,6 +118,7 @@ public: virtual void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right virtual void battlefieldPrepared(int battlefieldType, std::vector obstacles){}; //called when battlefield is prepared, prior the battle beginning virtual void battleStacksHealedRes(const std::vector > & healedStacks){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp + virtual void battleNewStackAppeared(int stackID){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned }; class CAIHandler { diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index 9d2399596..2c1975dbb 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -496,6 +496,10 @@ void CBattleInterface::show(SDL_Surface * to) for(size_t v=0; vgetType(); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 1c67740b0..ef25ab7ed 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1171,6 +1171,18 @@ void CPlayerInterface::battleStacksHealedRes(const std::vectorbattleGetStackByID(stackID); + + //changing necessary things in battle interface + std::pair coords = CBattleHex::getXYUnitAnim(newStack->position, newStack->owner == battleInt->attackingHeroInstance->tempOwner, newStack); + battleInt->creAnims[newStack->ID] = (new CCreatureAnimation(newStack->creature->animDefName)); + battleInt->creAnims[newStack->ID]->setType(2); + battleInt->creAnims[newStack->ID]->pos = genRect(battleInt->creAnims[newStack->ID]->fullHeight, battleInt->creAnims[newStack->ID]->fullWidth, coords.first, coords.second); + battleInt->creDir[newStack->ID] = newStack->owner == battleInt->attackingHeroInstance->tempOwner; +} + void CPlayerInterface::battleNewRound(int round) //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn { boost::unique_lock un(*pim); diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 8e3ba8cd3..63a96093d 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -193,6 +193,7 @@ public: void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right void battlefieldPrepared(int battlefieldType, std::vector obstacles); //called when battlefield is prepared, prior the battle beginning void battleStacksHealedRes(const std::vector > & healedStacks); //called when stacks are healed / resurrected + void battleNewStackAppeared(int stackID); //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned //-------------// diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index ca304494f..751a45fde 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -406,6 +406,14 @@ void SpellCast::applyCl( CClient *cl ) cl->playerint[GS(cl)->curB->side1]->battleSpellCast(this); if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end()) cl->playerint[GS(cl)->curB->side2]->battleSpellCast(this); + + if(id >= 66 && id <= 69) //elemental summoning + { + if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end()) + cl->playerint[GS(cl)->curB->side1]->battleNewStackAppeared(GS(cl)->curB->stacks.size() - 1); + if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end()) + cl->playerint[GS(cl)->curB->side2]->battleNewStackAppeared(GS(cl)->curB->stacks.size() - 1); + } } void SetStackEffect::applyCl( CClient *cl ) diff --git a/config/spell_info.txt b/config/spell_info.txt index dd01fc1a6..50bc89a84 100644 --- a/config/spell_info.txt +++ b/config/spell_info.txt @@ -34,9 +34,9 @@ 31 1 24 0 0 0 X 32 1 23 0 0 0 X 33 1 26 0 0 0 X -34 1 -1 0 0 0 X +34 1 5 0 0 0 X 35 0 41 0 0 0 X -36 1 -1 0 0 0 0 +36 1 3 0 0 0 0 37 1 39 0 0 0 0 38 1 79 0 0 0 0 39 1 79 0 0 0 0 diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 0a33d2bfd..a6cae2848 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2159,6 +2159,52 @@ std::set BattleInfo::getAttackedCreatures(const CSpell * s, const CGHer return attackedCres; } +int BattleInfo::calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster) +{ + switch(spell->id) + { + case 56: //frenzy + return 1; + default: //other spells + return caster->getPrimSkillLevel(2) + caster->valOfBonuses(HeroBonus::SPELL_DURATION); + } +} + +CStack * BattleInfo::generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) +{ + CStack * ret = new CStack(&VLC->creh->creatures[creatureID], amount, owner ? owner->tempOwner : 255, stackID, attackerOwned, slot); + if(owner) + { + ret->features.push_back(makeFeature(StackFeature::SPEED_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->valOfBonuses(HeroBonus::STACKS_SPEED), StackFeature::BONUS_FROM_HERO)); + //base luck/morale calculations + ret->morale = owner->getCurrentMorale(slot, false); + ret->luck = owner->getCurrentLuck(slot, false); + //other bonuses + ret->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->getPrimSkillLevel(0), StackFeature::BONUS_FROM_HERO)); + ret->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->getPrimSkillLevel(1), StackFeature::BONUS_FROM_HERO)); + ret->features.push_back(makeFeature(StackFeature::HP_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->valOfBonuses(HeroBonus::STACK_HEALTH), StackFeature::BONUS_FROM_HERO)); + ret->firstHPleft = ret->MaxHealth(); + } + else + { + ret->morale = 0; + ret->luck = 0; + } + + //native terrain bonuses + int faction = ret->creature->faction; + if(faction >= 0 && VLC->heroh->nativeTerrains[faction] == terrain) + { + ret->features.push_back(makeFeature(StackFeature::SPEED_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE)); + ret->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE)); + ret->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE)); + } + + ret->position = position; + + return ret; +} + CStack * BattleInfo::getNextStack() { CStack *current = getStack(activeStack); diff --git a/lib/CGameState.h b/lib/CGameState.h index e6fbdb41b..3cd43e16f 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -137,6 +137,8 @@ struct DLL_EXPORT BattleInfo static int calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting); //TODO: add additional conditions and require necessary data void calculateCasualties(std::set > *casualties); std::set getAttackedCreatures(const CSpell * s, const CGHeroInstance * caster, int destinationTile); //calculates stack affected by given spell + static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster); + static CStack * generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position); //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield }; class DLL_EXPORT CStack diff --git a/lib/NetPacks.h b/lib/NetPacks.h index a9d8e58c2..0270d0ebf 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -900,8 +900,8 @@ struct SpellCast : public CPackForClient//3009 void applyCl(CClient *cl); ui8 side; //which hero did cast spell: 0 - attacker, 1 - defender - ui32 id; - ui8 skill; + ui32 id; //id of spell + ui8 skill; //caster's skill level ui16 tile; //destination tile (may not be set in some global/mass spells std::vector resisted; //ids of creatures that resisted this spell std::set affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure) @@ -917,8 +917,8 @@ struct SetStackEffect : public CPackForClient //3010 DLL_EXPORT void applyGs(CGameState *gs); void applyCl(CClient *cl); - std::set stacks; - CStack::StackEffect effect; + std::set stacks; //affected stacks (IDs) + CStack::StackEffect effect; //type of effect template void serialize(Handler &h, const int version) { h & stacks & effect; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 61003dad3..65cc5eb47 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -695,6 +695,7 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs ) gs->curB->castSpells[side]++; } + if(gs->curB && id == 35) //dispel { for(std::set::const_iterator it = affectedCres.begin(); it != affectedCres.end(); ++it) @@ -716,6 +717,50 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs ) } } } + + //elemental summoning + if(id >= 66 && id <= 69) + { + int creID; + switch(id) + { + case 66: + creID = 114; //fire elemental + break; + case 67: + creID = 113; //earth elemental + break; + case 68: + creID = 115; //water elemental + break; + case 69: + creID = 112; //air elemental + break; + } + const int3 & tile = gs->curB->tile; + TerrainTile::EterrainType ter = gs->map->terrain[tile.x][tile.y][tile.z].tertype; + + int pos; //position of stack on the battlefield - to be calculated + + bool ac[BFIELD_SIZE]; + std::set occupyable; + bool twoHex = vstd::contains(VLC->creh->creatures[creID].abilities, StackFeature::DOUBLE_WIDE); + bool flying = vstd::contains(VLC->creh->creatures[creID].abilities, StackFeature::FLYING); + gs->curB->getAccessibilityMap(ac, twoHex, !side, true, occupyable, flying); + for(int g=0; ggetPrimSkillLevel(2) * VLC->spellh->spells[id].powers[skill], gs->curB->stacks.size(), !side, 255, ter, pos); + summonedStack->features.push_back( makeFeature(StackFeature::SUMMONED, StackFeature::WHOLE_BATTLE, 0, 0, StackFeature::BONUS_FROM_HERO) ); + + gs->curB->stacks.push_back(summonedStack); + } } static inline StackFeature featureGenerator(StackFeature::ECombatFeatures type, si16 subtype, si32 value, ui16 turnsRemain, si32 additionalInfo = 0) @@ -746,6 +791,9 @@ static std::vector stackEffectToFeature(const CStack::StackEffect case 33: //protection from earth sf.push_back(featureGenerator(StackFeature::SPELL_DAMAGE_REDUCTION, 3, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain)); break; + case 34: //anti-magic + sf.push_back(featureGenerator(StackFeature::LEVEL_SPELL_IMMUNITY, 0, VLC->spellh->spells[sse.id].powers[sse.level] - 1, sse.turnsRemain)); + break; case 41: //bless sf.push_back(featureGenerator(StackFeature::ALWAYS_MAXIMUM_DAMAGE, -1, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain)); break; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 12ac9a409..a9cfddc83 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -307,6 +307,9 @@ static CCreatureSet takeCasualties(int color, const CCreatureSet &set, BattleInf CCreatureSet ret(set); for(int i=0; istacks.size();i++) { + if(bat->stacks[i]->hasFeatureOfType(StackFeature::SUMMONED)) //don't take into account sumoned stacks + continue; + CStack *st = bat->stacks[i]; if(st->owner==color && vstd::contains(set.slots,st->slot) && st->amount < set.slots.find(st->slot)->second.second) { @@ -869,30 +872,8 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet curB->side2=(hero2)?(hero2->tempOwner):(-1); curB->round = -2; curB->activeStack = -1; - for(std::map >::const_iterator i = army1.slots.begin(); i!=army1.slots.end(); i++) - { - stacks.push_back(new CStack(&VLC->creh->creatures[i->second.first],i->second.second,hero1->tempOwner, stacks.size(), true,i->first)); - if(hero1) - { - stacks.back()->features.push_back(makeFeature(StackFeature::SPEED_BONUS, StackFeature::WHOLE_BATTLE, 0, hero1->valOfBonuses(HeroBonus::STACKS_SPEED), StackFeature::BONUS_FROM_HERO)); - //base luck/morale calculations - //TODO: check if terrain is native, add bonuses for neutral stacks, bonuses from town - stacks.back()->morale = hero1->getCurrentMorale(i->first,false); - stacks.back()->luck = hero1->getCurrentLuck(i->first,false); - stacks.back()->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, hero1->getPrimSkillLevel(0), StackFeature::BONUS_FROM_HERO)); - stacks.back()->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, hero1->getPrimSkillLevel(1), StackFeature::BONUS_FROM_HERO)); - stacks.back()->features.push_back(makeFeature(StackFeature::HP_BONUS, StackFeature::WHOLE_BATTLE, 0, hero1->valOfBonuses(HeroBonus::STACK_HEALTH), StackFeature::BONUS_FROM_HERO)); - stacks.back()->firstHPleft = stacks.back()->MaxHealth(); - } - else - { - stacks.back()->morale = 0; - stacks.back()->luck = 0; - } - stacks[stacks.size()-1]->ID = stacks.size()-1; - } - //initialization of positions + //reading battleStartpos std::ifstream positions; positions.open("config" PATHSEPARATOR "battleStartpos.txt", std::ios_base::in|std::ios_base::binary); if(!positions.is_open()) @@ -911,49 +892,31 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet positions>>dump; CGH::readItTo(positions, defenderTight); positions.close(); + //battleStartpos read + + for(std::map >::const_iterator i = army1.slots.begin(); i!=army1.slots.end(); i++) + { + int pos; + if(army1.formation) + pos = attackerTight[army1.slots.size()-1][i->first]; + else + pos = attackerLoose[army1.slots.size()-1][i->first]; + + CStack * stack = BattleInfo::generateNewStack(hero1, i->second.first, i->second.second, stacks.size(), true, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos); + stacks.push_back(stack); + } - if(army1.formation) - for(int b=0; bposition = attackerTight[army1.slots.size()-1][b]; - } - else - for(int b=0; bposition = attackerLoose[army1.slots.size()-1][b]; - } for(std::map >::const_iterator i = army2.slots.begin(); i!=army2.slots.end(); i++) { - stacks.push_back(new CStack(&VLC->creh->creatures[i->second.first],i->second.second,hero2 ? hero2->tempOwner : 255, stacks.size(), false, i->first)); - //base luck/morale calculations - //TODO: check if terrain is native, add bonuses for neutral stacks, bonuses from town - if(hero2) - { - stacks.back()->features.push_back(makeFeature(StackFeature::SPEED_BONUS, StackFeature::WHOLE_BATTLE, 0, hero2->valOfBonuses(HeroBonus::STACKS_SPEED), StackFeature::BONUS_FROM_HERO)); - stacks.back()->morale = hero2->getCurrentMorale(i->first,false); - stacks.back()->luck = hero2->getCurrentLuck(i->first,false); - stacks.back()->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, hero2->getPrimSkillLevel(0), StackFeature::BONUS_FROM_HERO)); - stacks.back()->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, hero2->getPrimSkillLevel(1), StackFeature::BONUS_FROM_HERO)); - stacks.back()->features.push_back(makeFeature(StackFeature::HP_BONUS, StackFeature::WHOLE_BATTLE, 0, hero2->valOfBonuses(HeroBonus::STACK_HEALTH), StackFeature::BONUS_FROM_HERO)); - stacks.back()->firstHPleft = stacks.back()->MaxHealth(); - } + int pos; + if(army2.formation) + pos = defenderTight[army2.slots.size()-1][i->first]; else - { - stacks.back()->morale = 0; - stacks.back()->luck = 0; - } - } + pos = defenderLoose[army2.slots.size()-1][i->first]; - if(army2.formation) - for(int b=0; bposition = defenderTight[army2.slots.size()-1][b]; - } - else - for(int b=0; bposition = defenderLoose[army2.slots.size()-1][b]; - } + CStack * stack = BattleInfo::generateNewStack(hero2, i->second.first, i->second.second, stacks.size(), false, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos); + stacks.push_back(stack); + } for(unsigned g=0; gcreature->faction; - if(faction >= 0 && VLC->heroh->nativeTerrains[faction] == gs->map->terrain[tile.x][tile.y][tile.z].tertype ) - { - stacks[g]->features.push_back(makeFeature(StackFeature::SPEED_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE)); - stacks[g]->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE)); - stacks[g]->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE)); - } - } - //adding war machines if(hero1) { if(hero1->getArt(13)) //ballista { - stacks.push_back(new CStack(&VLC->creh->creatures[146], 1, hero1->tempOwner, stacks.size(), true, 255)); - stacks[stacks.size()-1]->position = 52; - stacks.back()->morale = hero1->getCurrentMorale(stacks.back()->ID,false); - stacks.back()->luck = hero1->getCurrentLuck(stacks.back()->ID,false); + CStack * stack = BattleInfo::generateNewStack(hero1, 146, 1, stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 52); + stacks.push_back(stack); } if(hero1->getArt(14)) //ammo cart { - stacks.push_back(new CStack(&VLC->creh->creatures[148], 1, hero1->tempOwner, stacks.size(), true, 255)); - stacks[stacks.size()-1]->position = 18; - stacks.back()->morale = hero1->getCurrentMorale(stacks.back()->ID,false); - stacks.back()->luck = hero1->getCurrentLuck(stacks.back()->ID,false); + CStack * stack = BattleInfo::generateNewStack(hero1, 148, 1, stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 18); + stacks.push_back(stack); } if(hero1->getArt(15)) //first aid tent { - stacks.push_back(new CStack(&VLC->creh->creatures[147], 1, hero1->tempOwner, stacks.size(), true, 255)); - stacks[stacks.size()-1]->position = 154; - stacks.back()->morale = hero1->getCurrentMorale(stacks.back()->ID,false); - stacks.back()->luck = hero1->getCurrentLuck(stacks.back()->ID,false); + CStack * stack = BattleInfo::generateNewStack(hero1, 147, 1, stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 154); + stacks.push_back(stack); } } if(hero2) { if(hero2->getArt(13)) //ballista { - stacks.push_back(new CStack(&VLC->creh->creatures[146], 1, hero2->tempOwner, stacks.size(), false, 255)); - stacks[stacks.size()-1]->position = 66; - stacks.back()->morale = hero2->getCurrentMorale(stacks.back()->ID,false); - stacks.back()->luck = hero2->getCurrentLuck(stacks.back()->ID,false); + CStack * stack = BattleInfo::generateNewStack(hero2, 146, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 66); + stacks.push_back(stack); } if(hero2->getArt(14)) //ammo cart { - stacks.push_back(new CStack(&VLC->creh->creatures[148], 1, hero2->tempOwner, stacks.size(), false, 255)); - stacks[stacks.size()-1]->position = 32; - stacks.back()->morale = hero2->getCurrentMorale(stacks.back()->ID,false); - stacks.back()->luck = hero2->getCurrentLuck(stacks.back()->ID,false); + CStack * stack = BattleInfo::generateNewStack(hero2, 148, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 32); + stacks.push_back(stack); } if(hero2->getArt(15)) //first aid tent { - stacks.push_back(new CStack(&VLC->creh->creatures[147], 1, hero2->tempOwner, stacks.size(), false, 255)); - stacks[stacks.size()-1]->position = 168; - stacks.back()->morale = hero2->getCurrentMorale(stacks.back()->ID,false); - stacks.back()->luck = hero2->getCurrentLuck(stacks.back()->ID,false); + CStack * stack = BattleInfo::generateNewStack(hero2, 147, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 168); + stacks.push_back(stack); } } //war machines added @@ -1070,7 +1009,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet bool badObstacle = false; for(int b=0; b= BFIELD_SIZE || !obAv[block[b]]) { badObstacle = true; break; @@ -2811,6 +2750,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) case 31: //protection from fire case 32: //protection from water case 33: //protection from earth + case 34: //anti-magic case 41: //bless case 42: //curse case 43: //bloodlust @@ -2826,6 +2766,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) case 53: //haste case 54: //slow case 55: //slayer + case 56: //frenzy case 61: //forgetfulness { SetStackEffect sse; @@ -2837,23 +2778,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) } sse.effect.id = ba.additionalInfo; sse.effect.level = h->getSpellSchoolLevel(s); - sse.effect.turnsRemain = h->getPrimSkillLevel(2) + h->valOfBonuses(HeroBonus::SPELL_DURATION); - if(!sse.stacks.empty()) - sendAndApply(&sse); - break; - } - case 56: //frenzy - { - SetStackEffect sse; - for(std::set::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) - { - if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell - continue; - sse.stacks.insert((*it)->ID); - } - sse.effect.id = ba.additionalInfo; - sse.effect.level = h->getSpellSchoolLevel(s); - sse.effect.turnsRemain = 1; + sse.effect.turnsRemain = BattleInfo::calculateSpellDuration(s, h); if(!sse.stacks.empty()) sendAndApply(&sse); break;