Browse Source

* support for new spells: anti-magic and elemental summoning
* obstacles in battles should fit now the battlefield
* minor changes

mateuszb 16 years ago
parent
commit
ea6ab102a7

+ 1 - 0
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<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
 {

+ 4 - 0
client/CBattleInterface.cpp

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

+ 12 - 0
client/CPlayerInterface.cpp

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

+ 1 - 0
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<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
 
 
 	//-------------//

+ 8 - 0
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 )

+ 2 - 2
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

+ 46 - 0
lib/CGameState.cpp

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

+ 2 - 0
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<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

+ 4 - 4
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<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;

+ 48 - 0
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<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;

+ 40 - 115
server/CGameHandler.cpp

@@ -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,23 +2766,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 			case 53: //haste
 			case 54: //slow
 			case 55: //slayer
-			case 61: //forgetfulness
-				{
-					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 = h->getPrimSkillLevel(2) + h->valOfBonuses(HeroBonus::SPELL_DURATION);
-					if(!sse.stacks.empty())
-						sendAndApply(&sse);
-					break;
-				}
 			case 56: //frenzy
+			case 61: //forgetfulness
 				{
 					SetStackEffect sse;
 					for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
@@ -2853,7 +2778,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 					}
 					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;