Explorar o código

Stupid AI is capable of winning / losing battle.

Michał W. Urbańczyk %!s(int64=15) %!d(string=hai) anos
pai
achega
a53ec23556

+ 1 - 1
AI/GeniusAI/BattleLogic.cpp

@@ -75,7 +75,7 @@ void CBattleLogic::SetCurrentTurn(int turn)
 void CBattleLogic::MakeStatistics(int currentCreatureId)
 {
 	typedef std::vector<const CStack*> vector_stacks;
-	vector_stacks allStacks = m_cb->battleGetStacks();
+	vector_stacks allStacks = m_cb->battleGetStacks(false);
 	const CStack *currentStack = m_cb->battleGetStackByID(currentCreatureId);
 	if(currentStack->position < 0) //turret
 	{

+ 135 - 21
AI/StupidAI/StupidAI.cpp

@@ -2,6 +2,13 @@
 #include "StupidAI.h"
 #include "../../lib/BattleState.h"
 #include "../../CCallback.h"
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include "../../lib/CCreatureHandler.h"
+#include <algorithm>
+#include <boost/thread.hpp>
+
+IBattleCallback * cbc;
 
 CStupidAI::CStupidAI(void)
 	: side(-1), cb(NULL)
@@ -18,7 +25,7 @@ CStupidAI::~CStupidAI(void)
 void CStupidAI::init( IBattleCallback * CB )
 {
 	print("init called, saving ptr to IBattleCallback");
-	cb = CB;
+	cbc = cb = CB;
 }
 
 void CStupidAI::actionFinished( const BattleAction *action )
@@ -31,39 +38,121 @@ void CStupidAI::actionStarted( const BattleAction *action )
 	print("actionStarted called");
 }
 
+struct EnemyInfo
+{
+	const CStack * s;
+	int adi, adr;
+	std::vector<THex> attackFrom; //for melee fight
+	EnemyInfo(const CStack * _s) : s(_s)
+	{}
+	void calcDmg(const CStack * ourStack)
+	{
+		TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
+		adi = (dmg.first + dmg.second) / 2;
+		adr = (retal.first + retal.second) / 2;
+	}
+
+	bool operator==(const EnemyInfo& ei) const
+	{
+		return s == ei.s;
+	}
+};
+
+bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2) 
+{
+	return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
+}
+
+int distToNearestNeighbour(THex hex, const std::vector<int> & dists, THex *chosenHex = NULL)
+{
+	int ret = 1000000;
+	BOOST_FOREACH(THex n, hex.neighbouringTiles())
+	{
+		if(dists[n] >= 0 && dists[n] < ret)
+		{
+			ret = dists[n];
+			if(chosenHex)
+				*chosenHex = n;
+		}
+	}
+
+	return ret;
+}
+
+bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const std::vector<int> & dists)
+{
+	return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
+}
+
+static bool willSecondHexBlockMoreEnemyShooters(const THex &h1, const THex &h2)
+{
+	int shooters[2] = {0}; //count of shooters on hexes
+
+	for(int i = 0; i < 2; i++)
+		BOOST_FOREACH(THex neighbour, (i ? h2 : h1).neighbouringTiles())
+			if(const CStack *s = cbc->battleGetStackByPos(neighbour))
+				if(s->getCreature()->isShooting())
+						shooters[i]++;
+
+	return shooters[0] < shooters[1];
+}
+
 BattleAction CStupidAI::activeStack( const CStack * stack )
 {
+	boost::this_thread::sleep(boost::posix_time::seconds(2));
 	print("activeStack called");
 	std::vector<THex> avHexes = cb->battleGetAvailableHexes(stack, false);
-	std::vector<const CStack *> avEnemies;
-	for(int g=0; g<avHexes.size(); ++g)
+	std::vector<int> dists = cb->battleGetDistances(stack);
+	std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
+
+	BOOST_FOREACH(const CStack *s, cb->battleGetStacks())
 	{
-		const CStack * enemy = cb->battleGetStackByPos(avHexes[g]);
-		if (enemy)
+		if(s->owner != stack->owner)
 		{
-			avEnemies.push_back(enemy);
+			if(cb->battleCanShoot(stack, s->position))
+			{
+				enemiesShootable.push_back(s);
+			}
+			else
+			{
+				BOOST_FOREACH(THex hex, avHexes)
+				{
+					if(CStack::isMeleeAttackPossible(stack, s, hex))
+					{
+						std::vector<EnemyInfo>::iterator i = std::find(enemiesReachable.begin(), enemiesReachable.end(), s);
+						if(i == enemiesReachable.end())
+						{
+							enemiesReachable.push_back(s);
+							i = enemiesReachable.begin() + (enemiesReachable.size() - 1);
+						}
+
+						i->attackFrom.push_back(hex);
+					}
+				}
+
+				if(!vstd::contains(enemiesReachable, s))
+					enemiesUnreachable.push_back(s);
+			}
 		}
 	}
 
-
-	if(stack->position % 17  <  5) //move army little towards enemy
+	if(enemiesShootable.size())
 	{
-		THex dest = stack->position + !side*2 - 1;
-		print(stack->nodeName() + "will be moved to " + boost::lexical_cast<std::string>(dest));
-		return BattleAction::makeMove(stack, dest); 
+		const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable);
+		return BattleAction::makeShotAttack(stack, ei.s);
 	}
