فهرست منبع

Merge pull request #317 from vcmi/attackerOwned

Refactored CStack::attackerOwned to CStack::side
ArseniyShestakov 8 سال پیش
والد
کامیت
5dfb7a5771

+ 1 - 1
AI/BattleAI/BattleAI.h

@@ -24,7 +24,7 @@ struct CurrentOffensivePotential
 	{
 		for(auto stack : cbc->battleGetStacks())
 		{
-			if(stack->attackerOwned == !side)
+			if(stack->side == side)
 				ourAttacks[stack] = PotentialTargets(stack);
 			else
 				enemyAttacks[stack] = PotentialTargets(stack);

+ 1 - 1
AI/BattleAI/PotentialTargets.cpp

@@ -18,7 +18,7 @@ PotentialTargets::PotentialTargets(const CStack *attacker, const HypotheticChang
 	for(const CStack *enemy : getCbc()->battleGetStacks())
 	{
 		//Consider only stacks of different owner
-		if(enemy->attackerOwned == attacker->attackerOwned)
+		if(enemy->side == attacker->side)
 			continue;
 
 		auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility

+ 1 - 1
AI/BattleAI/ThreatMap.cpp

@@ -31,7 +31,7 @@ ThreatMap::ThreatMap(const CStack *Endangered) : endangered(Endangered)
 	for(const CStack *enemy : getCbc()->battleGetStacks())
 	{
 		//Consider only stacks of different owner
-		if(enemy->attackerOwned == endangered->attackerOwned)
+		if(enemy->side == endangered->side)
 			continue;
 
 		//Look-up which tiles can be melee-attacked

+ 1 - 1
client/Client.cpp

@@ -832,7 +832,7 @@ void CClient::commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> ba
 		battleInt->yourTacticPhase(gs->curB->tacticDistance);
 		if(gs && !!gs->curB && gs->curB->tacticDistance) //while awaiting for end of tactics phase, many things can happen (end of battle... or game)
 		{
-			MakeAction ma(BattleAction::makeEndOFTacticPhase(gs->curB->playerToSide(battleInt->playerID)));
+			MakeAction ma(BattleAction::makeEndOFTacticPhase(gs->curB->playerToSide(battleInt->playerID).get()));
 			sendRequest(&ma, battleInt->playerID);
 		}
 	}

+ 2 - 2
client/battle/CBattleAnimations.cpp

@@ -338,7 +338,7 @@ bool CMeleeAttackAnimation::init()
 	static const CCreatureAnim::EAnimType mutPosToGroup[] = {CCreatureAnim::ATTACK_UP, CCreatureAnim::ATTACK_UP,
 		CCreatureAnim::ATTACK_FRONT, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_FRONT};
 
-	int revShiftattacker = (attackingStack->attackerOwned ? -1 : 1);
+	int revShiftattacker = (attackingStack->side == BattleSide::ATTACKER ? -1 : 1);
 
 	int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest);
 	if(mutPos == -1 && attackingStack->doubleWide())
@@ -988,7 +988,7 @@ bool CSpellEffectAnimation::init()
 
 			// Correction for 2-hex creatures.
 			if (destStack != nullptr && destStack->doubleWide())
-				be.x += (destStack->attackerOwned ? -1 : 1)*tilePos.w/2;
+				be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2;
 
 			//Indicate if effect should be drawn on top of everything or just on top of the hex
 			be.position = destTile;

+ 70 - 68
client/battle/CBattleInterface.cpp

@@ -949,7 +949,7 @@ void CBattleInterface::bConsoleDownf()
 
 void CBattleInterface::newStack(const CStack *stack)
 {
-	creDir[stack->ID] = stack->attackerOwned; // must be set before getting stack position
+	creDir[stack->ID] = stack->side == BattleSide::ATTACKER; // must be set before getting stack position
 
 	Point coords = CClickableHex::getXYUnitAnim(stack->position, stack, this);
 
@@ -1180,15 +1180,15 @@ bool CBattleInterface::isCatapultAttackable(BattleHex hex) const
 	return state != EWallState::DESTROYED && state != EWallState::NONE;
 }
 
-const CGHeroInstance *CBattleInterface::getActiveHero()
+const CGHeroInstance * CBattleInterface::getActiveHero()
 {
 	const CStack *attacker = activeStack;
-	if (!attacker)
+	if(!attacker)
 	{
 		return nullptr;
 	}
 
-	if (attacker->attackerOwned)
+	if(attacker->side == BattleSide::ATTACKER)
 	{
 		return attackingHeroInstance;
 	}
@@ -1801,7 +1801,7 @@ void CBattleInterface::endAction(const BattleAction* action)
 
 	for (const CStack *s : stacks)
 	{
-		if (s && creDir[s->ID] != bool(s->attackerOwned) && s->alive()
+		if (s && creDir[s->ID] != (s->side == BattleSide::ATTACKER) && s->alive()
 		   && creAnims[s->ID]->isIdle())
 		{
 			addNewAnim(new CReverseAnimation(this, s, s->position, false));
@@ -2033,10 +2033,10 @@ std::string formatDmgRange(std::pair<ui32, ui32> dmgRange)
 		return (boost::format("%d") % dmgRange.first).str();
 }
 
-bool CBattleInterface::canStackMoveHere (const CStack *activeStack, BattleHex myNumber)
+bool CBattleInterface::canStackMoveHere(const CStack * activeStack, BattleHex myNumber)
 {
 	std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes (activeStack, false);
-	int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1);
+	BattleHex shiftedDest = myNumber.cloneInDirection(activeStack->destShiftDir(), false);
 
 	if (vstd::contains(acc, myNumber))
 		return true;
@@ -2262,14 +2262,14 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 
 				realizeAction = [=]
 				{
-					if (activeStack->doubleWide())
+					if(activeStack->doubleWide())
 					{
 						std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
-						int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1);
-						if (vstd::contains(acc, myNumber))
-							giveCommand (Battle::WALK ,myNumber, activeStack->ID);
-						else if (vstd::contains(acc, shiftedDest))
-							giveCommand (Battle::WALK, shiftedDest, activeStack->ID);
+						BattleHex shiftedDest = myNumber.cloneInDirection(activeStack->destShiftDir(), false);
+						if(vstd::contains(acc, myNumber))
+							giveCommand(Battle::WALK, myNumber, activeStack->ID);
+						else if(vstd::contains(acc, shiftedDest))
+							giveCommand(Battle::WALK, shiftedDest, activeStack->ID);
 					}
 					else
 					{
@@ -2547,17 +2547,17 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
 		{
 			bool doubleWide = activeStack->doubleWide();
 			destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ) +
-				(activeStack->attackerOwned && doubleWide ? 1 : 0);
-			if (vstd::contains(occupyableHexes, destHex))
+				(activeStack->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
+			if(vstd::contains(occupyableHexes, destHex))
 				return destHex;
-			else if (activeStack->attackerOwned) //if we are attacker
+			else if(activeStack->side == BattleSide::ATTACKER)
 			{
 				if (vstd::contains(occupyableHexes, destHex+1))
 					return destHex+1;
 			}
 			else //if we are defender
 			{
-				if (vstd::contains(occupyableHexes, destHex-1))
+				if(vstd::contains(occupyableHexes, destHex-1))
 					return destHex-1;
 			}
 			break;
@@ -2567,21 +2567,21 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
 			destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH-1 : GameConstants::BFIELD_WIDTH );
 			if (vstd::contains(occupyableHexes, destHex))
 				return destHex;
-			else if (activeStack->attackerOwned) //if we are attacker
+			else if(activeStack->side == BattleSide::ATTACKER)
 			{
-				if (vstd::contains(occupyableHexes, destHex+1))
+				if(vstd::contains(occupyableHexes, destHex+1))
 					return destHex+1;
 			}
-			else //if we are defender
+			else //we are defender
 			{
-				if (vstd::contains(occupyableHexes, destHex-1))
+				if(vstd::contains(occupyableHexes, destHex-1))
 					return destHex-1;
 			}
 			break;
 		}
 	case 8: //from left
 		{
-			if (activeStack->doubleWide() && !activeStack->attackerOwned)
+			if(activeStack->doubleWide() && activeStack->side == activeStack->side == BattleSide::DEFENDER)
 			{
 				std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
 				if (vstd::contains(acc, myNumber))
@@ -2597,17 +2597,17 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
 		}
 	case 9: //from top left
 		{
-			destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH+1 : GameConstants::BFIELD_WIDTH );
-			if (vstd::contains(occupyableHexes, destHex))
+			destHex = myNumber - ((myNumber/GameConstants::BFIELD_WIDTH) % 2 ? GameConstants::BFIELD_WIDTH + 1 : GameConstants::BFIELD_WIDTH);
+			if(vstd::contains(occupyableHexes, destHex))
 				return destHex;
-			else if (activeStack->attackerOwned) //if we are attacker
+			else if(activeStack->side == BattleSide::ATTACKER)
 			{
-				if (vstd::contains(occupyableHexes, destHex+1))
+				if(vstd::contains(occupyableHexes, destHex+1))
 					return destHex+1;
 			}
 			else //if we are defender
 			{
-				if (vstd::contains(occupyableHexes, destHex-1))
+				if(vstd::contains(occupyableHexes, destHex-1))
 					return destHex-1;
 			}
 			break;
@@ -2616,27 +2616,27 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
 		{
 			bool doubleWide = activeStack->doubleWide();
 			destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ) +
-				(activeStack->attackerOwned && doubleWide ? 1 : 0);
-			if (vstd::contains(occupyableHexes, destHex))
+				(activeStack->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
+			if(vstd::contains(occupyableHexes, destHex))
 				return destHex;
-			else if (activeStack->attackerOwned) //if we are attacker
+			else if(activeStack->side == BattleSide::ATTACKER)
 			{
-				if (vstd::contains(occupyableHexes, destHex+1))
+				if(vstd::contains(occupyableHexes, destHex+1))
 					return destHex+1;
 			}
 			else //if we are defender
 			{
-				if (vstd::contains(occupyableHexes, destHex-1))
+				if(vstd::contains(occupyableHexes, destHex-1))
 					return destHex-1;
 			}
 			break;
 		}
 	case 11: //from right
 		{
-			if (activeStack->doubleWide() && activeStack->attackerOwned)
+			if(activeStack->doubleWide() && activeStack->side == BattleSide::ATTACKER)
 			{
 				std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
-				if (vstd::contains(acc, myNumber))
+				if(vstd::contains(acc, myNumber))
 					return myNumber + 1;
 				else
 					return myNumber + 2;
@@ -2650,16 +2650,16 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
 	case 13: //from bottom
 		{
 			destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 );
-			if (vstd::contains(occupyableHexes, destHex))
+			if(vstd::contains(occupyableHexes, destHex))
 				return destHex;
-			else if (attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker
+			else if(activeStack->side == BattleSide::ATTACKER)
 			{
-				if (vstd::contains(occupyableHexes, destHex+1))
+				if(vstd::contains(occupyableHexes, destHex+1))
 					return destHex+1;
 			}
 			else //if we are defender
 			{
-				if (vstd::contains(occupyableHexes, destHex-1))
+				if(vstd::contains(occupyableHexes, destHex-1))
 					return destHex-1;
 			}
 			break;
@@ -2669,14 +2669,14 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
 			destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 );
 			if (vstd::contains(occupyableHexes, destHex))
 				return destHex;
-			else if (attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker
+			else if(activeStack->side == BattleSide::ATTACKER)
 			{
-				if (vstd::contains(occupyableHexes, destHex+1))
+				if(vstd::contains(occupyableHexes, destHex+1))
 					return destHex+1;
 			}
 			else //if we are defender
 			{
-				if (vstd::contains(occupyableHexes, destHex-1))
+				if(vstd::contains(occupyableHexes, destHex-1))
 					return destHex-1;
 			}
 			break;
@@ -3082,52 +3082,52 @@ void CBattleInterface::showAbsoluteObstacles(SDL_Surface *to)
 
 void CBattleInterface::showHighlightedHexes(SDL_Surface *to)
 {
-	for (int b=0; b<GameConstants::BFIELD_SIZE; ++b)
+	for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
 	{
-		if (bfield[b]->strictHovered && bfield[b]->hovered)
+		if(bfield[b]->strictHovered && bfield[b]->hovered)
 		{
-			if (previouslyHoveredHex == -1)
+			if(previouslyHoveredHex == -1)
 				previouslyHoveredHex = b; //something to start with
-			if (currentlyHoveredHex == -1)
+			if(currentlyHoveredHex == -1)
 				currentlyHoveredHex = b; //something to start with
 
-			if (currentlyHoveredHex != b) //repair hover info
+			if(currentlyHoveredHex != b) //repair hover info
 			{
 				previouslyHoveredHex = currentlyHoveredHex;
 				currentlyHoveredHex = b;
 			}
-			if (settings["battle"]["mouseShadow"].Bool())
+			if(settings["battle"]["mouseShadow"].Bool())
 			{
 				const ISpellCaster *caster = nullptr;
 				const CSpell *spell = nullptr;
 
-                if (spellToCast)//hero casts spell
+                if(spellToCast)//hero casts spell
 				{
 					spell = SpellID(spellToCast->additionalInfo).toSpell();
-					caster = activeStack->attackerOwned ? attackingHeroInstance : defendingHeroInstance;
+					caster = getActiveHero();
 				}
-				else if (creatureSpellToCast >= 0 && stackCanCastSpell && creatureCasting)//stack casts spell
+				else if(creatureSpellToCast >= 0 && stackCanCastSpell && creatureCasting)//stack casts spell
 				{
-                    spell = SpellID(creatureSpellToCast).toSpell();
-                    caster = activeStack;
+					spell = SpellID(creatureSpellToCast).toSpell();
+					caster = activeStack;
 				}
 
-				if (caster && spell) //when casting spell
+				if(caster && spell) //when casting spell
 				{
 					//calculating spell school level
 					ui8 schoolLevel = caster->getSpellSchoolLevel(spell);
 
 					// printing shaded hex(es)
 					auto shaded = spell->rangeInHexes(currentlyHoveredHex, schoolLevel, curInt->cb->battleGetMySide());
-					for (BattleHex shadedHex : shaded)
+					for(BattleHex shadedHex : shaded)
 					{
-						if ((shadedHex.getX() != 0) && (shadedHex.getX() != GameConstants::BFIELD_WIDTH -1))
+						if((shadedHex.getX() != 0) && (shadedHex.getX() != GameConstants::BFIELD_WIDTH - 1))
 							showHighlightedHex(to, shadedHex);
 					}
 				}
-				else if (active)//always highlight pointed hex
+				else if(active)//always highlight pointed hex
 				{
-					if (currentlyHoveredHex.getX() != 0
+					if(currentlyHoveredHex.getX() != 0
 					 && currentlyHoveredHex.getX() != GameConstants::BFIELD_WIDTH - 1)
 						showHighlightedHex(to, currentlyHoveredHex);
 				}
@@ -3135,18 +3135,18 @@ void CBattleInterface::showHighlightedHexes(SDL_Surface *to)
 		}
 	}
 
-	if (activeStack && settings["battle"]["stackRange"].Bool())
+	if(activeStack && settings["battle"]["stackRange"].Bool())
 	{
 		std::set<BattleHex> set = curInt->cb->battleGetAttackedHexes(activeStack, currentlyHoveredHex, attackingHex);
-		for (BattleHex hex : set)
+		for(BattleHex hex : set)
 			showHighlightedHex(to, hex);
 
 		// display the movement shadow of the stack at b (i.e. stack under mouse)
-		const CStack *const shere = curInt->cb->battleGetStackByPos(currentlyHoveredHex, false);
-		if (shere && shere != activeStack && shere->alive())
+		const CStack * const shere = curInt->cb->battleGetStackByPos(currentlyHoveredHex, false);
+		if(shere && shere != activeStack && shere->alive())
 		{
 			std::vector<BattleHex> v = curInt->cb->battleGetAvailableHexes(shere, true );
-			for (BattleHex hex : v)
+			for(BattleHex hex : v)
 				showHighlightedHex(to, hex);
 		}
 	}
@@ -3321,13 +3321,15 @@ void CBattleInterface::showAliveStacks(SDL_Surface *to, std::vector<const CStack
 		//printing amount
 		if (isAmountBoxVisible(stack))
 		{
-			const BattleHex nextPos = stack->position + (stack->attackerOwned ? 1 : -1);
-			const bool edge = stack->position % GameConstants::BFIELD_WIDTH == (stack->attackerOwned ? GameConstants::BFIELD_WIDTH - 2 : 1);
+			const int sideShift = stack->side == BattleSide::ATTACKER ? 1 : -1;
+			const int reverseSideShift = stack->side == BattleSide::ATTACKER ? -1 : 1;
+			const BattleHex nextPos = stack->position + sideShift;
+			const bool edge = stack->position % GameConstants::BFIELD_WIDTH == (stack->side == BattleSide::ATTACKER ? GameConstants::BFIELD_WIDTH - 2 : 1);
 			const bool moveInside = !edge && !stackCountOutsideHexes[nextPos];
-			int xAdd = (stack->attackerOwned ? 220 : 202) +
-					   (stack->doubleWide() ? 44 : 0) *(stack->attackerOwned ? +1 : -1) +
-					   (moveInside ? amountNormal->w + 10 : 0) *(stack->attackerOwned ? -1 : +1);
-			int yAdd = 260 + ((stack->attackerOwned || moveInside) ? 0 : -15);
+			int xAdd = (stack->side == BattleSide::ATTACKER ? 220 : 202) +
+					   (stack->doubleWide() ? 44 : 0) * sideShift +
+					   (moveInside ? amountNormal->w + 10 : 0) * reverseSideShift;
+			int yAdd = 260 + ((stack->side == BattleSide::ATTACKER || moveInside) ? 0 : -15);
 
 			//blitting amount background box
 			SDL_Surface *amountBG = amountNormal;

+ 2 - 2
client/battle/CBattleInterfaceClasses.cpp

@@ -384,7 +384,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 		{
 			auto stacks = owner.cb->battleGetAllStacks();
 			vstd::erase_if(stacks, [i](const CStack *stack) //erase stack of other side and not coming from garrison
-				{ return stack->attackerOwned == i  ||  !stack->base; });
+				{ return stack->side != i  ||  !stack->base; });
 
 			auto best = vstd::maxElementByFun(stacks, [](const CStack *stack){ return stack->type->AIValue; });
 			if(best != stacks.end()) //should be always but to be safe...
@@ -542,7 +542,7 @@ Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBatt
 			//shifting position for double - hex creatures
 			if(stack->doubleWide())
 			{
-				if(stack->attackerOwned)
+				if(stack->side == BattleSide::ATTACKER)
 				{
 					if(cbi->creDir[stack->ID])
 						ret.x -= 44;

+ 31 - 16
lib/CStack.cpp

@@ -15,8 +15,8 @@
 #include "NetPacks.h"
 
 
-CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S)
-	: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
+CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, ui8 Side, SlotID S)
+	: base(Base), ID(I), owner(O), slot(S), side(Side),
 	counterAttacksPerformed(0),counterAttacksTotalCache(0), cloneID(-1),
 	firstHPleft(-1), position(), shots(0), casts(0), resurrected(0)
 {
@@ -30,8 +30,8 @@ CStack::CStack()
 	init();
 	setNodeType(STACK_BATTLE);
 }
-CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S)
-	: base(nullptr), ID(I), owner(O), slot(S), attackerOwned(AO),
+CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, ui8 Side, SlotID S)
+	: base(nullptr), ID(I), owner(O), slot(S), side(Side),
 	counterAttacksPerformed(0), counterAttacksTotalCache(0), cloneID(-1),
 	firstHPleft(-1), position(), shots(0), casts(0), resurrected(0)
 {
@@ -49,7 +49,7 @@ void CStack::init()
 	firstHPleft = -1;
 	owner = PlayerColor::NEUTRAL;
 	slot = SlotID(255);
-	attackerOwned = false;
+	side = 1;
 	position = BattleHex();
 	counterAttacksPerformed = 0;
 	counterAttacksTotalCache = 0;
@@ -145,9 +145,9 @@ BattleHex CStack::occupiedHex() const
 
 BattleHex CStack::occupiedHex(BattleHex assumedPos) const
 {
-	if (doubleWide())
+	if(doubleWide())
 	{
-		if (attackerOwned)
+		if(side == BattleSide::ATTACKER)
 			return assumedPos - 1;
 		else
 			return assumedPos + 1;
@@ -165,17 +165,17 @@ std::vector<BattleHex> CStack::getHexes() const
 
 std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos) const
 {
-	return getHexes(assumedPos, doubleWide(), attackerOwned);
+	return getHexes(assumedPos, doubleWide(), side);
 }
 
-std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned)
+std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos, bool twoHex, ui8 side)
 {
 	std::vector<BattleHex> hexes;
 	hexes.push_back(assumedPos);
 
-	if (twoHex)
+	if(twoHex)
 	{
-		if (AttackerOwned)
+		if(side == BattleSide::ATTACKER)
 			hexes.push_back(assumedPos - 1);
 		else
 			hexes.push_back(assumedPos + 1);
@@ -193,10 +193,10 @@ std::vector<BattleHex> CStack::getSurroundingHexes(BattleHex attackerPos) const
 {
 	BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : position; //use hypothetical position
 	std::vector<BattleHex> hexes;
-	if (doubleWide())
+	if(doubleWide())
 	{
 		const int WN = GameConstants::BFIELD_WIDTH;
-		if(attackerOwned)
+		if(side == BattleSide::ATTACKER)
 		{ //position is equal to front hex
 			BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+2 : WN+1 ), hexes);
 			BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes);
@@ -226,6 +226,21 @@ std::vector<BattleHex> CStack::getSurroundingHexes(BattleHex attackerPos) const
 	}
 }
 
+BattleHex::EDir CStack::destShiftDir() const
+{
+	if(doubleWide())
+	{
+		if(side == BattleSide::ATTACKER)
+			return BattleHex::EDir::RIGHT;
+		else
+			return BattleHex::EDir::LEFT;
+	}
+	else
+	{
+		return BattleHex::EDir::NONE;
+	}
+}
+
 std::vector<si32> CStack::activeSpells() const
 {
 	std::vector<si32> ret;
@@ -382,11 +397,11 @@ bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defen
 	return
 		(BattleHex::mutualPosition(attackerPos, defenderPos) >= 0)						//front <=> front
 		|| (attacker->doubleWide()									//back <=> front
-		&& BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos) >= 0)
+		&& BattleHex::mutualPosition(attackerPos + (attacker->side == BattleSide::ATTACKER ? -1 : 1), defenderPos) >= 0)
 		|| (defender->doubleWide()									//front <=> back
-		&& BattleHex::mutualPosition(attackerPos, defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0)
+		&& BattleHex::mutualPosition(attackerPos, defenderPos + (defender->side == BattleSide::ATTACKER ? -1 : 1)) >= 0)
 		|| (defender->doubleWide() && attacker->doubleWide()//back <=> back
-		&& BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0);
+		&& BattleHex::mutualPosition(attackerPos + (attacker->side == BattleSide::ATTACKER ? -1 : 1), defenderPos + (defender->side == BattleSide::ATTACKER ? -1 : 1)) >= 0);
 
 }
 

+ 7 - 5
lib/CStack.h

@@ -24,7 +24,7 @@ public:
 	ui32 firstHPleft; //HP of first creature in stack
 	PlayerColor owner; //owner - player colour (255 for neutrals)
 	SlotID slot;  //slot - position in garrison (may be 255 for neutrals/called creatures)
-	bool attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
+	ui8 side;
 	BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
 	///how many times this stack has been counterattacked this round
 	ui8 counterAttacksPerformed;
@@ -39,8 +39,8 @@ public:
 	//overrides
 	const CCreature* getCreature() const {return type;}
 
-	CStack(const CStackInstance *base, PlayerColor O, int I, bool AO, SlotID S); //c-tor
-	CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S = SlotID(255)); //c-tor
+	CStack(const CStackInstance *base, PlayerColor O, int I, ui8 Side, SlotID S); //c-tor
+	CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, ui8 Side, SlotID S = SlotID(255)); //c-tor
 	CStack(); //c-tor
 	~CStack();
 	std::string nodeName() const override;
@@ -73,10 +73,12 @@ public:
 	BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1
 	std::vector<BattleHex> getHexes() const; //up to two occupied hexes, starting from front
 	std::vector<BattleHex> getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
-	static std::vector<BattleHex> getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned); //up to two occupied hexes, starting from front
+	static std::vector<BattleHex> getHexes(BattleHex assumedPos, bool twoHex, ui8 side); //up to two occupied hexes, starting from front
 	bool coversPos(BattleHex position) const; //checks also if unit is double-wide
 	std::vector<BattleHex> getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
 
+	BattleHex::EDir destShiftDir() const;
+
 	std::pair<int,int> countKilledByAttack(int damageReceived) const; //returns pair<killed count, new left HP>
 	void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional<int> customCount = boost::none) const; //requires bsa.damageAmout filled
 
@@ -110,7 +112,7 @@ public:
 		assert(isIndependentNode());
 		h & static_cast<CBonusSystemNode&>(*this);
 		h & static_cast<CStackBasicDescriptor&>(*this);
-		h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacksPerformed
+		h & ID & baseAmount & firstHPleft & owner & slot & side & position & state & counterAttacksPerformed
 			& shots & casts & count & resurrected;
 
 		const CArmedInstance *army = (base ? base->armyObj : nullptr);

+ 1 - 0
lib/IGameEventsReceiver.h

@@ -11,6 +11,7 @@
 
 
 #include "battle/BattleHex.h"
+#include "GameConstants.h"
 #include "int3.h"
 
 class CGTownInstance;

+ 3 - 3
lib/NetPacks.h

@@ -1628,13 +1628,13 @@ struct BattleStacksRemoved : public CPackForClient
 struct BattleStackAdded : public CPackForClient
 {
 	BattleStackAdded()
-		: attacker(0), amount(0), pos(0), summoned(0), newStackID(0)
+		: side(0), amount(0), pos(0), summoned(0), newStackID(0)
 	{};
 
 	DLL_LINKAGE void applyGs(CGameState *gs);
 	void applyCl(CClient *cl);
 
-	int attacker; // if true, stack belongs to attacker
+	ui8 side;
 	CreatureID creID;
 	int amount;
 	int pos;
@@ -1645,7 +1645,7 @@ struct BattleStackAdded : public CPackForClient
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & attacker & creID & amount & pos & summoned;
+		h & side & creID & amount & pos & summoned;
 	}
 };
 

+ 2 - 2
lib/NetPacksLib.cpp

@@ -1384,7 +1384,7 @@ void BattleStackMoved::applyGs(CGameState *gs)
 		{
 			SpellCreatedObstacle *sands = dynamic_cast<SpellCreatedObstacle*>(oi.get());
 			assert(sands);
-			if(sands->casterSide != !s->attackerOwned)
+			if(sands->casterSide != s->side)
 				sands->visibleForAnotherSide = true;
 		}
 	}
@@ -1803,7 +1803,7 @@ DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs)
 	}
 
 	CStackBasicDescriptor csbd(creID, amount);
-	CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks?
+	CStack * addedStack = gs->curB->generateNewStack(csbd, side, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks?
 	if (summoned)
 		addedStack->state.insert(EBattleStackState::SUMMONED);
 

+ 5 - 19
lib/battle/AccessibilityInfo.cpp

@@ -10,16 +10,17 @@
 #include "StdInc.h"
 #include "AccessibilityInfo.h"
 #include "../CStack.h"
+#include "../GameConstants.h"
 
 bool AccessibilityInfo::accessible(BattleHex tile, const CStack * stack) const
 {
-	return accessible(tile, stack->doubleWide(), stack->attackerOwned);
+	return accessible(tile, stack->doubleWide(), stack->side);
 }
 
-bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const
+bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, ui8 side) const
 {
 	// All hexes that stack would cover if standing on tile have to be accessible.
-	for(auto hex : CStack::getHexes(tile, doubleWide, attackerOwned))
+	for(auto hex : CStack::getHexes(tile, doubleWide, side))
 	{
 		// If the hex is out of range then the tile isn't accessible
 		if(!hex.isValid())
@@ -27,25 +28,10 @@ bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, bool attacke
 		// If we're no defender which step on gate and the hex isn't accessible, then the tile
 		// isn't accessible
 		else if(at(hex) != EAccessibility::ACCESSIBLE &&
-				!(at(hex) == EAccessibility::GATE && !attackerOwned))
+				!(at(hex) == EAccessibility::GATE && side == BattleSide::DEFENDER))
 		{
 			return false;
 		}
 	}
 	return true;
 }
-
-bool AccessibilityInfo::occupiable(const CStack * stack, BattleHex tile) const
-{
-	//obviously, we can occupy tile by standing on it
-	if(accessible(tile, stack))
-		return true;
-	if(stack->doubleWide())
-	{
-		//Check the tile next to -> if stack stands there, it'll also occupy considered hex
-		const BattleHex anotherTile = tile + (stack->attackerOwned ? BattleHex::RIGHT : BattleHex::LEFT);
-		if(accessible(anotherTile, stack))
-			return true;
-	}
-	return false;
-}

+ 2 - 2
lib/battle/AccessibilityInfo.h

@@ -9,6 +9,7 @@
  */
 #pragma once
 #include "BattleHex.h"
+#include "../GameConstants.h"
 
 class CStack;
 
@@ -29,7 +30,6 @@ typedef std::array<EAccessibility, GameConstants::BFIELD_SIZE> TAccessibilityArr
 
 struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray
 {
-	bool occupiable(const CStack * stack, BattleHex tile) const;
 	bool accessible(BattleHex tile, const CStack * stack) const; //checks for both tiles if stack is double wide
-	bool accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const; //checks for both tiles if stack is double wide
+	bool accessible(BattleHex tile, bool doubleWide, ui8 side) const; //checks for both tiles if stack is double wide
 };

+ 6 - 6
lib/battle/BattleAction.cpp

@@ -27,7 +27,7 @@ BattleAction::BattleAction():
 BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed)
 {
 	BattleAction ba;
-	ba.side = !healer->attackerOwned;
+	ba.side = healer->side;
 	ba.actionType = STACK_HEAL;
 	ba.stackNumber = healer->ID;
 	ba.destinationTile = healed->position;
@@ -37,7 +37,7 @@ BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed
 BattleAction BattleAction::makeDefend(const CStack * stack)
 {
 	BattleAction ba;
-	ba.side = !stack->attackerOwned;
+	ba.side = stack->side;
 	ba.actionType = DEFEND;
 	ba.stackNumber = stack->ID;
 	return ba;
@@ -47,7 +47,7 @@ BattleAction BattleAction::makeDefend(const CStack * stack)
 BattleAction BattleAction::makeMeleeAttack(const CStack * stack, const CStack * attacked, BattleHex attackFrom /*= BattleHex::INVALID*/)
 {
 	BattleAction ba;
-	ba.side = !stack->attackerOwned;
+	ba.side = stack->side;
 	ba.actionType = WALK_AND_ATTACK;
 	ba.stackNumber = stack->ID;
 	ba.destinationTile = attackFrom;
@@ -58,7 +58,7 @@ BattleAction BattleAction::makeMeleeAttack(const CStack * stack, const CStack *
 BattleAction BattleAction::makeWait(const CStack * stack)
 {
 	BattleAction ba;
-	ba.side = !stack->attackerOwned;
+	ba.side = stack->side;
 	ba.actionType = WAIT;
 	ba.stackNumber = stack->ID;
 	return ba;
@@ -67,7 +67,7 @@ BattleAction BattleAction::makeWait(const CStack * stack)
 BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack * target)
 {
 	BattleAction ba;
-	ba.side = !shooter->attackerOwned;
+	ba.side = shooter->side;
 	ba.actionType = SHOOT;
 	ba.stackNumber = shooter->ID;
 	ba.destinationTile = target->position;
@@ -77,7 +77,7 @@ BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack *
 BattleAction BattleAction::makeMove(const CStack * stack, BattleHex dest)
 {
 	BattleAction ba;
-	ba.side = !stack->attackerOwned;
+	ba.side = stack->side;
 	ba.actionType = WALK;
 	ba.stackNumber = stack->ID;
 	ba.destinationTile = dest;

+ 1 - 0
lib/battle/BattleAction.h

@@ -9,6 +9,7 @@
  */
 #pragma once
 #include "BattleHex.h"
+#include "../GameConstants.h"
 
 class CStack;
 

+ 6 - 3
lib/battle/BattleHex.cpp

@@ -9,6 +9,7 @@
  */
 #include "StdInc.h"
 #include "BattleHex.h"
+#include "../GameConstants.h"
 
 BattleHex::BattleHex() : hex(INVALID) {}
 
@@ -99,6 +100,8 @@ BattleHex& BattleHex::moveInDirection(EDir dir, bool hasToBeValid)
 	case LEFT:
 		setXY(x-1, y, hasToBeValid);
 		break;
+	case NONE:
+		break;
 	default:
 		throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n");
 		break;
@@ -160,7 +163,7 @@ void BattleHex::checkAndPush(BattleHex tile, std::vector<BattleHex> & ret)
 		ret.push_back(tile);
 }
 
-BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities)
+BattleHex BattleHex::getClosestTile(ui8 side, BattleHex initialPos, std::set<BattleHex> & possibilities)
 {
 	std::vector<BattleHex> sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :(
 	BattleHex initialHex = BattleHex(initialPos);
@@ -175,11 +178,11 @@ BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, st
 		return closestDistance < here.getDistance (initialPos, here);
 	};
 	vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
-	auto compareHorizontal = [attackerOwned, initialPos](const BattleHex left, const BattleHex right) -> bool
+	auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool
 	{
 		if(left.getX() != right.getX())
 		{
-			if (attackerOwned)
+			if(side == BattleSide::ATTACKER)
 				return left.getX() > right.getX(); //find furthest right
 			else
 				return left.getX() < right.getX(); //find furthest left

+ 24 - 3
lib/battle/BattleHex.h

@@ -8,14 +8,35 @@
  *
  */
 #pragma once
-#include "../GameConstants.h"
+
+//TODO: change to enum class
+
+namespace BattleSide
+{
+	enum
+	{
+		ATTACKER = 0,
+		DEFENDER = 1
+	};
+}
+
+typedef boost::optional<ui8> BattleSideOpt;
 
 // for battle stacks' positions
 struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design
 {
 	si16 hex;
 	static const si16 INVALID = -1;
-	enum EDir { TOP_LEFT, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT};
+	enum EDir
+	{
+		TOP_LEFT,
+		TOP_RIGHT,
+		RIGHT,
+		BOTTOM_RIGHT,
+		BOTTOM_LEFT,
+		LEFT,
+		NONE
+	};
 
 	BattleHex();
 	BattleHex(si16 _hex);
@@ -39,7 +60,7 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
 	static signed char mutualPosition(BattleHex hex1, BattleHex hex2);
 	static char getDistance(BattleHex hex1, BattleHex hex2);
 	static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret);
-	static BattleHex getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
+	static BattleHex getClosestTile(ui8 side, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
 
 	template <typename Handler>
 	void serialize(Handler &h, const int version)

+ 18 - 18
lib/battle/BattleInfo.cpp

@@ -26,7 +26,7 @@ const CStack * BattleInfo::getNextStack() const
 		return nullptr;
 }
 
-int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initialPos) const
+int BattleInfo::getAvaliableHex(CreatureID creID, ui8 side, int initialPos) const
 {
 	bool twoHex = VLC->creh->creatures[creID]->isDoubleWide();
 	//bool flying = VLC->creh->creatures[creID]->isFlying();
@@ -36,7 +36,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia
 		pos = initialPos;
 	else //summon elementals depending on player side
 	{
- 		if (attackerOwned)
+ 		if(side == BattleSide::ATTACKER)
 	 		pos = 0; //top left
  		else
  			pos = GameConstants::BFIELD_WIDTH - 1; //top right
@@ -46,7 +46,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia
 
 	std::set<BattleHex> occupyable;
 	for(int i = 0; i < accessibility.size(); i++)
-		if(accessibility.accessible(i, twoHex, attackerOwned))
+		if(accessibility.accessible(i, twoHex, side))
 			occupyable.insert(i);
 
 	if (occupyable.empty())
@@ -54,7 +54,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia
 		return BattleHex::INVALID; //all tiles are covered
 	}
 
-	return BattleHex::getClosestTile(attackerOwned, pos, occupyable);
+	return BattleHex::getClosestTile(side, pos, occupyable);
 }
 
 std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack)
@@ -104,28 +104,28 @@ void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
 		si32 killed = (st->alive() ? (st->baseAmount - st->count + st->resurrected) : st->baseAmount);
 		vstd::amax(killed, 0);
 		if(killed)
-			casualties[!st->attackerOwned][st->getCreature()->idNumber] += killed;
+			casualties[st->side][st->getCreature()->idNumber] += killed;
 	}
 }
 
-CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const
+CStack * BattleInfo::generateNewStack(const CStackInstance & base, ui8 side, SlotID slot, BattleHex position) const
 {
 	int stackID = getIdForNewStack();
-	PlayerColor owner = sides[attackerOwned ? 0 : 1].color;
+	PlayerColor owner = sides[side].color;
 	assert((owner >= PlayerColor::PLAYER_LIMIT) ||
 		(base.armyObj && base.armyObj->tempOwner == owner));
 
-	auto ret = new CStack(&base, owner, stackID, attackerOwned, slot);
-	ret->position = getAvaliableHex (base.getCreatureID(), attackerOwned, position); //TODO: what if no free tile on battlefield was found?
+	auto ret = new CStack(&base, owner, stackID, side, slot);
+	ret->position = getAvaliableHex(base.getCreatureID(), side, position); //TODO: what if no free tile on battlefield was found?
 	ret->state.insert(EBattleStackState::ALIVE); //alive state indication
 	return ret;
 }
 
-CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const
+CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor & base, ui8 side, SlotID slot, BattleHex position) const
 {
 	int stackID = getIdForNewStack();
-	PlayerColor owner = sides[attackerOwned ? 0 : 1].color;
-	auto ret = new CStack(&base, owner, stackID, attackerOwned, slot);
+	PlayerColor owner = sides[side].color;
+	auto ret = new CStack(&base, owner, stackID, side, slot);
 	ret->position = position;
 	ret->state.insert(EBattleStackState::ALIVE); //alive state indication
 	return ret;
@@ -155,7 +155,7 @@ void BattleInfo::localInitStack(CStack * s)
 	}
 	else //attach directly to obj to which stack belongs and creature type
 	{
-		CArmedInstance *army = battleGetArmyObject(!s->attackerOwned);
+		CArmedInstance *army = battleGetArmyObject(s->side);
 		s->attachTo(army);
 		assert(s->type);
 		s->attachTo(const_cast<CCreature*>(s->type));
@@ -486,7 +486,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
 			if(creatureBank && i->second->type->isDoubleWide())
 				pos += side ? BattleHex::LEFT : BattleHex::RIGHT;
 
-			CStack * stack = curB->generateNewStack(*i->second, !side, i->first, pos);
+			CStack * stack = curB->generateNewStack(*i->second, side, i->first, pos);
 			stacks.push_back(stack);
 		}
 	}
@@ -496,7 +496,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
 	{
 		if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive)
 		{
-			CStack * stack = curB->generateNewStack (*heroes[i]->commander, !i, SlotID::COMMANDER_SLOT_PLACEHOLDER,
+			CStack * stack = curB->generateNewStack (*heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER,
 				creatureBank ? commanderBank[i] : commanderField[i]);
 			stacks.push_back(stack);
 		}
@@ -506,15 +506,15 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
 	if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
 	{
 		// keep tower
-		CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -2);
+		CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -2);
 		stacks.push_back(stack);
 
 		if (curB->town->fortLevel() >= CGTownInstance::CASTLE)
 		{
 			// lower tower + upper tower
-			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -4);
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -4);
 			stacks.push_back(stack);
