소스 검색

Refactor BattleHex, remake the use of precomputed neighbouring tiles containers.
- Moved short, frequently used functions to the BattleHex header for inlining
- Made BattleHex a class with a private hex value
- Moved getClosestTile implementation back to BattleHex
- Enabled access to static precomputed data in BattleHexArray via BattleHex
(note: circular dependency prevented static precomputed containers being directly placed in BattleHex)

MichalZr6 11 달 전
부모
커밋
dad6437661

+ 1 - 1
AI/BattleAI/AttackPossibility.cpp

@@ -384,7 +384,7 @@ AttackPossibility AttackPossibility::evaluate(
 		affectedUnits = defenderUnits;
 		affectedUnits = defenderUnits;
 		vstd::concatenate(affectedUnits, retaliatedUnits);
 		vstd::concatenate(affectedUnits, retaliatedUnits);
 
 
-		logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex.hex, defHex.hex);
+		logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex, defHex);
 
 
 		std::map<uint32_t, std::shared_ptr<battle::CUnitState>> defenderStates;
 		std::map<uint32_t, std::shared_ptr<battle::CUnitState>> defenderStates;
 
 

+ 3 - 3
AI/BattleAI/BattleEvaluator.cpp

@@ -215,7 +215,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
 				bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
 				bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
 				bestAttack.affectedUnits[0]->getCount(),
 				bestAttack.affectedUnits[0]->getCount(),
 				(int)bestAttack.from,
 				(int)bestAttack.from,
-				(int)bestAttack.attack.attacker->getPosition().hex,
+				(int)bestAttack.attack.attacker->getPosition(),
 				bestAttack.attack.chargeDistance,
 				bestAttack.attack.chargeDistance,
 				bestAttack.attack.attacker->getMovementRange(0),
 				bestAttack.attack.attacker->getMovementRange(0),
 				bestAttack.defenderDamageReduce,
 				bestAttack.defenderDamageReduce,
@@ -252,7 +252,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
 
 
 					if(siegeDefense)
 					if(siegeDefense)
 					{
 					{
-						logAi->trace("Evaluating exchange at %d self-defense", stack->getPosition().hex);
+						logAi->trace("Evaluating exchange at %d self-defense", stack->getPosition());
 
 
 						BattleAttackInfo bai(stack, stack, 0, false);
 						BattleAttackInfo bai(stack, stack, 0, false);
 						AttackPossibility apDefend(stack->getPosition(), stack->getPosition(), bai);
 						AttackPossibility apDefend(stack->getPosition(), stack->getPosition(), bai);
@@ -286,7 +286,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
 				"Moving %s towards hex %s[%d], score: %2f",
 				"Moving %s towards hex %s[%d], score: %2f",
 				stack->getDescription(),
 				stack->getDescription(),
 				moveTarget.cachedAttack->attack.defender->getDescription(),
 				moveTarget.cachedAttack->attack.defender->getDescription(),
-				moveTarget.cachedAttack->attack.defender->getPosition().hex,
+				moveTarget.cachedAttack->attack.defender->getPosition(),
 				moveTarget.score);
 				moveTarget.score);
 
 
 			return goTowardsNearest(stack, moveTarget.positions, *targets);
 			return goTowardsNearest(stack, moveTarget.positions, *targets);

+ 2 - 2
AI/BattleAI/BattleExchangeVariant.cpp