-
-	if(avEnemies.size())
+	else if(enemiesReachable.size())
+	{
+		const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
+		return BattleAction::makeMeleeAttack(stack, ei.s, *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), &willSecondHexBlockMoreEnemyShooters));
+	}
+	else
 	{
-		const CStack * enemy = avEnemies[0];
-		//shooting
-		if (cb->battleCanShoot(stack, enemy->position))
+		const EnemyInfo &ei= *std::min_element(enemiesUnreachable.begin(), enemiesUnreachable.end(), boost::bind(isCloser, _1, _2, boost::ref(dists)));
+		if(distToNearestNeighbour(ei.s->position, dists) < BFIELD_SIZE)
 		{
-			return BattleAction::makeShotAttack(stack, enemy);
+			return goTowards(stack, ei.s->position);
 		}
-
-		//melee
-		return BattleAction::makeMeleeAttack(stack, enemy, avHexes);
 	}
 
 	return BattleAction::makeDefend(stack);
@@ -148,4 +237,29 @@ void CStupidAI::battleStacksRemoved(const BattleStacksRemoved & bsr)
 void CStupidAI::print(const std::string &text) const
 {
 	tlog0 << "CStupidAI [" << this <<"]: " << text << std::endl;
-}
+}
+
+BattleAction CStupidAI::goTowards(const CStack * stack, THex hex)
+{
+	THex realDest = hex;
+	int predecessors[BFIELD_SIZE];
+	std::vector<int> dists = cb->battleGetDistances(stack, hex);
+	if(distToNearestNeighbour(hex, dists, &realDest) > BFIELD_SIZE)
+	{
+		print("goTowards: Cannot reach");
+		return BattleAction::makeDefend(stack);
+	}
+
+	dists = cb->battleGetDistances(stack, realDest, predecessors);
+	std::vector<THex> avHexes = cb->battleGetAvailableHexes(stack, false);
+
+	while(1)
+	{
+		assert(realDest.isValid());
+		if(vstd::contains(avHexes, hex))
+			return BattleAction::makeMove(stack, hex);
+
+		hex = predecessors[hex];
+	}
+}
+

+ 1 - 0
AI/StupidAI/StupidAI.h

@@ -31,5 +31,6 @@ public:
 	void battleCatapultAttacked(const CatapultAttack & ca) OVERRIDE; //called when catapult makes an attack
 	void battleStacksRemoved(const BattleStacksRemoved & bsr) OVERRIDE; //called when certain stack is completely removed from battlefield
 
+	BattleAction goTowards(const CStack * stack, THex hex );
 };
 

+ 53 - 7
CCallback.cpp

@@ -539,7 +539,7 @@ int CBattleCallback::battleGetPos(int stack)
 	return -1;
 }
 
-std::vector<const CStack*> CBattleCallback::battleGetStacks()
+std::vector<const CStack*> CBattleCallback::battleGetStacks(bool onlyAlive /*= true*/)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	std::vector<const CStack*> ret;
@@ -550,7 +550,8 @@ std::vector<const CStack*> CBattleCallback::battleGetStacks()
 	}
 
 	BOOST_FOREACH(const CStack *s, gs->curB->stacks)
-		ret.push_back(s);
+		if(s->alive()  ||  !onlyAlive)
+			ret.push_back(s);
 
 	return ret;
 }
@@ -628,12 +629,13 @@ int CBattleCallback::battleGetWallUnderHex(int hex)
 	return gs->curB->hexToWallPart(hex);
 }
 
