Преглед на файлове

* next part of sieges
* partial implementation of berserk spell (unit does not always attack the nearest creature, wrong handling of situation when nearest stack is too far)
* ballista can shoot (it was considered to be always blocked instead of always free)

mateuszb преди 16 години
родител
ревизия
5bfbcfb000
променени са 8 файла, в които са добавени 202 реда и са изтрити 66 реда
  1. 8 0
      CCallback.cpp
  2. 2 0
      CCallback.h
  3. 17 0
      client/CBattleInterface.cpp
  4. 1 1
      client/CBattleInterface.h
  5. 94 15
      lib/CGameState.cpp
  6. 10 7
      lib/CGameState.h
  7. 3 0
      lib/NetPacksLib.cpp
  8. 67 43
      server/CGameHandler.cpp

+ 8 - 0
CCallback.cpp

@@ -650,6 +650,14 @@ std::pair<ui32, ui32> CCallback::battleEstimateDamage(int attackerID, int defend
 	return BattleInfo::calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker->ID, defender->position), 0);
 }
 
+ui8 CCallback::battleGetSiegeLevel()
+{
+	if(!gs->curB)
+		return 0;
+
+	return gs->curB->siege;
+}
+
 void CCallback::swapGarrisonHero( const CGTownInstance *town )
 {
 	if(town->tempOwner != player) return;

+ 2 - 0
CCallback.h

@@ -184,6 +184,7 @@ public:
 	virtual ui8 battleGetWallState(int partOfWall)=0; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
 	virtual int battleGetWallUnderHex(int hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found
 	virtual std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
+	virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
 };
 
 struct HeroMoveDetails
@@ -292,6 +293,7 @@ public:
 	ui8 battleGetWallState(int partOfWall); //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
 	int battleGetWallUnderHex(int hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found
 	std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID); //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
+	ui8 battleGetSiegeLevel(); //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
 
 //XXX hmmm _tmain on _GNUC_ wtf?
 //friends

+ 17 - 0
client/CBattleInterface.cpp

@@ -102,6 +102,19 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	if(siegeH)
 	{
 		background = BitmapHandler::loadBitmap( siegeH->getSiegeName(0) );
+		ui8 siegeLevel = LOCPLINT->cb->battleGetSiegeLevel();
+		if(siegeLevel >= 2) //citadel or castle
+		{
+			//print moat/mlip
+			SDL_Surface * moat = BitmapHandler::loadBitmap( siegeH->getSiegeName(13) ),
+				* mlip = BitmapHandler::loadBitmap( siegeH->getSiegeName(14) );
+
+			blitAt(moat, 410, background->h - moat->h, background);
+			blitAt(mlip, 410, background->h - mlip->h, background);
+
+			SDL_FreeSurface(moat);
+			SDL_FreeSurface(mlip);
+		}
 	}
 	else
 	{
@@ -3304,6 +3317,10 @@ std::string CBattleInterface::SiegeHelper::getSiegeName(ui16 what, ui16 additInf
 		return "SG" + townTypeInfixes[town->town->typeID] + "WA2.BMP";
 	case 12: //upper static wall
 		return "SG" + townTypeInfixes[town->town->typeID] + "WA5.BMP";
+	case 13: //moat
+		return "SG" + townTypeInfixes[town->town->typeID] + "MOAT.BMP";
+	case 14: //mlip
+		return "SG" + townTypeInfixes[town->town->typeID] + "MLIP.BMP";
 	default:
 		return "";
 	}

+ 1 - 1
client/CBattleInterface.h

@@ -242,7 +242,7 @@ private:
 		~SiegeHelper(); //d-tor
 
 		//filename getters
-		std::string getSiegeName(ui16 what, ui16 additInfo = 1) const; //what: 0 - background, 1 - background wall, 2 - keep, 3 - bottom tower, 4 - bottom wall, 5 - below gate, 6 - over gate, 7 - upper wall, 8 - uppert tower, 9 - gate, 10 - gate arch, 11 - bottom static wall, 12 - upper static wall; additInfo: 1 - intact, 2 - damaged, 3 - destroyed
+		std::string getSiegeName(ui16 what, ui16 additInfo = 1) const; //what: 0 - background, 1 - background wall, 2 - keep, 3 - bottom tower, 4 - bottom wall, 5 - below gate, 6 - over gate, 7 - upper wall, 8 - uppert tower, 9 - gate, 10 - gate arch, 11 - bottom static wall, 12 - upper static wall, 13 - moat, 14 - mlip; additInfo: 1 - intact, 2 - damaged, 3 - destroyed
 
 		void printPartOfWall(SDL_Surface * to, int what);//what: 1 - background wall, 2 - keep, 3 - bottom tower, 4 - bottom wall, 5 - below gate, 6 - over gate, 7 - upper wall, 8 - uppert tower, 9 - gate, 10 - gate arch, 11 - bottom static wall, 12 - upper static wall
 

+ 94 - 15
lib/CGameState.cpp

@@ -239,6 +239,7 @@ static CGObjectInstance * createObject(int id, int subid, int3 pos, int owner)
 	nobj->defInfo = VLC->dobjinfo->gobjs[id][subid];
 	return nobj;
 }
+
 CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
 {
 	for(unsigned int g=0; g<stacks.size(); ++g)
@@ -248,6 +249,17 @@ CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
 	}
 	return NULL;
 }