@@ -960,7 +960,7 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getOneTurnReachableUn
 
 
 				if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
 				if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
 				{
 				{
-					for(BattleHex neighbour : BattleHexArray::neighbouringTilesCache[hex.hex])
+					for(BattleHex neighbour : hex.getNeighbouringTiles())
 					{
 					{
 						reachable = unitReachability.distances.at(neighbour) <= radius;
 						reachable = unitReachability.distances.at(neighbour) <= radius;
 
 
@@ -1021,7 +1021,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
 					if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
 					if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
 					{
 					{
 						enemyUnit = true;
 						enemyUnit = true;
-						for(BattleHex neighbour : BattleHexArray::neighbouringTilesCache[hex.hex])
+						for(BattleHex neighbour : hex.getNeighbouringTiles())
 						{
 						{
 							reachable = unitReachability.distances.at(neighbour) <= unitSpeed;
 							reachable = unitReachability.distances.at(neighbour) <= unitSpeed;
 
 

+ 2 - 1
AI/StupidAI/StupidAI.cpp

@@ -107,7 +107,8 @@ static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr<CBattleCallback>
 
 
 	for(int i = 0; i < 2; i++)
 	for(int i = 0; i < 2; i++)
 	{
 	{
-		for (auto neighbour : BattleHexArray::neighbouringTilesCache[i ? h2.hex : h1.hex])
+		BattleHex hex = i ? h2 : h1;
+		for (auto neighbour : hex.getNeighbouringTiles())
 			if(const auto * s = cb->getBattle(battleID)->battleGetUnitByPos(neighbour))
 			if(const auto * s = cb->getBattle(battleID)->battleGetUnitByPos(neighbour))
 				if(s->isShooter())
 				if(s->isShooter())
 					shooters[i]++;
 					shooters[i]++;

+ 3 - 5
client/battle/BattleFieldController.cpp

@@ -482,7 +482,7 @@ std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeigh
 	{
 	{
 		// get all neighbours and their directions
 		// get all neighbours and their directions
 		
 		
-		const BattleHexArray & neighbouringTiles = BattleHexArray::getAllNeighbouringTiles(hex);
+		const BattleHexArray & neighbouringTiles = hex.getAllNeighbouringTiles();
 
 
 		std::vector<BattleHex::EDir> outsideNeighbourDirections;
 		std::vector<BattleHex::EDir> outsideNeighbourDirections;
 
 
@@ -492,9 +492,7 @@ std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeigh
 			if(!neighbouringTiles[direction].isAvailable())
 			if(!neighbouringTiles[direction].isAvailable())
 				continue;
 				continue;
 
 
-			auto it = std::find(wholeRangeHexes.begin(), wholeRangeHexes.end(), neighbouringTiles[direction]);
-
-			if(it == wholeRangeHexes.end())
+			if(!wholeRangeHexes.contains(neighbouringTiles[direction]))
 				outsideNeighbourDirections.push_back(BattleHex::EDir(direction)); // push direction
 				outsideNeighbourDirections.push_back(BattleHex::EDir(direction)); // push direction
 		}
 		}
 
 
@@ -680,7 +678,7 @@ BattleHex BattleFieldController::getHexAtPosition(Point hoverPos)
 BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber)
 BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber)
 {
 {
 	const bool doubleWide = owner.stacksController->getActiveStack()->doubleWide();
 	const bool doubleWide = owner.stacksController->getActiveStack()->doubleWide();
-	const BattleHexArray & neighbours = BattleHexArray::getAllNeighbouringTiles(myNumber);
+	const BattleHexArray & neighbours = myNumber.getAllNeighbouringTiles();
 	//   0 1
 	//   0 1
 	//  5 x 2
 	//  5 x 2
 	//   4 3
 	//   4 3

+ 1 - 1
client/battle/BattleInterface.h

@@ -10,6 +10,7 @@
 #pragma once
 #pragma once
 
 
 #include "BattleConstants.h"
 #include "BattleConstants.h"
+#include "../lib/battle/BattleHex.h"
 #include "../gui/CIntObject.h"
 #include "../gui/CIntObject.h"
 #include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
 #include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
 #include "../ConditionalWait.h"
 #include "../ConditionalWait.h"
@@ -27,7 +28,6 @@ class BattleAction;
 class CGTownInstance;
 class CGTownInstance;
 struct CatapultAttack;
 struct CatapultAttack;
 struct BattleTriggerEffect;
 struct BattleTriggerEffect;
-struct BattleHex;
 struct InfoAboutHero;
 struct InfoAboutHero;
 class ObstacleChanges;
 class ObstacleChanges;
 class CPlayerBattleCallback;
 class CPlayerBattleCallback;

+ 1 - 1
client/battle/BattleObstacleController.h

@@ -13,7 +13,7 @@
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
-struct BattleHex;
+class BattleHex;
 struct CObstacleInstance;
 struct CObstacleInstance;
 class JsonNode;
 class JsonNode;
 class ObstacleChanges;
 class ObstacleChanges;

+ 1 - 1
client/battle/BattleSiegeController.cpp

@@ -195,7 +195,7 @@ const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) co
 			return town->fortificationsLevel().lowerTowerShooter.toCreature();
 			return town->fortificationsLevel().lowerTowerShooter.toCreature();
 	}
 	}
 
 
-	throw std::runtime_error("Unable to select shooter for tower at " + std::to_string(position.hex));
+	throw std::runtime_error("Unable to select shooter for tower at " + std::to_string(position));
 }
 }
 
 
 Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const
 Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const

+ 1 - 1
client/battle/BattleStacksController.h

@@ -13,7 +13,7 @@
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
-struct BattleHex;
+class BattleHex;
 class BattleHexArray;
 class BattleHexArray;
 class BattleAction;
 class BattleAction;
 class CStack;
 class CStack;

+ 1 - 1
lib/battle/AccessibilityInfo.cpp

@@ -25,7 +25,7 @@ bool AccessibilityInfo::tileAccessibleWithGate(BattleHex tile, BattleSide side)
 		if(!destructibleEnemyTurns)
 		if(!destructibleEnemyTurns)
 			return false;
 			return false;
 
 
-		return destructibleEnemyTurns->at(tile.hex) >= 0;
+		return destructibleEnemyTurns->at(tile) >= 0;
 	}
 	}
 
 
 	if(accessibility != EAccessibility::ACCESSIBLE)
 	if(accessibility != EAccessibility::ACCESSIBLE)

+ 37 - 104
lib/battle/BattleHex.cpp

@@ -9,130 +9,63 @@
  */
  */
 #include "StdInc.h"
 #include "StdInc.h"
 #include "BattleHex.h"
 #include "BattleHex.h"
+#include "BattleHexArray.h"
 
 
-VCMI_LIB_NAMESPACE_BEGIN
-
-BattleHex::BattleHex() : hex(INVALID) {}
-
-BattleHex::BattleHex(si16 _hex) : hex(_hex) {}
-
-BattleHex::BattleHex(si16 x, si16 y)
-{
-	setXY(x, y);
-}
-
-BattleHex::BattleHex(std::pair<si16, si16> xy)
-{
-	setXY(xy);
-}
-
-BattleHex::operator si16() const
+VCMI_LIB_NAMESPACE_BEGIN
+
+BattleHex BattleHex::getClosestTile(const BattleHexArray & hexes, BattleSide side, BattleHex initialPos)
 {
 {
-	return hex;
-}
+	if(hexes.empty())
+		return BattleHex();
 
 
-void BattleHex::setX(si16 x)
-{
-	setXY(x, getY());
-}
+	BattleHex initialHex = BattleHex(initialPos);
+	int closestDistance = std::numeric_limits<int>::max();
+	BattleHexArray closestTiles;
 
 
-void BattleHex::setY(si16 y)
-{
-	setXY(getX(), y);
-}
-
-void BattleHex::setXY(si16 x, si16 y, bool hasToBeValid)
-{
-	if(hasToBeValid)
+	for(auto hex : hexes)
 	{
 	{
-		if(x < 0 || x >= GameConstants::BFIELD_WIDTH || y < 0 || y >= GameConstants::BFIELD_HEIGHT)
-			throw std::runtime_error("Valid hex required");
+		int distance = initialHex.getDistance(initialHex, hex);
+		if(distance < closestDistance)
+		{
+			closestDistance = distance;
+			closestTiles.clear();
+			closestTiles.insert(hex);
+		}
+		else if(distance == closestDistance)
+			closestTiles.insert(hex);
 	}
 	}
 
 
-	hex = x + y * GameConstants::BFIELD_WIDTH;
-}
-
-void BattleHex::setXY(std::pair<si16, si16> xy)
-{
-	setXY(xy.first, xy.second);
-}
-
-si16 BattleHex::getX() const
-{
-	return hex % GameConstants::BFIELD_WIDTH;
-}
-
-si16 BattleHex::getY() const
-{
-	return hex / GameConstants::BFIELD_WIDTH;
-}
-
-std::pair<si16, si16> BattleHex::getXY() const
-{
-	return std::make_pair(getX(), getY());
-}
-
-BattleHex & BattleHex::moveInDirection(EDir dir, bool hasToBeValid)
-{
-	si16 x = getX();
-	si16 y = getY();
-	switch(dir)
+	auto compareHorizontal = [side, initialPos](const BattleHex & left, const BattleHex & right)
 	{
 	{
-	case TOP_LEFT:
-		setXY((y%2) ? x-1 : x, y-1, hasToBeValid);
-		break;
-	case TOP_RIGHT:
-		setXY((y%2) ? x : x+1, y-1, hasToBeValid);
-		break;
-	case RIGHT:
-		setXY(x+1, y, hasToBeValid);
-		break;
-	case BOTTOM_RIGHT:
-		setXY((y%2) ? x : x+1, y+1, hasToBeValid);
-		break;
-	case BOTTOM_LEFT:
-		setXY((y%2) ? x-1 : x, y+1, hasToBeValid);
-		break;
-	case LEFT:
-		setXY(x-1, y, hasToBeValid);
-		break;
-	case NONE:
-		break;
-	default:
-		throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n");
-		break;
-	}
-	return *this;
-}
+		if(left.getX() != right.getX())
+		{
+			return (side == BattleSide::ATTACKER) ? (left.getX() > right.getX()) : (left.getX() < right.getX());
+		}
+		return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
+	};
 
 
-BattleHex & BattleHex::operator+=(BattleHex::EDir dir)
-{
-	return moveInDirection(dir);
-}
+	auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
+	return (bestTile != closestTiles.end()) ? *bestTile : BattleHex();
+}
 
 
-BattleHex BattleHex::cloneInDirection(BattleHex::EDir dir, bool hasToBeValid) const
+const BattleHexArray & BattleHex::getAllNeighbouringTiles() const
 {
 {
-	BattleHex result(hex);
-	result.moveInDirection(dir, hasToBeValid);
-	return result;
+	return BattleHexArray::getAllNeighbouringTiles(*this);
 }
 }
 
 
-BattleHex BattleHex::operator+(BattleHex::EDir dir) const
+const BattleHexArray & BattleHex::getNeighbouringTiles() const
 {
 {
-	return cloneInDirection(dir);
+	return BattleHexArray::getNeighbouringTiles(*this);
 }
 }
 
 
-BattleHex::EDir BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2)
+const BattleHexArray & BattleHex::getNeighbouringTilesDblWide(BattleSide side) const
 {
 {
-	for(auto dir : hexagonalDirections())
-		if(hex2 == hex1.cloneInDirection(dir, false))
-			return dir;
-	return NONE;
-}
+	return BattleHexArray::getNeighbouringTilesDblWide(*this, side);
+}
 
 
 std::ostream & operator<<(std::ostream & os, const BattleHex & hex)
 std::ostream & operator<<(std::ostream & os, const BattleHex & hex)
 {
 {
-	return os << boost::str(boost::format("{BattleHex: x '%d', y '%d', hex '%d'}") % hex.getX() % hex.getY() % hex.hex);
+	return os << boost::str(boost::format("{BattleHex: x '%d', y '%d', hex '%d'}") % hex.getX() % hex.getY() % static_cast<si16>(hex));
 }
 }
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END

+ 145 - 25
lib/battle/BattleHex.h

@@ -22,9 +22,13 @@ namespace GameConstants
 	const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
 	const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
 }
 }
 
 