-std::pair<ui32, ui32> CBattleCallback::battleEstimateDamage(int attackerID, int defenderID)
+TDmgRange CBattleCallback::battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg)
 {
 	if(!gs->curB)
 		return std::make_pair(0, 0);
 
 	const CGHeroInstance * attackerHero, * defenderHero;
+	bool shooting = battleCanShoot(attacker, defender->position);
 
 	if(gs->curB->side1 == player)
 	{
@@ -646,10 +648,27 @@ std::pair<ui32, ui32> CBattleCallback::battleEstimateDamage(int attackerID, int
 		defenderHero = gs->curB->heroes[0];
 	}
 
-	const CStack * attacker = gs->curB->getStack(attackerID, false),
-		* defender = gs->curB->getStack(defenderID);
+	TDmgRange ret = gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, shooting, 0, false);
 
-	return gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker, defender->position), 0, false);
+	if(retaliationDmg)
+	{
+		if(shooting)
+		{
+			retaliationDmg->first = retaliationDmg->second = 0;
+		}
+		else
+		{
+			ui32 TDmgRange::* pairElems[] = {&TDmgRange::first, &TDmgRange::second};
+			for (int i=0; i<2; ++i)
+			{
+				BattleStackAttacked bsa;
+				bsa.damageAmount = ret.*pairElems[i];
+				retaliationDmg->*pairElems[!i] = gs->curB->calculateDmgRange(defender, attacker, bsa.newAmount, attacker->count, attackerHero, defenderHero, false, false, false).*pairElems[!i];
+			}
+		}
+	}
+	
+	return ret;
 }
 
 ui8 CBattleCallback::battleGetSiegeLevel()
@@ -1039,4 +1058,31 @@ CBattleCallback::CBattleCallback(CGameState *GS, int Player, CClient *C )
 	gs = GS;
 	player = Player;
 	cl = C;
-}
+}
+
+std::vector<int> CBattleCallback::battleGetDistances(const CStack * stack, THex hex /*= THex::INVALID*/, int * predecessors /*= NULL*/)
+{
+	if(!hex.isValid())
+		hex = stack->position;
+
+	std::vector<int> ret;
+	bool ac[BFIELD_SIZE];
+	int pr[BFIELD_SIZE], dist[BFIELD_SIZE];
+	gs->curB->makeBFS(stack->position, ac, pr, dist, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), false);
+
+	for(int i=0; i<BFIELD_SIZE; ++i)
+	{
+		if(pr[i] == -1)
+			ret.push_back(-1);
+		else
+			ret.push_back(dist[i]);
+	}
+
+	if(predecessors)
+	{
+		memcpy(predecessors, pr, BFIELD_SIZE * sizeof(int));
+	}
+
+	return ret;
+}
+

+ 6 - 4
CCallback.h

@@ -82,16 +82,17 @@ public:
 	virtual const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true)=0; //returns stack info by given pos
 	virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack
 	virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
-	virtual std::vector<const CStack*> battleGetStacks()=0; //returns stacks on battlefield
+	virtual std::vector<const CStack*> battleGetStacks(bool onlyAlive = true)=0; //returns stacks on battlefield
 	virtual void getStackQueue( std::vector<const CStack *> &out, int howMany )=0; //returns vector of stack in order of their move sequence
 	virtual std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID
+	virtual std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, int * predecessors = NULL)=0; //returns vector of distances to [dest hex number]
 	virtual bool battleCanShoot(const CStack * stack, THex dest)=0; //returns true if unit with id ID can shoot to dest
 	virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
 	virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle
 	virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead
 	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 TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL)=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
 	virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding to given side (0 - attacker, 1 - defender)
 	virtual si8 battleHasDistancePenalty(const CStack * stack, THex destHex) =0; //checks if given stack has distance penalty
@@ -209,16 +210,17 @@ public:
 	const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true) OVERRIDE; //returns stack info by given pos
 	int battleGetPos(int stack) OVERRIDE; //returns position (tile ID) of stack
 	int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack
-	std::vector<const CStack*> battleGetStacks() OVERRIDE; //returns stacks on battlefield
+	std::vector<const CStack*> battleGetStacks(bool onlyAlive = true) OVERRIDE; //returns stacks on battlefield
 	void getStackQueue( std::vector<const CStack *> &out, int howMany ) OVERRIDE; //returns vector of stack in order of their move sequence
 	std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable) OVERRIDE; //reutrns numbers of hexes reachable by creature with id ID
+	std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, int * predecessors = NULL) OVERRIDE; //returns vector of distances to [dest hex number]; if predecessors is not null, it must point to BFIELD_SIZE * sizeof(int) of allocated memory
 	bool battleCanShoot(const CStack * stack, THex dest) OVERRIDE; //returns true if unit with id ID can shoot to dest
 	bool battleCanCastSpell() OVERRIDE; //returns true, if caller can cast a spell
 	bool battleCanFlee() OVERRIDE; //returns true if caller can flee from the battle
 	const CGTownInstance * battleGetDefendedTown() OVERRIDE; //returns defended town if current battle is a siege, NULL instead
 	ui8 battleGetWallState(int partOfWall) OVERRIDE; //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) OVERRIDE; //returns part of destructible wall / gate / keep under given hex or -1 if not found
-	std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID) OVERRIDE; //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>
+	TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL) OVERRIDE; //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() OVERRIDE; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
 	const CGHeroInstance * battleGetFightingHero(ui8 side) const OVERRIDE; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
 	si8 battleHasDistancePenalty(const CStack * stack, THex destHex) OVERRIDE; //checks if given stack has distance penalty