+
+const CStack * BattleInfo::getStack(int stackID, bool onlyAlive) const
+{
+	for(unsigned int g=0; g<stacks.size(); ++g)
+	{
+		if(stacks[g]->ID == stackID && (!onlyAlive || stacks[g]->alive()))
+			return stacks[g];
+	}
+	return NULL;
+}
+
 CStack * BattleInfo::getStackT(int tileID, bool onlyAlive)
 {
 	for(unsigned int g=0; g<stacks.size(); ++g)
@@ -264,7 +276,25 @@ CStack * BattleInfo::getStackT(int tileID, bool onlyAlive)
 	}
 	return NULL;
 }
-void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit)
+
+const CStack * BattleInfo::getStackT(int tileID, bool onlyAlive) const
+{
+	for(unsigned int g=0; g<stacks.size(); ++g)
+	{
+		if(stacks[g]->position == tileID 
+			|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
+			|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !stacks[g]->attackerOwned && stacks[g]->position+1 == tileID))
+		{
+			if(!onlyAlive || stacks[g]->alive())
+			{
+				return stacks[g];
+			}
+		}
+	}
+	return NULL;
+}
+
+void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit) const
 {
 	memset(accessibility, 1, BFIELD_SIZE); //initialize array with trues
 
@@ -303,14 +333,14 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
 	//walls
 	if(siege > 0)
 	{
-		static const int permanentlyLocked[] = {12, 45, 78, 112, 147, 182};
+		static const int permanentlyLocked[] = {12, 45, 78, 112, 147, 165};
 		for(int b=0; b<ARRAY_COUNT(permanentlyLocked); ++b)
 		{
 			accessibility[permanentlyLocked[b]] = false;
 		}
 
 		static const std::pair<int, int> lockedIfNotDestroyed[] = //(which part of wall, which hex is blocked if this part of wall is not destroyed
-			{std::make_pair(2, 165), std::make_pair(3, 130), std::make_pair(4, 62), std::make_pair(5, 29)};
+			{std::make_pair(2, 182), std::make_pair(3, 130), std::make_pair(4, 62), std::make_pair(5, 29)};
 		for(int b=0; b<ARRAY_COUNT(lockedIfNotDestroyed); ++b)
 		{
 			if(si.wallState[lockedIfNotDestroyed[b].first] < 3)
@@ -318,6 +348,12 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
 				accessibility[lockedIfNotDestroyed[b].second] = false;
 			}
 		}
+
+		//gate
+		if(attackerOwned && si.wallState[7] < 3) //if it attacker's unit and gate is not destroyed
+		{
+			accessibility[95] = accessibility[96] = false; //block gate's hexes
+		}
 	}
 
 	//occupyability
@@ -359,7 +395,7 @@ bool BattleInfo::isAccessible(int hex, bool * accessibility, bool twoHex, bool a
 	}
 }
 
-void BattleInfo::makeBFS(int start, bool *accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying) //both pointers must point to the at least 187-elements int arrays
+void BattleInfo::makeBFS(int start, bool *accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying) const //both pointers must point to the at least 187-elements int arrays
 {
 	//inits
 	for(int b=0; b<BFIELD_SIZE; ++b)
@@ -379,20 +415,24 @@ void BattleInfo::makeBFS(int start, bool *accessibility, int *predecessor, int *
 		for(unsigned int nr=0; nr<neighbours.size(); nr++)
 		{
 			curNext = neighbours[nr]; //if(!accessibility[curNext] || (dists[curHex]+1)>=dists[curNext])
-			if(!isAccessible(curNext, accessibility, twoHex, attackerOwned, flying, dists[curHex]+1 == dists[curNext]) || (dists[curHex]+1)>=dists[curNext])
+			bool accessible = isAccessible(curNext, accessibility, twoHex, attackerOwned, flying, dists[curHex]+1 == dists[curNext]);
+			if( dists[curHex]+1 >= dists[curNext] )
 				continue;
-			hexq.push(curNext);
-			dists[curNext] = dists[curHex] + 1;
+			if(accessible)
+			{
+				hexq.push(curNext);
+				dists[curNext] = dists[curHex] + 1;
+			}
 			predecessor[curNext] = curHex;
 		}
 	}
 };
 