+class BattleHexArray;
+
 // for battle stacks' positions
 // for battle stacks' positions
-struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design
+class DLL_LINKAGE BattleHex
 {
 {
+public:
+
 	// helpers for siege
 	// helpers for siege
 	static constexpr si16 CASTLE_CENTRAL_TOWER = -2;
 	static constexpr si16 CASTLE_CENTRAL_TOWER = -2;
 	static constexpr si16 CASTLE_BOTTOM_TOWER = -3;
 	static constexpr si16 CASTLE_BOTTOM_TOWER = -3;
@@ -46,8 +50,8 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
 	static constexpr si16 GATE_OUTER = 95;
 	static constexpr si16 GATE_OUTER = 95;
 	static constexpr si16 GATE_INNER = 96;
 	static constexpr si16 GATE_INNER = 96;
 
 
-	si16 hex;
 	static constexpr si16 INVALID = -1;
 	static constexpr si16 INVALID = -1;
+
 	enum EDir
 	enum EDir
 	{
 	{
 		NONE = -1,
 		NONE = -1,
@@ -64,11 +68,25 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
 		BOTTOM
 		BOTTOM
 	};
 	};
 
 
-	BattleHex();
-	BattleHex(si16 _hex);
-	BattleHex(si16 x, si16 y);
-	BattleHex(std::pair<si16, si16> xy);
-	operator si16() const;
+	BattleHex() 
+		: hex(INVALID) 
+	{}
+	BattleHex(si16 _hex) 
+		: hex(_hex) 
+	{}
+	BattleHex(si16 x, si16 y)
+	{
+		setXY(x, y);
+	}
+	BattleHex(std::pair<si16, si16> xy)
+	{
+		setXY(xy);
+	}
+	operator si16() const
+	{
+		return hex;
+	}
+
 	inline bool isValid() const
 	inline bool isValid() const
 	{
 	{
 		return hex >= 0 && hex < GameConstants::BFIELD_SIZE;
 		return hex >= 0 && hex < GameConstants::BFIELD_SIZE;
@@ -79,19 +97,97 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
 		return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH - 1;
 		return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH - 1;
 	}
 	}
 
 
-	void setX(si16 x);
-	void setY(si16 y);
-	void setXY(si16 x, si16 y, bool hasToBeValid = true);
-	void setXY(std::pair<si16, si16> xy);
-	si16 getX() const;
-	si16 getY() const;
-	std::pair<si16, si16> getXY() const;
-	BattleHex& moveInDirection(EDir dir, bool hasToBeValid = true);
-	BattleHex& operator+=(EDir dir);
-	BattleHex cloneInDirection(EDir dir, bool hasToBeValid = true) const;
-	BattleHex operator+(EDir dir) const;
+	void setX(si16 x)
+	{
+		setXY(x, getY());
+	}
+
+	void setY(si16 y)
+	{
+		setXY(getX(), y);
+	}
+
+	void setXY(si16 x, si16 y, bool hasToBeValid = true)
+	{
+		if(hasToBeValid)
+		{
+			if(x < 0 || x >= GameConstants::BFIELD_WIDTH || y < 0 || y >= GameConstants::BFIELD_HEIGHT)
+				throw std::runtime_error("Valid hex required");
+		}
+
+		hex = x + y * GameConstants::BFIELD_WIDTH;
+	}
+
+	void setXY(std::pair<si16, si16> xy)
+	{
+		setXY(xy.first, xy.second);
+	}
+
+	si16 getX() const
+	{
+		return hex % GameConstants::BFIELD_WIDTH;
+	}
+
+	si16 getY() const
+	{
+		return hex / GameConstants::BFIELD_WIDTH;
+	}
+
+	std::pair<si16, si16> getXY() const
+	{
+		return std::make_pair(getX(), getY());
+	}
+
+	BattleHex & moveInDirection(EDir dir, bool hasToBeValid = true)
+	{
+		si16 x = getX();
+		si16 y = getY();
+		switch(dir)
+		{
+		case TOP_LEFT:
+			setXY((y % 2) ? x - 1 : x, y - 1, hasToBeValid);
+			break;
+		case TOP_RIGHT:
+			setXY((y % 2) ? x : x + 1, y - 1, hasToBeValid);
+			break;
+		case RIGHT:
+			setXY(x + 1, y, hasToBeValid);
+			break;
+		case BOTTOM_RIGHT:
+			setXY((y % 2) ? x : x + 1, y + 1, hasToBeValid);
+			break;
+		case BOTTOM_LEFT:
+			setXY((y % 2) ? x - 1 : x, y + 1, hasToBeValid);
+			break;
+		case LEFT:
+			setXY(x - 1, y, hasToBeValid);
+			break;
+		case NONE:
+			break;
+		default:
+			throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n");
+			break;
+		}
+		return *this;
+	}
+
+	BattleHex & operator+=(EDir dir)
+	{
+		return moveInDirection(dir);
+	}
+
+	BattleHex operator+(EDir dir) const
+	{
+		return cloneInDirection(dir);
+	}
+
+	BattleHex cloneInDirection(EDir dir, bool hasToBeValid = true) const
+	{
+		BattleHex result(hex);
+		result.moveInDirection(dir, hasToBeValid);
+		return result;
+	}
 
 
-	static EDir mutualPosition(BattleHex hex1, BattleHex hex2);
 	static uint8_t getDistance(BattleHex hex1, BattleHex hex2)
 	static uint8_t getDistance(BattleHex hex1, BattleHex hex2)
 	{
 	{
 		int y1 = hex1.getY();
 		int y1 = hex1.getY();
@@ -108,17 +204,41 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
 
 
 		return std::abs(xDst) + std::abs(yDst);
 		return std::abs(xDst) + std::abs(yDst);
 	}
 	}
-	
+
+	static BattleHex getClosestTile(const BattleHexArray & hexes, BattleSide side, BattleHex initialPos);
+
+	//Constexpr defined array with all directions used in battle
+	static constexpr auto hexagonalDirections() 
+	{
+		return std::array<EDir,6>{TOP_LEFT, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT};
+	}
+
+	static EDir mutualPosition(BattleHex hex1, BattleHex hex2)
+	{
+		for(auto dir : hexagonalDirections())
+			if(hex2 == hex1.cloneInDirection(dir, false))
+				return dir;
+		return NONE;
+	}
+
+	/// get (precomputed) all possible surrounding tiles
+	const BattleHexArray & getAllNeighbouringTiles() const;
+
+	/// get (precomputed) only valid and available surrounding tiles
+	const BattleHexArray & getNeighbouringTiles() const;
+
+	/// get (precomputed) only valid and available surrounding tiles for double wide creatures
+	const BattleHexArray & getNeighbouringTilesDblWide(BattleSide side) const;
+
 	template <typename Handler>
 	template <typename Handler>
-	void serialize(Handler &h)
+	void serialize(Handler & h)
 	{
 	{
 		h & hex;
 		h & hex;
 	}
 	}
 
 
-	//Constexpr defined array with all directions used in battle
-	static constexpr auto hexagonalDirections() {
-		return std::array<EDir,6>{BattleHex::TOP_LEFT, BattleHex::TOP_RIGHT, BattleHex::RIGHT, BattleHex::BOTTOM_RIGHT, BattleHex::BOTTOM_LEFT, BattleHex::LEFT};
-	}
+private:
+
+	si16 hex;
 };
 };
 
 
 DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex);
 DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex);