+ 11 - 5
client/CBattleInterface.cpp

@@ -913,6 +913,10 @@ bool CMeleeAttack::init()
 	static const int mutPosToGroup[] = {11, 11, 12, 13, 13, 12};
 
 	int mutPos = THex::mutualPosition(attackingStackPosBeforeReturn + reversedShift, dest);
+	if(mutPos == -1 && attackedStack->doubleWide())
+	{
+		mutPos = THex::mutualPosition(attackingStackPosBeforeReturn + reversedShift, attackedStack->occupiedHex());
+	}
 	switch(mutPos) //attack direction
 	{
 	case 0: case 1: case 2: case 3: case 4: case 5:
@@ -920,6 +924,8 @@ bool CMeleeAttack::init()
 		break;
 	default:
 		tlog1<<"Critical Error! Wrong dest in stackAttacking! dest: "<<dest<<" attacking stack pos: "<<attackingStackPosBeforeReturn<<" reversed shift: "<<reversedShift<<std::endl;
+		group = 11;
+		break;
 	}
 
 	return true;
@@ -1129,7 +1135,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	//initializing armies
 	this->army1 = army1;
 	this->army2 = army2;
-	std::vector<const CStack*> stacks = curInt->cb->battleGetStacks();
+	std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(false);
 	BOOST_FOREACH(const CStack *s, stacks)
 	{
 		newStack(s);
@@ -1455,7 +1461,7 @@ void CBattleInterface::deactivate()
 
 void CBattleInterface::show(SDL_Surface * to)
 {
-	std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(); //used in a few places
+	std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(false); //used in a few places
 	++animCount;
 	if(!to) //"evaluating" to
 		to = screen;
@@ -1806,7 +1812,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 						//setting console text
 						char buf[500];
 						//calculating estimated dmg
-						std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive->ID, shere->ID);
+						std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
 						std::ostringstream estDmg;
 						estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
 						//printing
@@ -1956,7 +1962,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 						//setting console info
 						char buf[500];
 						//calculating estimated dmg
-						std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive->ID, shere->ID);
+						std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
 						std::ostringstream estDmg;
 						estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
 						//printing
@@ -2273,7 +2279,7 @@ void CBattleInterface::stackAttacking( const CStack * attacker, THex dest, const
 void CBattleInterface::newRoundFirst( int round )
 {
 	//handle regeneration
-	std::vector<const CStack*> stacks = curInt->cb->battleGetStacks();
+	std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(false);
 	BOOST_FOREACH(const CStack *s, stacks)
 	{
 		//don't show animation when no HP is regenerated

+ 16 - 3
global.h

@@ -18,7 +18,7 @@ typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
 typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 typedef si64 expType;
 typedef ui16 spelltype;
-
+typedef std::pair<ui32, ui32> TDmgRange;
 
 #include "int3.h"
 #include <map>
@@ -132,20 +132,26 @@ const int SPELLBOOK_GOLD_COST = 500;
 //for battle stacks' positions
 struct THex
 {
+	static const si16 INVALID = -1;
 	enum EDir{RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP_RIGHT};
 
 	si16 hex;
 
-	THex() : hex(-1) {}
+	THex() : hex(INVALID) {}
 	THex(si16 _hex) : hex(_hex)
 	{
-		assert(hex >= 0 && hex < BFIELD_SIZE);
+		//assert(isValid());
 	}
 	operator si16() const
 	{
 		return hex;
 	}
 
+	bool isValid() const
+	{
+		return hex >= 0 && hex < BFIELD_SIZE;
+	}
+
 	template<typename inttype>
 	THex(inttype x, inttype y)
 	{
@@ -208,18 +214,25 @@ struct THex
 		{
 		case TOP_LEFT:
 			setXY(y%2 ? x-1 : x, y-1);
+			break;
 		case TOP_RIGHT:
 			setXY(y%2 ? x : x+1, y-1);
+			break;
 		case RIGHT:
 			setXY(x+1, y);
+			break;
 		case BOTTOM_RIGHT:
 			setXY(y%2 ? x : x+1, y+1);
+			break;
 		case BOTTOM_LEFT:
 			setXY(y%2 ? x-1 : x, y+1);
+			break;
 		case LEFT:
 			setXY(x-1, y);
+			break;
 		default:
 			throw std::string("Disaster: wrong direction in THex::operator+=!\n");
+			break;
 		}
 	}
 

+ 4 - 20
lib/BattleAction.cpp

@@ -30,34 +30,18 @@ BattleAction BattleAction::makeDefend(const CStack *stack)
 	return ba;
 }
 
-BattleAction BattleAction::makeMeleeAttack( const CStack *stack, const CStack * attacked, std::vector<THex> reachableByAttacker )
+
+BattleAction BattleAction::makeMeleeAttack(const CStack *stack, const CStack * attacked, THex attackFrom /*= THex::INVALID*/)
 {
 	BattleAction ba;
 	ba.side = !stack->attackerOwned;
 	ba.actionType = WALK_AND_ATTACK;
 	ba.stackNumber = stack->ID;
-	ba.destinationTile = -1;
-	for (int g=0; g<reachableByAttacker.size(); ++g)
-	{
-		if (THex::mutualPosition(reachableByAttacker[g], attacked->position) >= 0 )
-		{
-			ba.destinationTile = reachableByAttacker[g];
-			break;
-		}
-	}
-
-	if (ba.destinationTile == -1)
-	{
-		//we couldn't determine appropriate pos
-		//TODO: should we throw an exception?
-		return makeDefend(stack);
-	}
-
+	ba.destinationTile = attackFrom;
 	ba.additionalInfo = attacked->position;
-
 	return ba;
-}
 
+}
 BattleAction BattleAction::makeWait(const CStack *stack)
 {
 	BattleAction ba;

+ 2 - 2
lib/BattleAction.h

@@ -25,7 +25,7 @@ struct DLL_EXPORT BattleAction
 	};
 	ui8 actionType; //use ActionType enum for values
 		//10 = Monster casts a spell (i.e. Faerie Dragons)	11 - Bad morale freeze	12 - stacks heals another stack
-	ui16 destinationTile;
+	THex destinationTile;
 	si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -36,7 +36,7 @@ struct DLL_EXPORT BattleAction
 
 	static BattleAction makeDefend(const CStack *stack);
 	static BattleAction makeWait(const CStack *stack);
-	static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, std::vector<THex> reachableByAttacker);
+	static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, THex attackFrom = THex::INVALID);
 	static BattleAction makeShotAttack(const CStack *shooter, const CStack *target);
 	static BattleAction makeMove(const CStack *stack, THex dest);
 };