-std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable)
+std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) const
 {
 	std::vector<int> ret;
 	bool ac[BFIELD_SIZE];
-	CStack *s = getStack(stackID);
+	const CStack *s = getStack(stackID);
 	std::set<int> occupyable;
 
 	getAccessibilityMap(ac, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, addOccupiable, occupyable, s->hasFeatureOfType(StackFeature::FLYING), stackID);
@@ -446,7 +486,7 @@ bool BattleInfo::isStackBlocked(int ID)
 {
 	CStack *our = getStack(ID);
 	if(our->hasFeatureOfType(StackFeature::SIEGE_WEAPON)) //siege weapons cannot be blocked
-		return true;
+		return false;
 
 	for(unsigned int i=0; i<stacks.size();i++)
 	{
@@ -2291,7 +2331,7 @@ ui32 BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender, co
 		return range.first;
 }
 
-void BattleInfo::calculateCasualties( std::set<std::pair<ui32,si32> > *casualties )
+void BattleInfo::calculateCasualties( std::set<std::pair<ui32,si32> > *casualties ) const
 {
 	for(unsigned int i=0; i<stacks.size();i++)//setting casualties
 	{
@@ -2408,7 +2448,7 @@ int BattleInfo::calculateSpellDuration(const CSpell * spell, const CGHeroInstanc
 	}
 }
 
-CStack * BattleInfo::generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position)
+CStack * BattleInfo::generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) const
 {
 	CStack * ret = new CStack(&VLC->creh->creatures[creatureID], amount, attackerOwned ? side1 : side2, stackID, attackerOwned, slot);
 	if(owner)
@@ -2456,7 +2496,7 @@ CStack * BattleInfo::generateNewStack(const CGHeroInstance * owner, int creature
 	return ret;
 }
 
-ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster)
+ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const
 {
 	ui32 ret = VLC->spellh->spells[sp->id].costs[caster->getSpellSchoolLevel(sp)];
 
@@ -2473,13 +2513,13 @@ ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster)
 	return ret + manaReduction;
 }
 