+ 50 - 72
lib/battle/BattleHexArray.cpp

@@ -22,48 +22,42 @@ BattleHexArray::BattleHexArray(std::initializer_list<BattleHex> initList) noexce
 	}
 	}
 }
 }
 
 
-BattleHex BattleHexArray::getClosestTile(BattleSide side, BattleHex initialPos) const
+void BattleHexArray::insert(const BattleHexArray & other) noexcept
 {
 {
-	if(this->empty())
-		return BattleHex();
-
-	BattleHex initialHex = BattleHex(initialPos);
-	int closestDistance = std::numeric_limits<int>::max();
-	BattleHexArray closestTiles;
-
-	for(auto hex : internalStorage)
+	for(auto hex : other)
 	{
 	{
-		int distance = initialHex.getDistance(initialHex, hex);
-		if(distance < closestDistance)
-		{
-			closestDistance = distance;
-			closestTiles.clear();
-			closestTiles.insert(hex);
-		}
-		else if(distance == closestDistance)
-			closestTiles.insert(hex);
+		insert(hex);
 	}
 	}
+}
 
 
-	auto compareHorizontal = [side, initialPos](const BattleHex & left, const BattleHex & right)
+void BattleHexArray::erase(iterator first, iterator last) noexcept
+{
+	for(auto it = first; it != last && it != internalStorage.end(); ++it)
 	{
 	{
-		if(left.getX() != right.getX())
-		{
-			return (side == BattleSide::ATTACKER) ? (left.getX() > right.getX()) : (left.getX() < right.getX());
-		}
-		return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
-	};
+		presenceFlags[*it] = 0;
+	}
 
 
-	auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
-	return (bestTile != closestTiles.end()) ? *bestTile : BattleHex();
+	internalStorage.erase(first, last);
 }
 }
 
 
-BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTiles()
+void BattleHexArray::clear() noexcept
+{
+	for(auto hex : internalStorage)
+		presenceFlags[hex] = 0;
+
+	internalStorage.clear();
+}
+
+BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::precalculateNeighbouringTiles()
 {
 {
 	BattleHexArray::ArrayOfBattleHexArrays ret;
 	BattleHexArray::ArrayOfBattleHexArrays ret;
 
 
 	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
 	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
 	{
 	{
-		BattleHexArray hexes = BattleHexArray::generateNeighbouringTiles(hex);
+		BattleHexArray hexes;
+
+		for(auto dir : BattleHex::hexagonalDirections())
+			hexes.checkAndPush(BattleHex(hex).cloneInDirection(dir, false));
 
 
 		size_t index = 0;
 		size_t index = 0;
 		ret[hex].resize(hexes.size());
 		ret[hex].resize(hexes.size());
@@ -74,17 +68,33 @@ BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTile
 	return ret;
 	return ret;
 }
 }
 
 
-BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTilesDblWide(BattleSide side)
+BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::precalculateAllNeighbouringTiles()
 {
 {
 	ArrayOfBattleHexArrays ret;
 	ArrayOfBattleHexArrays ret;
 
 
-	for(BattleHex hex = 0; hex < GameConstants::BFIELD_SIZE; hex.hex++)
+	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
+	{
+		ret[hex].resize(6);
+
+		for(auto dir : BattleHex::hexagonalDirections())
+			ret[hex].set(dir, BattleHex(hex).cloneInDirection(dir, false));
+	}
+
+	return ret;
+}
+
+BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::precalculateNeighbouringTilesDblWide(BattleSide side)
+{
+	ArrayOfBattleHexArrays ret;
+
+	for(si16 h = 0; h < GameConstants::BFIELD_SIZE; h++)
 	{
 	{
 		BattleHexArray hexes;
 		BattleHexArray hexes;
+		BattleHex hex(h);
 
 
 		if(side == BattleSide::ATTACKER)
 		if(side == BattleSide::ATTACKER)
 		{
 		{
-			const BattleHex otherHex = hex - 1;
+			const BattleHex otherHex = h - 1;
 
 
 			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
 			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
 				hexes.checkAndPush(hex.cloneInDirection(dir, false));
 				hexes.checkAndPush(hex.cloneInDirection(dir, false));
@@ -95,7 +105,7 @@ BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTile
 		}
 		}
 		else if(side == BattleSide::DEFENDER)
 		else if(side == BattleSide::DEFENDER)
 		{
 		{
-			const BattleHex otherHex = hex + 1;
+			const BattleHex otherHex = h + 1;
 
 
 			hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
 			hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
 
 
@@ -105,50 +115,18 @@ BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTile
 			hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
 			hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
 			hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::LEFT, false));
 			hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::LEFT, false));
 		}
 		}
-		ret[hex.hex] = std::move(hexes);
+		ret[h] = std::move(hexes);
 	}
 	}
 
 
 	return ret;
 	return ret;
 }
 }
 
 
-BattleHexArray BattleHexArray::generateNeighbouringTiles(BattleHex hex)
-{
-	BattleHexArray ret;
-	for(auto dir : BattleHex::hexagonalDirections())
-		ret.checkAndPush(hex.cloneInDirection(dir, false));
-	
-	return ret;
-}
-
-void BattleHexArray::insert(const BattleHexArray & other) noexcept
-{
-	for(auto hex : other)
-	{
-		insert(hex);
-	}
-}
-
-void BattleHexArray::erase(iterator first, iterator last) noexcept
-{
-	for(auto it = first; it != last && it != internalStorage.end(); ++it)
+const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::neighbouringTiles = precalculateNeighbouringTiles();
+const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::allNeighbouringTiles = precalculateAllNeighbouringTiles();
+const std::map<BattleSide, BattleHexArray::ArrayOfBattleHexArrays> BattleHexArray::neighbouringTilesDblWide =
 	{
 	{
-		presenceFlags[*it] = 0;
-	}
-
-	internalStorage.erase(first, last);
-}
-
-void BattleHexArray::clear() noexcept
-{
-	for(auto hex : internalStorage)
-		presenceFlags[hex] = 0;
-
-	internalStorage.clear();
-}
-
-const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::neighbouringTilesCache = calculateNeighbouringTiles();
-const std::map<BattleSide, BattleHexArray::ArrayOfBattleHexArrays> BattleHexArray::neighbouringTilesDblWide = 
-	{ { BattleSide::ATTACKER, calculateNeighbouringTilesDblWide(BattleSide::ATTACKER) },
-	{ BattleSide::DEFENDER, calculateNeighbouringTilesDblWide(BattleSide::DEFENDER) } };
+	   { BattleSide::ATTACKER, precalculateNeighbouringTilesDblWide(BattleSide::ATTACKER) },
+	   { BattleSide::DEFENDER, precalculateNeighbouringTilesDblWide(BattleSide::DEFENDER) }
+	};
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END