+ 69 - 14
lib/BattleState.cpp

@@ -14,6 +14,7 @@
 #include "CCreatureHandler.h"
 #include "CSpellHandler.h"
 #include "CTownHandler.h"
+#include "NetPacks.h"
 
 /*
  * BattleState.h, part of VCMI engine
@@ -326,7 +327,7 @@ std::vector<THex> BattleInfo::getAccessibility(const CStack * stack, bool addOcc
 
 	return ret;
 }
-bool BattleInfo::isStackBlocked(const CStack * stack)
+bool BattleInfo::isStackBlocked(const CStack * stack) const
 {
 	if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
 		return false;
@@ -377,11 +378,11 @@ std::pair< std::vector<int>, int > BattleInfo::getPath(int start, int dest, bool
 	return std::make_pair(path, dist[dest]);
 }
 
-std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
+TDmgRange BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount, TQuantity defenderCount, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky ) const
 {
 	float additiveBonus=1.0f, multBonus=1.0f,
-		minDmg = attacker->getMinDamage() * attacker->count, 
-		maxDmg = attacker->getMaxDamage() * attacker->count;
+		minDmg = attacker->getMinDamage() * attackerCount, 
+		maxDmg = attacker->getMaxDamage() * attackerCount;
 
 	if(attacker->getCreature()->idNumber == 149) //arrow turret
 	{
@@ -569,7 +570,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	minDmg *= additiveBonus * multBonus;
 	maxDmg *= additiveBonus * multBonus;
 
-	std::pair<ui32, ui32> returnedVal;
+	TDmgRange returnedVal;
 
 	if(attacker->getEffect(42)) //curse handling (rest)
 	{
@@ -593,9 +594,14 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	return returnedVal;
 }
 
+TDmgRange BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const
+{
+	return calculateDmgRange(attacker, defender, attacker->count, defender->count, attackerHero, defendingHero, shooting, charge, lucky);
+}
+
 ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
 {
-	std::pair<ui32, ui32> range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky);
+	TDmgRange range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky);
 
 	if(range.first != range.second)
 	{
@@ -1021,7 +1027,7 @@ void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, i
 	}
 }
 
-si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex )
+si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex ) const
 {
 	struct HLP
 	{
@@ -1042,7 +1048,7 @@ si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex )
 
 }
 
-si8 BattleInfo::sameSideOfWall(int pos1, int pos2)
+si8 BattleInfo::sameSideOfWall(int pos1, int pos2) const
 {
 	int wallInStackLine = lineToWallHex(pos1/BFIELD_WIDTH);
 	int wallInDestLine = lineToWallHex(pos2/BFIELD_WIDTH);
@@ -1053,7 +1059,7 @@ si8 BattleInfo::sameSideOfWall(int pos1, int pos2)
 	return stackLeft != destLeft;
 }
 
-si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex )
+si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex ) const
 {
 	if (siege == 0)
 	{
@@ -1067,7 +1073,7 @@ si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex )
 	return !sameSideOfWall(stack->position, destHex);
 }
 
-si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLevel)
+si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const
 {
 	bool ac[BFIELD_SIZE];
 
@@ -1107,7 +1113,7 @@ si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLev
 // 	}
 // }
 
-bool BattleInfo::battleCanShoot(const CStack * stack, THex dest)
+bool BattleInfo::battleCanShoot(const CStack * stack, THex dest) const
 {
 	const CStack *dst = getStackT(dest);
 
@@ -1131,7 +1137,7 @@ bool BattleInfo::battleCanShoot(const CStack * stack, THex dest)
 	return false;
 }
 
-bool BattleInfo::battleCanFlee(int player)
+bool BattleInfo::battleCanFlee(int player) const
 {
 	if (player == side1)
 	{
@@ -1171,12 +1177,12 @@ const CStack * BattleInfo::battleGetStack(THex pos, bool onlyAlive)
 	return NULL;
 }
 
-const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack)
+const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack) const
 {
 	return heroes[!stack->attackerOwned];
 }
 
-si8 BattleInfo::battleMaxSpellLevel()
+si8 BattleInfo::battleMaxSpellLevel() const
 {
 // 	if(!curB) //there is not battle
 // 	{
@@ -1944,6 +1950,55 @@ std::string CStack::nodeName() const
 	return oss.str();
 }
 
+void CStack::prepareAttacked(BattleStackAttacked &bsa) const
+{
+	bsa.killedAmount = bsa.damageAmount / MaxHealth();
+	unsigned damageFirst = bsa.damageAmount % MaxHealth();
+
+	if( firstHPleft <= damageFirst )
+	{
+		bsa.killedAmount++;
+		bsa.newHP = firstHPleft + MaxHealth() - damageFirst;
+	}
+	else
+	{
+		bsa.newHP = firstHPleft - damageFirst;
+	}
+
+	if(count <= bsa.killedAmount) //stack killed
+	{
+		bsa.newAmount = 0;
+		bsa.flags |= 1;
+		bsa.killedAmount = count; //we cannot kill more creatures than we have
+	}
+	else
+	{
+		bsa.newAmount = count - bsa.killedAmount;
+	}
+}
+
+bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defender, THex attackerPos /*= THex::INVALID*/, THex defenderPos /*= THex::INVALID*/)
+{
+	if (!attackerPos.isValid())
+	{
+		attackerPos = attacker->position;
+	}
+	if (!defenderPos.isValid())
+	{
+		defenderPos = defender->position;
+	}
+
+	return
+		(THex::mutualPosition(attackerPos, defenderPos) >= 0)						//front <=> front
+		|| (attacker->doubleWide()									//back <=> front
+		&& THex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos) >= 0)
+		|| (defender->doubleWide()									//front <=> back
+		&& THex::mutualPosition(attackerPos, defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0)
+		|| (defender->doubleWide() && attacker->doubleWide()//back <=> back
+		&& THex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0);
+		
+}
+
 bool CMP_stack::operator()( const CStack* a, const CStack* b )
 {
 	switch(phase)

+ 16 - 11
lib/BattleState.h

@@ -21,7 +21,7 @@ class CStack;
 class CArmedInstance;
 class CGTownInstance;
 class CStackInstance;
-
+struct BattleStackAttacked;
 
 struct DLL_EXPORT CObstacleInstance
 {
@@ -84,10 +84,11 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	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<THex> getAccessibility(const CStack * stack, bool addOccupiable) const; //returns vector of accessible tiles (taking into account the creature range)
 
-	bool isStackBlocked(const CStack * stack); //returns true if there is neighboring enemy stack
+	bool isStackBlocked(const CStack * stack) const; //returns true if there is neighboring enemy stack
 
 	ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting)
-	std::pair<ui32, ui32> calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
+	TDmgRange calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount, TQuantity defenderCount, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
+	TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
 	void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
 	std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, int destinationTile); //calculates stack affected by given spell
 	static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
