1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

* support for new spells: anti-magic and elemental summoning

* obstacles in battles should fit now the battlefield
* minor changes
This commit is contained in:
mateuszb 2009-08-06 14:02:21 +00:00
parent 184676d7c4
commit ea6ab102a7
11 changed files with 168 additions and 121 deletions

View File

@ -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<CObstacle*> obstacles){}; //called when battlefield is prepared, prior the battle beginning
virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & 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
{

View File

@ -496,6 +496,10 @@ void CBattleInterface::show(SDL_Surface * to)
for(size_t v=0; v<stackAliveByHex[b].size(); ++v)
{
int curStackID = stackAliveByHex[b][v];
if(creAnims.find(curStackID) == creAnims.end()) //eg. for summoned but not yet handled stacks
continue;
const CStack &curStack = stacks[curStackID];
int animType = creAnims[curStackID]->getType();

View File

@ -1171,6 +1171,18 @@ void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, u
}
}
void CPlayerInterface::battleNewStackAppeared(int stackID)
{
const CStack * newStack = cb->battleGetStackByID(stackID);
//changing necessary things in battle interface
std::pair <int, int> 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<boost::recursive_mutex> un(*pim);

View File

@ -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<CObstacle*> obstacles); //called when battlefield is prepared, prior the battle beginning
void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & 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
//-------------//

View File

@ -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 )

View File

@ -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

View File

@ -2159,6 +2159,52 @@ std::set<CStack*> 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);

View File

@ -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<std::pair<ui32,si32> > *casualties);
std::set<CStack*> 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

View File

@ -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<ui32> resisted; //ids of creatures that resisted this spell
std::set<ui32> 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<ui32> stacks;
CStack::StackEffect effect;
std::set<ui32> stacks; //affected stacks (IDs)
CStack::StackEffect effect; //type of effect
template <typename Handler> void serialize(Handler &h, const int version)
{
h & stacks & effect;

View File

@ -695,6 +695,7 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs )
gs->curB->castSpells[side]++;
}
if(gs->curB && id == 35) //dispel
{
for(std::set<ui32>::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<int> 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; g<BFIELD_SIZE; ++g)
{
if(g % BFIELD_WIDTH != 0 && g % BFIELD_WIDTH != BFIELD_WIDTH-1 && BattleInfo::isAccessible(g, ac, twoHex, !side, flying, true) )
{
pos = g;
break;
}
}
CStack * summonedStack = BattleInfo::generateNewStack(h, creID, h->getPrimSkillLevel(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<StackFeature> 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;

View File

@ -307,6 +307,9 @@ static CCreatureSet takeCasualties(int color, const CCreatureSet &set, BattleInf
CCreatureSet ret(set);
for(int i=0; i<bat->stacks.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<si32,std::pair<ui32,si32> >::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<si32,std::pair<ui32,si32> >::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; b<army1.slots.size(); ++b) //tight
{
stacks[b]->position = attackerTight[army1.slots.size()-1][b];
}
else
for(int b=0; b<army1.slots.size(); ++b) //loose
{
stacks[b]->position = attackerLoose[army1.slots.size()-1][b];
}
for(std::map<si32,std::pair<ui32,si32> >::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; b<army2.slots.size(); ++b) //tight
{
stacks[b+army1.slots.size()]->position = defenderTight[army2.slots.size()-1][b];
}
else
for(int b=0; b<army2.slots.size(); ++b) //loose
{
stacks[b+army1.slots.size()]->position = 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; g<stacks.size(); ++g) //shifting positions of two-hex creatures
{
@ -967,65 +930,41 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
}
}
//adding native terrain bonuses
for(int g=0; g<stacks.size(); ++g)
{
int faction = stacks[g]->creature->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<block.size(); ++b)
{
if(!obAv[block[b]])
if(block[b] < 0 || block[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<CStack*>::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;