-int BattleInfo::hexToWallPart(int hex)
+int BattleInfo::hexToWallPart(int hex) const
 {
 	if(siege == 0) //there is no battle!
 		return -1;
 
 	static const std::pair<int, int> attackable[] = //potentially attackable parts of wall
-	{std::make_pair(50, 0), std::make_pair(182, 1), std::make_pair(165, 2), std::make_pair(130, 3),
+	{std::make_pair(50, 0), std::make_pair(183, 1), std::make_pair(182, 2), std::make_pair(130, 3),
 	std::make_pair(62, 4), std::make_pair(29, 5), std::make_pair(12, 6), std::make_pair(95, 7), std::make_pair(96, 7)};
 
 	for(int g = 0; g < ARRAY_COUNT(attackable); ++g)
@@ -2491,6 +2531,45 @@ int BattleInfo::hexToWallPart(int hex)
 	return -1; //not found!
 }
 
+std::pair<const CStack *, int> BattleInfo::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const
+{	
+	bool ac[BFIELD_SIZE];
+	std::set<int> occupyable;
+
+	getAccessibilityMap(ac, closest->hasFeatureOfType(StackFeature::DOUBLE_WIDE), closest->attackerOwned, false, occupyable, closest->hasFeatureOfType(StackFeature::FLYING), closest->ID);
+
+	int predecessor[BFIELD_SIZE], dist[BFIELD_SIZE];
+	makeBFS(closest->position, ac, predecessor, dist, closest->hasFeatureOfType(StackFeature::DOUBLE_WIDE), closest->attackerOwned, closest->hasFeatureOfType(StackFeature::FLYING));
+
+	std::vector< std::pair< std::pair<int, int>, const CStack *> > stackPairs; //pairs <<distance, hex>, stack>
+	for(int g=0; g<BFIELD_SIZE; ++g)
+	{
+		const CStack * atG = getStackT(g);
+		if(!atG || atG->ID == closest->ID) //if there is not stack or we are the closest one
+			continue;
+		if(boost::logic::indeterminate(attackerOwned) || atG->attackerOwned == attackerOwned)
+		{
+			if(predecessor[g] == -1) //TODO: is it really the best solution?
+				continue;
+			stackPairs.push_back( std::make_pair( std::make_pair(dist[predecessor[g]], g), atG) );
+		}
+	}
+
+	if(stackPairs.size() > 0)
+	{
+		std::pair< std::pair<int, int>, const CStack *> minimalPair = stackPairs[0];
+	
+		for(int b=1; b<stackPairs.size(); ++b)
+		{
+			if(stackPairs[b].first.first < minimalPair.first.first)
+				minimalPair = stackPairs[b];
+		}
+		return std::make_pair(minimalPair.second, predecessor[minimalPair.first.second]);
+	}
+
+	return std::make_pair<const CStack * , int>(NULL, -1);
+}
+
 CStack * BattleInfo::getNextStack()
 {
 	CStack *current = getStack(activeStack);

+ 10 - 7
lib/CGameState.h

@@ -138,24 +138,27 @@ struct DLL_EXPORT BattleInfo
 	CStack * getNextStack(); //which stack will have turn after current one
 	std::vector<CStack> getStackQueue(); //returns stack in order of their movement action
 	CStack * getStack(int stackID, bool onlyAlive = true);
+	const CStack * getStack(int stackID, bool onlyAlive = true) const;
 	CStack * getStackT(int tileID, bool onlyAlive = true);
-	void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit=-1); //send pointer to at least 187 allocated bytes
+	const CStack * getStackT(int tileID, bool onlyAlive = true) const;
+	void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit=-1) const; //send pointer to at least 187 allocated bytes
 	static bool isAccessible(int hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
-	void makeBFS(int start, bool*accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying); //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
+	void makeBFS(int start, bool*accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
 	std::pair< std::vector<int>, int > getPath(int start, int dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
-	std::vector<int> getAccessibility(int stackID, bool addOccupiable); //returns vector of accessible tiles (taking into account the creature range)
+	std::vector<int> getAccessibility(int stackID, bool addOccupiable) const; //returns vector of accessible tiles (taking into account the creature range)
 
 	bool isStackBlocked(int ID); //returns true if there is neighbouring enemy stack
 	static signed char mutualPosition(int hex1, int hex2); //returns info about mutual position of given hexes (-1 - they're distant, 0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left)
 	static std::vector<int> neighbouringTiles(int hex);
 	static ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge); //charge - number of hexes travelled before attack (for champion's jousting)
 	static std::pair<ui32, ui32> calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge); //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
-	void calculateCasualties(std::set<std::pair<ui32,si32> > *casualties);
+	void calculateCasualties(std::set<std::pair<ui32,si32> > *casualties) const;
 	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);
-	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
-	ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster); //returns cost of given spell
-	int hexToWallPart(int hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found
+	CStack * generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
+	int hexToWallPart(int hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
+	std::pair<const CStack *, int> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
 };
 
 class DLL_EXPORT CStack

+ 3 - 0
lib/NetPacksLib.cpp

@@ -907,6 +907,9 @@ static std::vector<StackFeature> stackEffectToFeature(const CStack::StackEffect
 	case 58: //counterstrike
 		sf.push_back(featureGenerator(StackFeature::ADDITIONAL_RETALIATION, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
 		break;
+	case 59: //bersek
+		sf.push_back(featureGenerator(StackFeature::ATTACKS_NEAREST_CREATURE, 0, sse.level, sse.turnsRemain));
+		break;
 	case 60: //hypnotize
 		sf.push_back(featureGenerator(StackFeature::HYPNOTIZED, 0, sse.level, sse.turnsRemain));
 		break;

+ 67 - 43
server/CGameHandler.cpp

@@ -376,6 +376,26 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 				}
 			}
 
+			if(next->hasFeatureOfType(StackFeature::ATTACKS_NEAREST_CREATURE)) //while in berserk
+			{
+				std::pair<const CStack *, int> attackInfo = curB.getNearestStack(next, boost::logic::indeterminate);
+				if(attackInfo.first != NULL)
+				{
+					BattleAction attack;
+					attack.actionType = 6;
+					attack.side = !next->attackerOwned;
+					attack.stackNumber = next->ID;
+
+					attack.additionalInfo = attackInfo.first->position;
+					attack.destinationTile = attackInfo.second;
+
+					makeBattleAction(attack);
+
+					checkForBattleEnd(stacks);
+				}
+				continue;
+			}
+
 askInterfaceForMove:
 			//ask interface and wait for answer
 			if(!battleResult.get())
@@ -1037,62 +1057,65 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 			curB->si.wallState[b] = 1;
 		}
 	}