+ 32 - 33
lib/battle/BattleHexArray.h

@@ -21,6 +21,7 @@ class DLL_LINKAGE BattleHexArray
 public:
 public:
 	static constexpr uint8_t totalSize = GameConstants::BFIELD_SIZE;
 	static constexpr uint8_t totalSize = GameConstants::BFIELD_SIZE;
 	using StorageType = boost::container::small_vector<BattleHex, 8>;
 	using StorageType = boost::container::small_vector<BattleHex, 8>;
+	using ArrayOfBattleHexArrays = std::array<BattleHexArray, totalSize>;
 
 
 	using value_type = BattleHex;
 	using value_type = BattleHex;
 	using size_type = StorageType::size_type;
 	using size_type = StorageType::size_type;
@@ -34,11 +35,6 @@ public:
 	using reverse_iterator = typename StorageType::reverse_iterator;
 	using reverse_iterator = typename StorageType::reverse_iterator;
 	using const_reverse_iterator = typename StorageType::const_reverse_iterator;
 	using const_reverse_iterator = typename StorageType::const_reverse_iterator;
 
 
-	using ArrayOfBattleHexArrays = std::array<BattleHexArray, GameConstants::BFIELD_SIZE>;
-
-	static const ArrayOfBattleHexArrays neighbouringTilesCache;
-	static const std::map<BattleSide, ArrayOfBattleHexArrays> neighbouringTilesDblWide;
-
 	BattleHexArray() = default;
 	BattleHexArray() = default;
 
 
 	template <typename Container, typename = std::enable_if_t<
 	template <typename Container, typename = std::enable_if_t<
@@ -60,28 +56,6 @@ public:
 	
 	
 	BattleHexArray(std::initializer_list<BattleHex> initList) noexcept;
 	BattleHexArray(std::initializer_list<BattleHex> initList) noexcept;
 
 
-	/// returns all tiles, unavailable tiles will be set as invalid
-	/// order of returned tiles matches EDir enum
-	static BattleHexArray getAllNeighbouringTiles(BattleHex hex)
-	{
-		static ArrayOfBattleHexArrays cache;
-		static bool initialized = false;
-
-		if(initialized)
-			return cache[hex.hex];
-
-		for(BattleHex h = 0; h < GameConstants::BFIELD_SIZE; h.hex++)
-		{
-			cache[h].resize(6);
-
-			for(auto dir : BattleHex::hexagonalDirections())
-				cache[h].set(dir, h.cloneInDirection(dir, false));
-		}
-		initialized = true;
-
-		return cache[hex.hex];
-	}
-
 	void checkAndPush(BattleHex tile)
 	void checkAndPush(BattleHex tile)
 	{
 	{
 		if(tile.isAvailable() && !contains(tile))
 		if(tile.isAvailable() && !contains(tile))
@@ -126,8 +100,6 @@ public:
 		return internalStorage.insert(pos, hex);
 		return internalStorage.insert(pos, hex);
 	}
 	}
 
 
-	BattleHex getClosestTile(BattleSide side, BattleHex initialPos) const;
-
 	void insert(const BattleHexArray & other) noexcept;
 	void insert(const BattleHexArray & other) noexcept;
 
 
 	template <typename Container, typename = std::enable_if_t<
 	template <typename Container, typename = std::enable_if_t<
@@ -185,6 +157,30 @@ public:
 		return filtered;
 		return filtered;
 	}
 	}
 
 
+	/// get (precomputed) all possible surrounding tiles
+	static const BattleHexArray & getAllNeighbouringTiles(BattleHex hex)
+	{
+		assert(hex.isValid());
+
+		return allNeighbouringTiles[hex];
+	}
+
+	/// get (precomputed) only valid and available surrounding tiles
+	static const BattleHexArray & getNeighbouringTiles(BattleHex hex)
+	{
+		assert(hex.isValid());
+
+		return neighbouringTiles[hex];
+	}
+
+	/// get (precomputed) only valid and available surrounding tiles for double wide creatures
+	static const BattleHexArray & getNeighbouringTilesDblWide(BattleHex hex, BattleSide side)
+	{
+		assert(hex.isValid() && (side == BattleSide::ATTACKER || BattleSide::DEFENDER));
+
+		return neighbouringTilesDblWide.at(side)[hex];
+	}
+
 	[[nodiscard]] inline bool contains(BattleHex hex) const noexcept
 	[[nodiscard]] inline bool contains(BattleHex hex) const noexcept
 	{
 	{
 		if(hex.isValid())
 		if(hex.isValid())
@@ -301,10 +297,13 @@ private:
 		return hex == BattleHex::CASTLE_CENTRAL_TOWER || hex == BattleHex::CASTLE_UPPER_TOWER || hex == BattleHex::CASTLE_BOTTOM_TOWER;
 		return hex == BattleHex::CASTLE_CENTRAL_TOWER || hex == BattleHex::CASTLE_UPPER_TOWER || hex == BattleHex::CASTLE_BOTTOM_TOWER;
 	}
 	}
 
 
-	/// returns all valid neighbouring tiles
-	static ArrayOfBattleHexArrays calculateNeighbouringTiles();
-	static ArrayOfBattleHexArrays calculateNeighbouringTilesDblWide(BattleSide side);
-	static BattleHexArray generateNeighbouringTiles(BattleHex hex);
+	static const ArrayOfBattleHexArrays neighbouringTiles;
+	static const ArrayOfBattleHexArrays allNeighbouringTiles;
+	static const std::map<BattleSide, ArrayOfBattleHexArrays> neighbouringTilesDblWide;
+
+	static ArrayOfBattleHexArrays precalculateNeighbouringTiles();
+	static ArrayOfBattleHexArrays precalculateAllNeighbouringTiles();
+	static ArrayOfBattleHexArrays precalculateNeighbouringTilesDblWide(BattleSide side);
 };
 };
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/battle/BattleInfo.cpp