@@ -100,16 +101,16 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
 	ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
 	ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const;
-	si8 hasDistancePenalty(const CStack * stackID, THex destHex); //determines if given stack has distance penalty shooting given pos
-	si8 sameSideOfWall(int pos1, int pos2); //determines if given positions are on the same side of wall
-	si8 hasWallPenalty(const CStack * stack, THex destHex); //determines if given stack has wall penalty shooting given pos
-	si8 canTeleportTo(const CStack * stack, THex destHex, int telportLevel); //determines if given stack can teleport to given place
-	bool battleCanShoot(const CStack * stack, THex dest); //determines if stack with given ID shoot at the selected destination
+	si8 hasDistancePenalty(const CStack * stackID, THex destHex) const; //determines if given stack has distance penalty shooting given pos
+	si8 sameSideOfWall(int pos1, int pos2) const; //determines if given positions are on the same side of wall
+	si8 hasWallPenalty(const CStack * stack, THex destHex) const; //determines if given stack has wall penalty shooting given pos
+	si8 canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const; //determines if given stack can teleport to given place
+	bool battleCanShoot(const CStack * stack, THex dest) const; //determines if stack with given ID shoot at the selected destination
 
-	bool battleCanFlee(int player); //returns true if player can flee from the battle
+	bool battleCanFlee(int player) const; //returns true if player can flee from the battle
 	const CStack * battleGetStack(THex pos, bool onlyAlive); //returns stack at given tile