+	
+	int terType = gs->battleGetBattlefieldType(tile);
 
 	//randomize obstacles
-	bool obAv[BFIELD_SIZE]; //availability of hexes for obstacles;
-	std::vector<int> possibleObstacles;
-
-	for(int i=0; i<BFIELD_SIZE; ++i)
+	if(town == NULL) //do it only when it's not siege
 	{
-		if(i%17 < 4 || i%17 > 12)
-		{
-			obAv[i] = false;
-		}
-		else
+		bool obAv[BFIELD_SIZE]; //availability of hexes for obstacles;
+		std::vector<int> possibleObstacles;
+
+		for(int i=0; i<BFIELD_SIZE; ++i)
 		{
-			obAv[i] = true;
+			if(i%17 < 4 || i%17 > 12)
+			{
+				obAv[i] = false;
+			}
+			else
+			{
+				obAv[i] = true;
+			}
 		}
-	}
-
-	int terType = gs->battleGetBattlefieldType(tile);
 
-	for(std::map<int, CObstacleInfo>::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g)
-	{
-		if(g->second.allowedTerrains[terType-1] == '1') //we need to take terType with -1 because terrain ids start from 1 and allowedTerrains array is indexed from 0
+		for(std::map<int, CObstacleInfo>::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g)
 		{
-			possibleObstacles.push_back(g->first);
+			if(g->second.allowedTerrains[terType-1] == '1') //we need to take terType with -1 because terrain ids start from 1 and allowedTerrains array is indexed from 0
+			{
+				possibleObstacles.push_back(g->first);
+			}
 		}
-	}
 
-	srand(time(NULL));
-	if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them
-	{
-		int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles
-		while(toBlock>0)
-		{
-			CObstacleInstance coi;
-			coi.uniqueID = curB->obstacles.size();
-			coi.ID = possibleObstacles[rand()%possibleObstacles.size()];
-			coi.pos = rand()%BFIELD_SIZE;
-			std::vector<int> block = VLC->heroh->obstacles[coi.ID].getBlocked(coi.pos);
-			bool badObstacle = false;
-			for(int b=0; b<block.size(); ++b)
+		srand(time(NULL));
+		if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them
+		{
+			int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles
+			while(toBlock>0)
 			{
-				if(block[b] < 0 || block[b] >= BFIELD_SIZE || !obAv[block[b]])
+				CObstacleInstance coi;
+				coi.uniqueID = curB->obstacles.size();
+				coi.ID = possibleObstacles[rand()%possibleObstacles.size()];
+				coi.pos = rand()%BFIELD_SIZE;
+				std::vector<int> block = VLC->heroh->obstacles[coi.ID].getBlocked(coi.pos);
+				bool badObstacle = false;
+				for(int b=0; b<block.size(); ++b)
 				{
-					badObstacle = true;
-					break;
+					if(block[b] < 0 || block[b] >= BFIELD_SIZE || !obAv[block[b]])
+					{
+						badObstacle = true;
+						break;
+					}
 				}
+				if(badObstacle) continue;
+				//obstacle can be placed
+				curB->obstacles.push_back(coi);
+				for(int b=0; b<block.size(); ++b)
+				{
+					if(block[b] >= 0 && block[b] < BFIELD_SIZE)
+						obAv[block[b]] = false;
+				}
+				toBlock -= block.size();
 			}
-			if(badObstacle) continue;
-			//obstacle can be placed
-			curB->obstacles.push_back(coi);
-			for(int b=0; b<block.size(); ++b)
-			{
-				if(block[b] >= 0 && block[b] < BFIELD_SIZE)
-					obAv[block[b]] = false;
-			}
-			toBlock -= block.size();
 		}
 	}
 
@@ -2955,6 +2978,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 			case 55: //slayer
 			case 56: //frenzy
 			case 58: //counterstrike
+			case 59: //berserk
 			case 60: //hypnotize
 			case 61: //forgetfulness
 			case 62: //blind