@@ -709,7 +709,7 @@ void BattleInfo::setUnitState(uint32_t id, const JsonNode & data, int64_t health
 
 
 		if(!accessibility.accessible(changedStack->getPosition(), changedStack))
 		if(!accessibility.accessible(changedStack->getPosition(), changedStack))
 		{
 		{
-			logNetwork->error("Cannot resurrect %s because hex %d is occupied!", changedStack->nodeName(), changedStack->getPosition().hex);
+			logNetwork->error("Cannot resurrect %s because hex %d is occupied!", changedStack->nodeName(), changedStack->getPosition());
 			return; //position is already occupied
 			return; //position is already occupied
 		}
 		}
 	}
 	}

+ 12 - 12
lib/battle/CBattleInfoCallback.cpp

@@ -176,7 +176,7 @@ bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const
 bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
 bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
 {
 {
 	if (!from.isAvailable() || !dest.isAvailable())
 	if (!from.isAvailable() || !dest.isAvailable())
-		throw std::runtime_error("Invalid hex (" + std::to_string(from.hex) + " and " + std::to_string(dest.hex) + ") received in battleHasPenaltyOnLine!" );
+		throw std::runtime_error("Invalid hex (" + std::to_string(from) + " and " + std::to_string(dest) + ") received in battleHasPenaltyOnLine!" );
 
 
 	auto isTileBlocked = [&](BattleHex tile)
 	auto isTileBlocked = [&](BattleHex tile)
 	{
 	{
@@ -204,7 +204,7 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
 
 
 		while (next != dest)
 		while (next != dest)
 		{
 		{
-			next = BattleHexArray::neighbouringTilesCache[next].getClosestTile(direction, dest);
+			next = BattleHex::getClosestTile(next.getNeighbouringTiles(), direction, dest);
 			ret.insert(next);
 			ret.insert(next);
 		}
 		}
 		assert(!ret.empty());
 		assert(!ret.empty());
@@ -1077,9 +1077,9 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo & accessib
 		if(isInObstacle(curHex, obstacles, checkParams))
 		if(isInObstacle(curHex, obstacles, checkParams))
 			continue;
 			continue;
 
 
-		const int costToNeighbour = ret.distances.at(curHex.hex) + 1;
+		const int costToNeighbour = ret.distances.at(curHex) + 1;
 
 
-		for(BattleHex neighbour : BattleHexArray::neighbouringTilesCache[curHex.hex])
+		for(BattleHex neighbour : curHex.getNeighbouringTiles())
 		{
 		{
 			auto additionalCost = 0;
 			auto additionalCost = 0;
 
 
@@ -1093,13 +1093,13 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo & accessib
 				}
 				}
 			}
 			}
 
 
-			const int costFoundSoFar = ret.distances[neighbour.hex];
+			const int costFoundSoFar = ret.distances[neighbour];
 
 
-			if(accessibleCache[neighbour.hex] && costToNeighbour + additionalCost < costFoundSoFar)
+			if(accessibleCache[neighbour] && costToNeighbour + additionalCost < costFoundSoFar)
 			{
 			{
 				hexq.push(neighbour);
 				hexq.push(neighbour);
-				ret.distances[neighbour.hex] = costToNeighbour + additionalCost;
-				ret.predecessors[neighbour.hex] = curHex;
+				ret.distances[neighbour] = costToNeighbour + additionalCost;
+				ret.predecessors[neighbour] = curHex;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1222,7 +1222,7 @@ BattleHex CBattleInfoCallback::getAvailableHex(const CreatureID & creID, BattleS
 		return BattleHex::INVALID; //all tiles are covered
 		return BattleHex::INVALID; //all tiles are covered
 	}
 	}
 
 
-	return occupyable.getClosestTile(side, pos);
+	return BattleHex::getClosestTile(occupyable, side, pos);
 }
 }
 
 
 si8 CBattleInfoCallback::battleGetTacticDist() const
 si8 CBattleInfoCallback::battleGetTacticDist() const
@@ -1353,7 +1353,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
 	}
 	}
 	if(attacker->hasBonusOfType(BonusType::WIDE_BREATH))
 	if(attacker->hasBonusOfType(BonusType::WIDE_BREATH))
 	{
 	{
-		BattleHexArray hexes = BattleHexArray::neighbouringTilesCache[destinationTile];
+		BattleHexArray hexes = destinationTile.getNeighbouringTiles();
 		for(int i = 0; i < hexes.size(); i++)
 		for(int i = 0; i < hexes.size(); i++)
 		{
 		{
 			if(hexes.at(i) == attackOriginHex)
 			if(hexes.at(i) == attackOriginHex)
@@ -1426,9 +1426,9 @@ AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const battle::
 	AttackableTiles at;
 	AttackableTiles at;
 	RETURN_IF_NOT_BATTLE(at);
 	RETURN_IF_NOT_BATTLE(at);
 
 
-	if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !BattleHexArray::neighbouringTilesCache[attackerPos].contains(destinationTile))
+	if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !attackerPos.getNeighbouringTiles().contains(destinationTile))
 	{
 	{
-		at.hostileCreaturePositions.insert(BattleHexArray::neighbouringTilesCache[destinationTile]);
+		at.hostileCreaturePositions.insert(destinationTile.getNeighbouringTiles());
 		at.hostileCreaturePositions.insert(destinationTile);
 		at.hostileCreaturePositions.insert(destinationTile);
 	}
 	}
 
 

+ 1 - 1
lib/battle/Destination.h

@@ -10,7 +10,7 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "BattleHexArray.h"
+#include "BattleHex.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 

+ 1 - 1
lib/battle/ReachabilityInfo.cpp

@@ -44,7 +44,7 @@ uint32_t ReachabilityInfo::distToNearestNeighbour(
 
 
 	for(auto targetHex : targetHexes)
 	for(auto targetHex : targetHexes)
 	{
 	{
-		for(auto & n : BattleHexArray::neighbouringTilesCache[targetHex])
+		for(auto & n : targetHex.getNeighbouringTiles())
 		{
 		{
 			if(distances[n] < ret)
 			if(distances[n] < ret)
 			{
 			{

+ 3 - 5
lib/battle/Unit.cpp

@@ -60,12 +60,10 @@ const BattleHexArray & Unit::getSurroundingHexes(BattleHex assumedPosition) cons
 
 
 const BattleHexArray & Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
 const BattleHexArray & Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
 {
 {
-	assert(position.isValid());			// check outside if position isValid
-	
 	if(!twoHex)
 	if(!twoHex)
-		return BattleHexArray::neighbouringTilesCache[position];
+		return position.getNeighbouringTiles();
 
 
-	return BattleHexArray::neighbouringTilesDblWide.at(side).at(position);
+	return position.getNeighbouringTilesDblWide(side);
 }
 }
 
 
 BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
 BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
@@ -88,7 +86,7 @@ BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
 			hexes.pop_back();
 			hexes.pop_back();
 
 
 		for(auto hex : hexes)
 		for(auto hex : hexes)
-			targetableHexes.insert(BattleHexArray::neighbouringTilesCache[hex]);
+			targetableHexes.insert(hex.getNeighbouringTiles());
 	}
 	}
 
 
 	return targetableHexes;
 	return targetableHexes;

+ 1 - 1
lib/networkPacks/PacksForClientBattle.h

@@ -11,6 +11,7 @@
 
 
 #include "NetPacksBase.h"
 #include "NetPacksBase.h"
 #include "BattleChanges.h"
 #include "BattleChanges.h"
+#include "../battle/BattleHexArray.h"
 #include "../battle/BattleAction.h"
 #include "../battle/BattleAction.h"
 #include "../texts/MetaString.h"
 #include "../texts/MetaString.h"
 
 
@@ -22,7 +23,6 @@ class CGHeroInstance;
 class CArmedInstance;
 class CArmedInstance;
 class IBattleState;
 class IBattleState;
 class BattleInfo;
 class BattleInfo;
-class BattleHexArray;
 
 
 struct DLL_LINKAGE BattleStart : public CPackForClient
 struct DLL_LINKAGE BattleStart : public CPackForClient
 {
 {

+ 1 - 1
lib/spells/BattleSpellMechanics.cpp

@@ -610,7 +610,7 @@ std::vector<Destination> BattleSpellMechanics::getPossibleDestinations(size_t in
 			{
 			{
 				hexesToCheck.insert(stack->getPosition());
 				hexesToCheck.insert(stack->getPosition());
 
 
-				for(auto adjacent : BattleHexArray::neighbouringTilesCache[stack->getPosition().hex])
+				for(auto adjacent : stack->getPosition().getNeighbouringTiles())
 					hexesToCheck.insert(adjacent);
 					hexesToCheck.insert(adjacent);
 			}
 			}
 
 

+ 0 - 2
lib/spells/CSpellHandler.h

@@ -16,8 +16,6 @@
 #include "../IHandlerBase.h"
 #include "../IHandlerBase.h"
 #include "../ConstTransitivePtr.h"
 #include "../ConstTransitivePtr.h"
 #include "../int3.h"
 #include "../int3.h"
-#include "../GameConstants.h"
-#include "../battle/BattleHexArray.h"
 #include "../bonuses/Bonus.h"
 #include "../bonuses/Bonus.h"
 #include "../filesystem/ResourcePath.h"
 #include "../filesystem/ResourcePath.h"
 #include "../json/JsonNode.h"
 #include "../json/JsonNode.h"

+ 1 - 1
lib/spells/effects/Effect.h

@@ -14,7 +14,7 @@
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
-struct BattleHex;
+class BattleHex;
 class BattleHexArray;
 class BattleHexArray;
 class CBattleInfoCallback;
 class CBattleInfoCallback;
 class JsonSerializeFormat;
 class JsonSerializeFormat;

+ 1 - 0
lib/spells/effects/LocationEffect.cpp

@@ -11,6 +11,7 @@
 
 
 #include "LocationEffect.h"
 #include "LocationEffect.h"
 #include "../ISpellMechanics.h"
 #include "../ISpellMechanics.h"
+#include "battle/BattleHexArray.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 

+ 1 - 1
lib/spells/effects/UnitEffect.cpp

@@ -228,7 +228,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe
 		if(possibleHexes.empty())
 		if(possibleHexes.empty())
 			break;
 			break;
 
 
-		destHex = possibleHexes.getClosestTile(unit->unitSide(), destHex);
+		destHex = BattleHex::getClosestTile(possibleHexes, unit->unitSide(), destHex);
 	}
 	}
 
 
 	return effectTarget;
 	return effectTarget;

+ 1 - 1
server/battles/BattleActionProcessor.h

@@ -16,7 +16,7 @@ struct BattleLogMessage;
 struct BattleAttack;
 struct BattleAttack;
 class BattleAction;
 class BattleAction;
 class CBattleInfoCallback;
 class CBattleInfoCallback;
-struct BattleHex;
+class BattleHex;
 class CStack;
 class CStack;
 class PlayerColor;
 class PlayerColor;
 enum class BonusType : uint8_t;
 enum class BonusType : uint8_t;

+ 1 - 1
server/battles/BattleFlowProcessor.h

@@ -13,7 +13,7 @@
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 class CStack;
 class CStack;
-struct BattleHex;
+class BattleHex;
 class BattleHexArray;
 class BattleHexArray;
 class BattleAction;
 class BattleAction;
 class CBattleInfoCallback;
 class CBattleInfoCallback;