-	const CGHeroInstance * battleGetOwner(const CStack * stack); //returns hero that owns given stack; NULL if none
-	si8 battleMaxSpellLevel(); //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
+	const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none
+	si8 battleMaxSpellLevel() const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
 	void localInit();
 	static BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
 };
@@ -167,9 +168,13 @@ public:
 		return ret;
 	}
 
+	static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, THex attackerPos = THex::INVALID, THex defenderPos = THex::INVALID);
+
 	bool doubleWide() const;
 	int occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
 
+	void prepareAttacked(BattleStackAttacked &bsa) const; //requires bsa.damageAmout filled
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		assert(isIndependentNode());

+ 3 - 2
lib/CGameState.cpp

@@ -905,11 +905,12 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 			h->subID = 1;
 			h->initHero(1);
 			h->initObj();
-			//h->putStack(0, new CStackInstance(34, 5));
+			h->setCreature(0, 110, 1);
 
 			CGCreature *c = new CGCreature();
 			c->setOwner(1);
-			c->putStack(0, new CStackInstance(70, 6));
+			c->putStack(0, new CStackInstance(69, 6));
+			c->putStack(1, new CStackInstance(11, 3));
 			c->subID = 34;
 			c->initObj();
 

+ 18 - 61
server/CGameHandler.cpp

@@ -19,7 +19,7 @@
 #include "../lib/VCMIDirs.h"
 #include "../client/CSoundBase.h"
 #include "CGameHandler.h"