-			stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -3);
+			stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -3);
 			stacks.push_back(stack);
 		}
 

+ 3 - 14
lib/battle/BattleInfo.h

@@ -55,28 +55,17 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
 	CGHeroInstance * battleGetFightingHero(ui8 side) const;
 
 	const CStack * getNextStack() const; //which stack will have turn after current one
-	//void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action
 
-	//void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<BattleHex> & occupyable, bool flying, const CStack* stackToOmmit = nullptr) const; //send pointer to at least 187 allocated bytes
-	//static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
-	int getAvaliableHex(CreatureID creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects
-	//void makeBFS(BattleHex start, bool*accessibility, BattleHex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) 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
+	int getAvaliableHex(CreatureID creID, ui8 side, int initialPos = -1) const; //find place for summon / clone effects
 	std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const CStack * stack); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
-	//std::vector<BattleHex> getAccessibility(const CStack * stack, bool addOccupiable, std::vector<BattleHex> * attackable = nullptr, bool forPassingBy = false) const; //returns vector of accessible tiles (taking into account the creature range)
-
-	//bool isObstacleVisibleForSide(const CObstacleInstance &obstacle, ui8 side) const;
 	std::shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const;
 	std::set<BattleHex> getStoppers(bool whichSidePerspective) const;
 
 	ui32 calculateDmg(const CStack * attacker, const CStack * defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand); //charge - number of hexes travelled before attack (for champion's jousting)
 	void calculateCasualties(std::map<ui32,si32> * casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
 
-	//void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee
-	//std::set<CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
-	//std::set<BattleHex> getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
-
-	CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
-	CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	CStack * generateNewStack(const CStackInstance &base, ui8 side, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	CStack * generateNewStack(const CStackBasicDescriptor &base, ui8 side, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
 	int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack
 
 	const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player

+ 23 - 24
lib/battle/CBattleInfoCallback.cpp

@@ -103,10 +103,10 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con
 		return ESpellCastProblem::INVALID;
 	}
 	const PlayerColor player = caster->getOwner();
-	const si8 side = playerToSide(player);
-	if(side < 0)
+	const auto side = playerToSide(player);
+	if(!side)
 		return ESpellCastProblem::INVALID;
-	if(!battleDoWeKnowAbout(side))
+	if(!battleDoWeKnowAbout(side.get()))
 	{
 		logGlobal->warnStream() << "You can't check if enemy can cast given spell!";
 		return ESpellCastProblem::INVALID;
@@ -119,7 +119,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con
 	{
 	case ECastingMode::HERO_CASTING:
 	{
-		if(battleCastSpells(side) > 0)
+		if(battleCastSpells(side.get()) > 0)
 			return ESpellCastProblem::ALREADY_CASTED_THIS_TURN;
 
 		auto hero = dynamic_cast<const CGHeroInstance *>(caster);
@@ -255,7 +255,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
 		int bestSpeed = fastest->Speed(turn);
 
 		//FIXME: comparison between bool and integer. Logic does not makes sense either
-		if(fastest->attackerOwned != lastMoved)
+		if(fastest->side != lastMoved)
 		{
 			ret = fastest;
 		}
@@ -264,7 +264,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
 			for(j = i + 1; j < st.size(); j++)
 			{
 				if(!st[j]) continue;
-				if(st[j]->attackerOwned != lastMoved || st[j]->Speed(turn) != bestSpeed)
+				if(st[j]->side != lastMoved || st[j]->Speed(turn) != bestSpeed)
 					break;
 			}
 
@@ -288,7 +288,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
 		else
 			st[j] = nullptr;
 
-		lastMoved = ret->attackerOwned;
+		lastMoved = ret->side;
 		return ret;
 	};
 
@@ -362,9 +362,9 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
 		{
 			//FIXME: both branches contain same code!!!
 			if(out.size() && out.front() == active)
-				lastMoved = active->attackerOwned;
+				lastMoved = active->side;
 			else
-				lastMoved = active->attackerOwned;
+				lastMoved = active->side;
 		}
 		else
 		{
@@ -418,7 +418,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const CStack
 		if(!reachability.isReachable(i))
 			continue;
 
-		if(battleTacticDist() && battleGetTacticsSide() == !stack->attackerOwned)
+		if(battleTacticDist() && battleGetTacticsSide() == stack->side)
 		{
 			//Stack has to perform tactic-phase movement -> can enter any reachable tile within given range
 			if(!isInTacticRange(i))
@@ -453,7 +453,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const CStack
 			});
 			return availableNeighbor != ret.end();
 		};
-		for(const CStack * otherSt : battleAliveStacks(stack->attackerOwned))
+		for(const CStack * otherSt : battleAliveStacks(1-stack->side))
 		{
 			if(!otherSt->isValidTarget(false))
 				continue;
@@ -800,7 +800,6 @@ std::pair<ui32, ui32> CBattleInfoCallback::battleEstimateDamage(CRandomGenerator
 	RETURN_IF_NOT_BATTLE(std::make_pair(0, 0));
 
 	//const bool shooting = battleCanShoot(bai.attacker, bai.defenderPosition); //TODO handle bonus bearer
-	//const ui8 mySide = !attacker->attackerOwned;
 
 	TDmgRange ret = calculateDmgRange(bai);
 
@@ -965,7 +964,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
 		const int costToNeighbour = ret.distances[curHex] + 1;
 		for(BattleHex neighbour : curHex.neighbouringTiles())
 		{
-			const bool accessible = accessibility.accessible(neighbour, params.doubleWide, params.attackerOwned);
+			const bool accessible = accessibility.accessible(neighbour, params.doubleWide, params.side);
 			const int costFoundSoFar = ret.distances[neighbour];
 
 			if(accessible && costToNeighbour < costFoundSoFar)
@@ -1001,7 +1000,7 @@ std::set<BattleHex> CBattleInfoCallback::getStoppers(BattlePerspective::BattlePe
 	return ret;
 }
 
-std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const
+std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const CStack * closest, BattleSideOpt side) const
 {
 	auto reachability = getReachability(closest);
 	auto avHexes = battleGetAvailableHexes(closest, false);
@@ -1018,7 +1017,7 @@ std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const
 
 	std::vector<const CStack *> possibleStacks = battleGetStacksIf([=](const CStack * s)
 	{
-		return s->isValidTarget(false) && s != closest && (boost::logic::indeterminate(attackerOwned) || s->attackerOwned == attackerOwned);
+		return s->isValidTarget(false) && s != closest && (!side || side.get() == s->side);
 	});
 
 	for(const CStack * st : possibleStacks)
@@ -1064,7 +1063,7 @@ ReachabilityInfo CBattleInfoCallback::getReachability(const CStack *stack) const
 {
 	ReachabilityInfo::Parameters params(stack);
 
-	if(!battleDoWeKnowAbout(!stack->attackerOwned))
+	if(!battleDoWeKnowAbout(stack->side))
 	{
 		//Stack is held by enemy, we can't use his perspective to check for reachability.
 		// Happens ie. when hovering enemy stack for its range. The arg could be set properly, but it's easier to fix it here.
@@ -1089,7 +1088,7 @@ ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityIn
 
 	for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
 	{
-		if(ret.accessibility.accessible(i, params.doubleWide, params.attackerOwned))
+		if(ret.accessibility.accessible(i, params.doubleWide, params.side))
 		{
 			ret.predecessors[i] = params.startPosition;
 			ret.distances[i] = BattleHex::getDistance(params.startPosition, i);
@@ -1103,7 +1102,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
 {
 	//does not return hex attacked directly
 	//TODO: apply rotation to two-hex attackers
-	bool isAttacker = attacker->attackerOwned;
+	bool isAttacker = attacker->side == BattleSide::ATTACKER;
 
 	AttackableTiles at;
 	RETURN_IF_NOT_BATTLE(at);
@@ -1482,7 +1481,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
 		case SpellID::PROTECTION_FROM_FIRE:
 		case SpellID::PROTECTION_FROM_WATER:
 		{
-			const ui8 enemySide = (ui8)subject->attackerOwned;
+			const ui8 enemySide = 1 - subject->side;
 			//todo: only if enemy has spellbook
 			if (!battleHasHero(enemySide)) //only if there is enemy hero
 				continue;
@@ -1567,17 +1566,17 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const
 	if(!battleCanSurrender(Player))
 		return -1;
 
-	const si8 playerSide = playerToSide(Player);
-	if(playerSide < 0)
+	const auto side = playerToSide(Player);
+	if(!side)
 		return -1;
 
 	int ret = 0;
 	double discount = 0;
-	for(const CStack *s : battleAliveStacks(playerSide))
+	for(const CStack * s : battleAliveStacks(side.get()))
 		if(s->base) //we pay for our stack that comes from our army slots - condition eliminates summoned cres and war machines
 			ret += s->getCreature()->cost[Res::GOLD] * s->count;
 
-	if(const CGHeroInstance * h = battleGetFightingHero(playerSide))
+	if(const CGHeroInstance * h = battleGetFightingHero(side.get()))
 		discount += h->valOfBonuses(Bonus::SURRENDER_DISCOUNT);
 
 	ret *= (100.0 - discount) / 100.0;
@@ -1616,7 +1615,7 @@ boost::optional<int> CBattleInfoCallback::battleIsFinished() const
 	{
 		if(stack->alive() && !stack->hasBonusOfType(Bonus::SIEGE_WEAPON))
 		{
-			hasStack[1-stack->attackerOwned] = true;
+			hasStack[stack->side] = true;
 		}
 	}
 

+ 1 - 1
lib/battle/CBattleInfoCallback.h

@@ -101,7 +101,7 @@ public:
 	AccessibilityInfo getAccesibility() const;
 	AccessibilityInfo getAccesibility(const CStack *stack) const; //Hexes ocupied by stack will be marked as accessible.
 	AccessibilityInfo getAccesibility(const std::vector<BattleHex> & accessibleHexes) const; //given hexes will be marked as accessible
-	std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const;
+	std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, BattleSideOpt side) const;
 protected:
 	ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const;
 	ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const;

+ 31 - 15
lib/battle/CBattleInfoEssentials.cpp

@@ -67,7 +67,7 @@ bool CBattleInfoEssentials::battleHasNativeStack(ui8 side) const
 
 	for(const CStack * s : battleGetAllStacks())
 	{
-		if(s->attackerOwned == !side && s->getCreature()->isItNativeTerrain(getBattle()->terrainType))
+		if(s->side == side && s->getCreature()->isItNativeTerrain(getBattle()->terrainType))
 			return true;
 	}
 
@@ -102,7 +102,7 @@ TStacks CBattleInfoEssentials::battleAliveStacks() const
 TStacks CBattleInfoEssentials::battleAliveStacks(ui8 side) const
 {
 	return battleGetStacksIf([=](const CStack * s){
-		return s->isValidTarget(false) && s->attackerOwned == !side;
+		return s->isValidTarget(false) && s->side == side;
 	});
 }
 
@@ -237,8 +237,11 @@ const IBonusBearer * CBattleInfoEssentials::getBattleNode() const
 bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
 {
 	RETURN_IF_NOT_BATTLE(false);
-	const si8 mySide = playerToSide(player);
-	const CGHeroInstance *myHero = battleGetFightingHero(mySide);
+	const auto side = playerToSide(player);
+	if(!side)
+		return false;
+
+	const CGHeroInstance *myHero = battleGetFightingHero(side.get());
 
 	//current player have no hero
 	if(!myHero)
@@ -249,7 +252,7 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
 		return false;
 
 	//we are besieged defender
-	if(mySide == BattleSide::DEFENDER && battleGetSiegeLevel())
+	if(side.get() == BattleSide::DEFENDER && battleGetSiegeLevel())
 	{
 		auto town = battleGetDefendedTown();
 		if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD))
@@ -259,23 +262,31 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
 	return true;
 }
 
-si8 CBattleInfoEssentials::playerToSide(PlayerColor player) const
+BattleSideOpt CBattleInfoEssentials::playerToSide(PlayerColor player) const
 {
-	RETURN_IF_NOT_BATTLE(-1);
+	RETURN_IF_NOT_BATTLE(boost::none);
 	int ret = vstd::find_pos_if(getBattle()->sides, [=](const SideInBattle &side){ return side.color == player; });
 	if(ret < 0)
 		logGlobal->warnStream() << "Cannot find side for player " << player;
 
-	return ret;
+	return BattleSideOpt(ret);
+}
+
+ui8 CBattleInfoEssentials::otherSide(ui8 side) const
+{
+    if(side == BattleSide::ATTACKER)
+		return BattleSide::DEFENDER;
+	else
+		return BattleSide::ATTACKER;
 }
 
 bool CBattleInfoEssentials::playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const
 {
 	RETURN_IF_NOT_BATTLE(false);
-	const si8 playerSide = playerToSide(player);
-	if (playerSide >= 0)
+	const auto side = playerToSide(player);
+	if(side)
 	{
-		if (getBattle()->sides[!playerSide].hero == h)
+		if (getBattle()->sides[otherSide(side.get())].hero == h)
 			return true;
 	}
 	return false;
@@ -290,10 +301,12 @@ ui8 CBattleInfoEssentials::battleGetSiegeLevel() const
 bool CBattleInfoEssentials::battleCanSurrender(PlayerColor player) const
 {
 	RETURN_IF_NOT_BATTLE(false);
-	ui8 mySide = playerToSide(player);
-	bool iAmSiegeDefender = (mySide == BattleSide::DEFENDER && battleGetSiegeLevel());
+	const auto side = playerToSide(player);
+	if(!side)
+		return false;
+	bool iAmSiegeDefender = (side.get() == BattleSide::DEFENDER && battleGetSiegeLevel());
 	//conditions like for fleeing (except escape tunnel presence) + enemy must have a hero
-	return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(!mySide);
+	return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(otherSide(side.get()));
 }
 
 bool CBattleInfoEssentials::battleHasHero(ui8 side) const
@@ -334,7 +347,10 @@ PlayerColor CBattleInfoEssentials::battleGetOwner(const CStack * stack) const
 const CGHeroInstance * CBattleInfoEssentials::battleGetOwnerHero(const CStack * stack) const
 {
 	RETURN_IF_NOT_BATTLE(nullptr);
-	return getBattle()->sides.at(playerToSide(battleGetOwner(stack))).hero;
+	const auto side = playerToSide(battleGetOwner(stack));
+	if(!side)
+		return nullptr;
+	return getBattle()->sides.at(side.get()).hero;
 }
 
 bool CBattleInfoEssentials::battleMatchOwner(const CStack * attacker, const CStack * defender, const boost::logic::tribool positivness /* = false*/) const

+ 3 - 7
lib/battle/CBattleInfoEssentials.h

@@ -9,6 +9,7 @@
  */
 #pragma once
 #include "CCallbackBase.h"
+#include "BattleHex.h"
 
 class CGTownInstance;
 class CGHeroInstance;
@@ -32,12 +33,6 @@ namespace BattlePerspective
 	};
 }
 
-namespace BattleSide
-{
-	enum {ATTACKER = 0, DEFENDER = 1};
-}
-
-
 class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase
 {
 protected:
@@ -71,7 +66,8 @@ public:
 	si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?)
 	bool battleCanFlee(PlayerColor player) const;
 	bool battleCanSurrender(PlayerColor player) const;
-	si8 playerToSide(PlayerColor player) const;
+	ui8 otherSide(ui8 side) const;
+	BattleSideOpt playerToSide(PlayerColor player) const;
 	bool playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const;
 	ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
 	bool battleHasHero(ui8 side) const;

+ 4 - 3
lib/battle/ReachabilityInfo.cpp

@@ -17,16 +17,17 @@ ReachabilityInfo::Parameters::Parameters()
 {
 	stack = nullptr;
 	perspective = BattlePerspective::ALL_KNOWING;
-	attackerOwned = doubleWide = flying = false;
+	side = 0;
+	doubleWide = flying = false;
 }
 
 ReachabilityInfo::Parameters::Parameters(const CStack * Stack)
 {
 	stack = Stack;
-	perspective = (BattlePerspective::BattlePerspective)(!Stack->attackerOwned);
+	perspective = (BattlePerspective::BattlePerspective)(Stack->side);
 	startPosition = Stack->position;
 	doubleWide = stack->doubleWide();
-	attackerOwned = stack->attackerOwned;
+	side = stack->side;
 	flying = stack->hasBonusOfType(Bonus::FLYING);
 	knownAccessible = stack->getHexes();
 }

+ 1 - 1
lib/battle/ReachabilityInfo.h

@@ -27,7 +27,7 @@ struct DLL_LINKAGE ReachabilityInfo
 	{
 		const CStack * stack; //stack for which calculation is mage => not required (kept for debugging mostly), following variables are enough
 
-		bool attackerOwned;
+		ui8 side;
 		bool doubleWide;
 		bool flying;
 		std::vector<BattleHex> knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself)

+ 15 - 13
lib/spells/BattleSpellMechanics.cpp

@@ -104,7 +104,7 @@ std::vector<const CStack *> ChainLightningMechanics::calculateAffectedStacks(con
 		}
 		if(possibleHexes.empty()) //not enough targets
 			break;
-		lightningHex = BattleHex::getClosestTile(stack->attackerOwned, lightningHex, possibleHexes);
+		lightningHex = BattleHex::getClosestTile(stack->side, lightningHex, possibleHexes);
 	}
 
 	return res;
@@ -121,13 +121,12 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, const
 		env->complain ("No target stack to clone!");
 		return;
 	}
-	const int attacker = !(bool)parameters.casterSide;
 
 	BattleStackAdded bsa;
 	bsa.creID = clonedStack->type->idNumber;
-	bsa.attacker = attacker;
+	bsa.side = parameters.casterSide;
 	bsa.summoned = true;
-	bsa.pos = parameters.cb->getAvaliableHex(bsa.creID, attacker); //TODO: unify it
+	bsa.pos = parameters.cb->getAvaliableHex(bsa.creID, parameters.casterSide);
 	bsa.amount = clonedStack->count;
 	env->sendAndApply(&bsa);
 
@@ -370,8 +369,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl
 	CSpell::TargetInfo ti(owner, caster->getSpellSchoolLevel(owner));
 	if(ti.smart)
 	{
+		const auto side = cb->playerToSide(caster->getOwner());
+		if(!side)
+			return ESpellCastProblem::INVALID;
 		//if spell targeting is smart, then only attacker can use it
-		if(cb->playerToSide(caster->getOwner()) != BattleSide::ATTACKER)
+		if(side.get() != BattleSide::ATTACKER)
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 	}
 
@@ -407,13 +409,13 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I
 ///ObstacleMechanics
 ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const
 {
-	const si8 side = cb->playerToSide(ctx.caster->getOwner());
-	if(side < 0)
+	const auto side = cb->playerToSide(ctx.caster->getOwner());
+	if(!side)
 		return ESpellCastProblem::INVALID;
 
 	bool hexesOutsideBattlefield = false;
 
-	auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side, &hexesOutsideBattlefield);
+	auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side.get(), &hexesOutsideBattlefield);
 
 	for(const BattleHex & hex : tilesThatMustBeClear)
 		if(!isHexAviable(cb, hex, ctx.ti.clearAffected))
@@ -499,11 +501,11 @@ void PatchObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env
 ESpellCastProblem::ESpellCastProblem LandMineMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const
 {
 	//LandMine are useless if enemy has native stack and can see mines, check for LandMine damage immunity is done in general way by CSpell
-	const si8 playerSide = cb->playerToSide(caster->getOwner());
-	if(playerSide < 0)
+	const auto side = cb->playerToSide(caster->getOwner());
+	if(!side)
 		return ESpellCastProblem::INVALID;
 
-	const si8 otherSide = !playerSide;
+	const ui8 otherSide = cb->otherSide(side.get());
 
 	if(cb->battleHasNativeStack(otherSide))
 		return ESpellCastProblem::NO_APPROPRIATE_TARGET;
@@ -887,9 +889,9 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, const
 {
 	BattleStackAdded bsa;
 	bsa.creID = creatureToSummon;
-	bsa.attacker = !(bool)parameters.casterSide;
+	bsa.side = parameters.casterSide;
 	bsa.summoned = true;
-	bsa.pos = parameters.cb->getAvaliableHex(creatureToSummon, !(bool)parameters.casterSide); //TODO: unify it
+	bsa.pos = parameters.cb->getAvaliableHex(creatureToSummon, parameters.casterSide); //TODO: unify it
 
 	//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
 	int percentBonus = parameters.casterHero ? parameters.casterHero->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0;

+ 3 - 3
lib/spells/CDefaultSpellMechanics.cpp

@@ -649,10 +649,10 @@ std::vector<const CStack *> DefaultSpellMechanics::calculateAffectedStacks(const
 {
 	std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures
 
-	const si8 playerSide = cb->playerToSide(ctx.caster->getOwner());
-	if(playerSide < 0)
+	const auto side = cb->playerToSide(ctx.caster->getOwner());
+	if(!side)
 		return std::vector<const CStack *>();
-	auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, playerSide);
+	auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, side.get());
 
 	//hackfix for banned creature massive spells
 	if(!ctx.ti.massive && owner->getLevelInfo(ctx.schoolLvl).range == "X")

+ 3 - 3
lib/spells/CSpellHandler.cpp

@@ -186,15 +186,15 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback
 		return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
 
 	const PlayerColor player = caster->getOwner();
-	const si8 side = cb->playerToSide(player);
+	const auto side = cb->playerToSide(player);
 
-	if(side < 0)
+	if(!side)
 		return ESpellCastProblem::INVALID;
 
 	//effect like Recanter's Cloak. Blocks also passive casting.
 	//TODO: check creature abilities to block
 	//TODO: check any possible caster
-	if(cb->battleMaxSpellLevel(side) < level)
+	if(cb->battleMaxSpellLevel(side.get()) < level)
 		return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
 
 	const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster);

+ 27 - 32
server/CGameHandler.cpp

@@ -138,11 +138,13 @@ static void giveExp(BattleResult &r)
 	}
 }
 
-static void summonGuardiansHelper(std::vector<BattleHex> & output, const BattleHex & targetPosition, bool targetIsAttacker, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
+static void summonGuardiansHelper(std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
 {
 	int x = targetPosition.getX();
 	int y = targetPosition.getY();
 
+	const bool targetIsAttacker = side == BattleSide::ATTACKER;
+
 	if (targetIsAttacker) //handle front guardians, TODO: should we handle situation when units start battle near opposite side of the battlefield? Cannot happen in normal H3...
 		BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false), output);
 	else
@@ -1148,21 +1150,15 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 	auto accessibility = getAccesibility(curStack);
 
 	//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
-	if (!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack))
+	if(!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack))
 	{
-		if (curStack->attackerOwned)
-		{
-			if (accessibility.accessible(dest+1, curStack))
-				dest += BattleHex::RIGHT;
-		}
-		else
-		{
-			if (accessibility.accessible(dest-1, curStack))
-				dest += BattleHex::LEFT;
-		}
+		BattleHex shifted = dest.cloneInDirection(curStack->destShiftDir(), false);
+
+		if(accessibility.accessible(shifted, curStack))
+			dest = shifted;
 	}
 
-	if ((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility.accessible(dest, curStack))
+	if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility.accessible(dest, curStack))
 	{
 		complain("Given destination is not accessible!");
 		return 0;
@@ -1170,7 +1166,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 
 	bool canUseGate = false;
 	auto dbState = gs->curB->si.gateState;
-	if (battleGetSiegeLevel() > 0 && !curStack->attackerOwned &&
+	if(battleGetSiegeLevel() > 0 && curStack->side == BattleSide::DEFENDER &&
 		dbState != EGateState::DESTROYED &&
 		dbState != EGateState::BLOCKED)
 	{
@@ -3828,7 +3824,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 
 		if (battleTacticDist())
 		{
-			if (stack && !stack->attackerOwned != battleGetTacticsSide())
+			if (stack && stack->side != battleGetTacticsSide())
 			{
 				complain("This is not a stack of side that has tactics!");
 				return false;
@@ -3922,10 +3918,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 
 			logGlobal->trace("%s will attack %s", stack->nodeName(), destinationStack->nodeName());
 
-			if (stack->position != ba.destinationTile //we wasn't able to reach destination tile
-				&& !(stack->doubleWide()
-					&&  (stack->position == ba.destinationTile + (stack->attackerOwned ?  +1 : -1))
-						) //nor occupy specified hex
+			if(stack->position != ba.destinationTile //we wasn't able to reach destination tile
+				&& !(stack->doubleWide() && (stack->position == ba.destinationTile.cloneInDirection(stack->destShiftDir(), false))) //nor occupy specified hex
 				)
 			{
 				complain("We cannot move this stack to its destination " + stack->getCreature()->namePl);
@@ -4263,7 +4257,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 
 			CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream!
 			BattleStackAdded bsa;
-			bsa.attacker = summoner->attackerOwned;
+			bsa.side = summoner->side;
 
 			bsa.creID = summonedType;
 			ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum());
@@ -4274,7 +4268,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 
 			bsa.amount = std::min(canRiseAmount, destStack->baseAmount);
 
-			bsa.pos = gs->curB->getAvaliableHex(bsa.creID, bsa.attacker, destStack->position);
+			bsa.pos = gs->curB->getAvaliableHex(bsa.creID, bsa.side, destStack->position);
 			bsa.summoned = false;
 
 			if (bsa.amount) //there's rare possibility single creature cannot rise desired type
@@ -4632,7 +4626,7 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c
 	//helper info
 	const SpellCreatedObstacle *spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(&obstacle); //not nice but we may need spell params
 
-	const ui8 side = !curStack->attackerOwned; //if enemy is defending (false = 0), side of enemy hero is 1 (true)
+	const ui8 side = curStack->side; //if enemy is defending (false = 0), side of enemy hero is 1 (true)
 	const CGHeroInstance *hero = gs->curB->battleGetFightingHero(side);//FIXME: there may be no hero - landmines in Tower
 
 	if (obstacle.obstacleType == CObstacleInstance::MOAT)
@@ -5387,6 +5381,7 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat)
 
 		BattleStackAdded resurrectInfo;
 		resurrectInfo.pos = defender->position;
+		resurrectInfo.side = defender->side;
 
 		if (bonusAdditionalInfo != -1)
 			resurrectInfo.creID = (CreatureID)bonusAdditionalInfo;
@@ -5486,7 +5481,7 @@ void CGameHandler::makeStackDoNothing(const CStack * next)
 	doNothing.actionType = Battle::NO_ACTION;
 	doNothing.additionalInfo = 0;
 	doNothing.destinationTile = -1;
-	doNothing.side = !next->attackerOwned;
+	doNothing.side = next->side;
 	doNothing.stackNumber = next->ID;
 
 	makeAutomaticAction(next, doNothing);
@@ -5679,16 +5674,16 @@ void CGameHandler::runBattle()
 			if (!guardianIsBig)
 				targetHexes = stack->getSurroundingHexes();
 			else
-				summonGuardiansHelper(targetHexes, stack->position, stack->attackerOwned, targetIsBig);
+				summonGuardiansHelper(targetHexes, stack->position, stack->side, targetIsBig);
 
 			for (auto hex : targetHexes)
 			{
-				if (accessibility.accessible(hex, guardianIsBig, stack->attackerOwned)) //without this multiple creatures can occupy one hex
+				if (accessibility.accessible(hex, guardianIsBig, stack->side)) //without this multiple creatures can occupy one hex
 				{
 					BattleStackAdded newStack;
 					newStack.amount = std::max(1, (int)(stack->count * 0.01 * summonInfo->val));
 					newStack.creID = creatureData.num;
-					newStack.attacker = stack->attackerOwned;
+					newStack.side = stack->side;
 					newStack.summoned = true;
 					newStack.pos = hex.hex;
 					sendAndApply(&newStack);
@@ -5779,7 +5774,7 @@ void CGameHandler::runBattle()
 					BattleAction ba;
 					ba.actionType = Battle::BAD_MORALE;
 					ba.additionalInfo = 1;
-					ba.side = !next->attackerOwned;
+					ba.side = next->side;
 					ba.stackNumber = next->ID;
 
 					makeAutomaticAction(next, ba);
@@ -5790,12 +5785,12 @@ void CGameHandler::runBattle()
 			if (next->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE)) //while in berserk
 			{
 				logGlobal->debug("Handle Berserk effect");
-				std::pair<const CStack *, int> attackInfo = curB.getNearestStack(next, boost::logic::indeterminate);
+				std::pair<const CStack *, int> attackInfo = curB.getNearestStack(next, boost::none);
 				if (attackInfo.first != nullptr)
 				{
 					BattleAction attack;
 					attack.actionType = Battle::WALK_AND_ATTACK;
-					attack.side = !next->attackerOwned;
+					attack.side = next->side;
 					attack.stackNumber = next->ID;
 					attack.additionalInfo = attackInfo.first->position;
 					attack.destinationTile = attackInfo.second;
@@ -5818,7 +5813,7 @@ void CGameHandler::runBattle()
 			{
 				BattleAction attack;
 				attack.actionType = Battle::SHOOT;
-				attack.side = !next->attackerOwned;
+				attack.side = next->side;
 				attack.stackNumber = next->ID;
 
 				for (auto & elem : gs->curB->stacks)
@@ -5851,7 +5846,7 @@ void CGameHandler::runBattle()
 												getRandomGenerator());
 					attack.actionType = Battle::CATAPULT;
 					attack.additionalInfo = 0;
-					attack.side = !next->attackerOwned;
+					attack.side = next->side;
 					attack.stackNumber = next->ID;
 
 					makeAutomaticAction(next, attack);
@@ -5881,7 +5876,7 @@ void CGameHandler::runBattle()
 					heal.actionType = Battle::STACK_HEAL;
 					heal.additionalInfo = 0;
 					heal.destinationTile = toBeHealed->position;
-					heal.side = !next->attackerOwned;
+					heal.side = next->side;
 					heal.stackNumber = next->ID;
 
 					makeAutomaticAction(next, heal);

+ 8 - 6
test/Battlefield.cpp

@@ -91,16 +91,16 @@ BOOST_AUTO_TEST_CASE(getClosestTile)
 	possibilities.insert(119);
 	possibilities.insert(186);
 
-	BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==3);
+	BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==3);
 	mainHex = 139;
-	BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==119);
+	BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==119);
 	mainHex = 16;
-	BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==100);
+	BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==100);
 	mainHex = 166;
-	BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==186);
+	BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==186);
 	mainHex = 76;
-	BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==3);
-	BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==100);
+	BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==3);
+	BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==100);
 }
 
 BOOST_AUTO_TEST_CASE(moveEDir)
@@ -118,6 +118,8 @@ BOOST_AUTO_TEST_CASE(moveEDir)
 	BOOST_TEST(mainHex==3);
 	mainHex.moveInDirection(BattleHex::EDir::BOTTOM_LEFT);
 	BOOST_TEST(mainHex==20);
+	mainHex.moveInDirection(BattleHex::EDir::NONE);
+	BOOST_TEST(mainHex==20);
 }
 
 BOOST_AUTO_TEST_SUITE_END()