-
+#include <boost/format.hpp>
 
 /*
  * CGameHandler.cpp, part of VCMI engine
@@ -309,8 +309,6 @@ void CGameHandler::changeSecSkill( int ID, int which, int val, bool abs/*=false*
 void CGameHandler::startBattle( const CArmedInstance *armies[2], int3 tile, const CGHeroInstance *heroes[2], bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town /*= NULL*/ )
 {
 	battleEndCallback = new boost::function<void(BattleResult*)>(cb);
-	bEndArmy1 = armies[0];
-	bEndArmy2 = armies[1];
 	{
 		setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
 	}
@@ -321,6 +319,9 @@ void CGameHandler::startBattle( const CArmedInstance *armies[2], int3 tile, cons
 void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
 {
 	BattleResultsApplied resultsApplied;
+
+	const CArmedInstance *bEndArmy1 = gs->curB->belligerents[0];
+	const CArmedInstance *bEndArmy2 = gs->curB->belligerents[0];
 	resultsApplied.player1 = bEndArmy1->tempOwner;
 	resultsApplied.player2 = bEndArmy2->tempOwner;
 
@@ -425,33 +426,6 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 	delete battleResult.data;
 }
 
-void CGameHandler::prepareAttacked(BattleStackAttacked &bsa, const CStack *def)
-{	
-	bsa.killedAmount = bsa.damageAmount / def->MaxHealth();
-	unsigned damageFirst = bsa.damageAmount % def->MaxHealth();
-
-	if( def->firstHPleft <= damageFirst )
-	{
-		bsa.killedAmount++;
-		bsa.newHP = def->firstHPleft + def->MaxHealth() - damageFirst;
-	}
-	else
-	{
-		bsa.newHP = def->firstHPleft - damageFirst;
-	}
-
-	if(def->count <= bsa.killedAmount) //stack killed
-	{
-		bsa.newAmount = 0;
-		bsa.flags |= 1;
-		bsa.killedAmount = def->count; //we cannot kill more creatures than we have
-	}
-	else
-	{
-		bsa.newAmount = def->count - bsa.killedAmount;
-	}
-}
-
 void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance)
 {
 	bat.bsa.clear();
@@ -480,7 +454,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 	
 	
 	int dmg = bsa->damageAmount;
-	prepareAttacked(*bsa, def);
+	def->prepareAttacked(*bsa);
 
 	//life drain handling
 	if (att->hasBonusOfType(Bonus::LIFE_DRAIN))
@@ -515,7 +489,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 		bsa->effect = 11;
 
 		bsa->damageAmount = (dmg * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
-		prepareAttacked(*bsa, att);
+		att->prepareAttacked(*bsa);
 	}
 
 }
@@ -3026,21 +3000,21 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 	bool ok = true;
 	switch(ba.actionType)
 	{
-	case 2: //walk
+	case BattleAction::WALK: //walk
 		{
 			sendAndApply(&StartAction(ba)); //start movement
 			moveStack(ba.stackNumber,ba.destinationTile); //move
 			sendAndApply(&EndAction());
 			break;
 		}
-	case 3: //defend
-	case 8: //wait
+	case BattleAction::DEFEND: //defend
+	case BattleAction::WAIT: //wait
 		{
 			sendAndApply(&StartAction(ba));
 			sendAndApply(&EndAction());
 			break;
 		}
-	case 4: //retreat/flee
+	case BattleAction::RETREAT: //retreat/flee
 		{
 			if( !gs->curB->battleCanFlee(ba.side ? gs->curB->side2 : gs->curB->side1) )
 				break;
@@ -3053,7 +3027,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			battleResult.set(br);
 			break;
 		}
-	case 6: //walk or attack
+	case BattleAction::WALK_AND_ATTACK: //walk or attack
 		{
 			sendAndApply(&StartAction(ba)); //start movement and attack
 			int startingPos = gs->curB->getStack(ba.stackNumber)->position;
@@ -3075,37 +3049,20 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 				break;
 			}
 
-			if(curStack->ID == stackAtEnd->ID) //we should just move, it will be handled by following check
+			if(stackAtEnd && curStack->ID == stackAtEnd->ID) //we should just move, it will be handled by following check
 			{
 				stackAtEnd = NULL;
 			}
 
 			if(!stackAtEnd)
 			{
-				std::ostringstream problem;
-				problem << "There is no stack on " << ba.additionalInfo << " tile (no attack)!";
-				std::string probl = problem.str();
-				tlog3 << probl << std::endl;
-				complain(probl);
+				complain(boost::str(boost::format("walk and attack error: no stack at additionalInfo tile (%d)!\n") % ba.additionalInfo));
 				ok = false;
 				sendAndApply(&EndAction());
 				break;
 			}
 
-			ui16 curpos = curStack->position, 
-				enemypos = stackAtEnd->position;
-
-
-			if( !(
-				(THex::mutualPosition(curpos, enemypos) >= 0)						//front <=> front
-				|| (curStack->doubleWide()									//back <=> front
-					&& THex::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
-				|| (stackAtEnd->doubleWide()									//front <=> back
-					&& THex::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
-				|| (stackAtEnd->doubleWide() && curStack->doubleWide()//back <=> back
-					&& THex::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
-				)
-				)
+			if( !CStack::isMeleeAttackPossible(curStack, stackAtEnd) )
 			{
 				tlog3 << "Attack cannot be performed!";
 				sendAndApply(&EndAction());
@@ -3152,7 +3109,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			sendAndApply(&EndAction());
 			break;
 		}
-	case 7: //shoot
+	case BattleAction::SHOOT: //shoot
 		{
 			CStack *curStack = gs->curB->getStack(ba.stackNumber),
 				*destStack= gs->curB->getStackT(ba.destinationTile);
@@ -3180,7 +3137,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			sendAndApply(&EndAction());
 			break;
 		}
-	case 9: //catapult
+	case BattleAction::CATAPULT: //catapult
 		{
 			sendAndApply(&StartAction(ba));
 			const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
@@ -3282,7 +3239,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			sendAndApply(&EndAction());
 			break;
 		}
-	case 12: //healing
+		case BattleAction::STACK_HEAL: //healing
 		{
 			sendAndApply(&StartAction(ba));
 			const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
@@ -3609,7 +3566,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
 				bsa.damageAmount = gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl, usedSpellPower);
 				bsa.stackAttacked = (*it)->ID;
 				bsa.attackerID = -1;
-				prepareAttacked(bsa,*it);
+				(*it)->prepareAttacked(bsa);
 				si.stacks.push_back(bsa);
 			}
 			if(!si.stacks.empty())

+ 1 - 2
server/CGameHandler.h

@@ -113,12 +113,11 @@ public:
 
 	////used only in endBattle - don't touch elsewhere
 	boost::function<void(BattleResult*)> * battleEndCallback;
-	const CArmedInstance * bEndArmy1, * bEndArmy2;
+	//const CArmedInstance * bEndArmy1, * bEndArmy2;
 	bool visitObjectAfterVictory;
 	//
 	void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle
 	void prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance); //distance - number of hexes travelled before attacking
-	void prepareAttacked(BattleStackAttacked &bsa, const CStack *def);
 	void checkForBattleEnd( std::vector<CStack*> &stacks );
 	void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);