Browse Source

Unit.cpp refactor and some other minor changes

MichalZr6 10 tháng trước cách đây
mục cha
commit
fb9a3da651

+ 1 - 1
AI/BattleAI/AttackPossibility.cpp

@@ -280,7 +280,7 @@ int64_t AttackPossibility::evaluateBlockedShootersDmg(
 	std::set<uint32_t> checkedUnits;
 
 	auto attacker = attackInfo.attacker;
-	auto hexes = attacker->getSurroundingHexes(hex);
+	const BattleHexArray & hexes = attacker->getSurroundingHexes(hex);
 	for(BattleHex tile : hexes)
 	{
 		auto st = state->battleGetUnitByPos(tile, true);

+ 10 - 14
AI/BattleAI/BattleEvaluator.cpp

@@ -278,7 +278,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
 		score = moveTarget.score;
 		cachedAttack.ap = moveTarget.cachedAttack;
 		cachedAttack.score = score;
-		cachedAttack.turn = moveTarget.turnsToRich;
+		cachedAttack.turn = moveTarget.turnsToReach;
 
 		if(stack->waited())
 		{
@@ -390,11 +390,11 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, BattleHexAr
 			return reachability.distances[h1] < reachability.distances[h2];
 		});
 
-	BattleHex bestNeighbor = targetHexes.front();
+	BattleHex bestNeighbour = hexes.front();
 
-	if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
+	if(reachability.distances[bestNeighbour] > GameConstants::BFIELD_SIZE)
 	{
-		logAi->trace("No richable hexes.");
+		logAi->trace("No reachable hexes.");
 		return BattleAction::makeDefend(stack);
 	}
 
@@ -421,22 +421,17 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, BattleHexAr
 	{
 		BattleHexArray obstacleHexes;
 
-		auto insertAffected = [](const CObstacleInstance & spellObst, BattleHexArray & obstacleHexes) {
-			auto affectedHexes = spellObst.getAffectedTiles();
-			obstacleHexes.merge(affectedHexes);
-		};
-
 		const auto & obstacles = hb->battleGetAllObstacles();
 
-		for (const auto & obst : obstacles) {
-
+		for (const auto & obst : obstacles) 
+		{
 			if(obst->triggersEffects())
 			{
 				auto triggerAbility =  VLC->spells()->getById(obst->getTrigger());
 				auto triggerIsNegative = triggerAbility->isNegative() || triggerAbility->isDamage();
 
 				if(triggerIsNegative)
-					insertAffected(*obst, obstacleHexes);
+					obstacleHexes.merge(obst->getAffectedTiles());
 			}
 		}
 		// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
@@ -446,7 +441,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, BattleHexAr
 			const int NEGATIVE_OBSTACLE_PENALTY = 100; // avoid landing on negative obstacle (moat, fire wall, etc)
 			const int BLOCKED_STACK_PENALTY = 100; // avoid landing on moat
 
-			auto distance = BattleHex::getDistance(bestNeighbor, hex);
+			auto distance = BattleHex::getDistance(bestNeighbour, hex);
 
 			if(vstd::contains(obstacleHexes, hex))
 				distance += NEGATIVE_OBSTACLE_PENALTY;
@@ -458,7 +453,8 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, BattleHexAr
 	}
 	else
 	{
-		BattleHex currentDest = bestNeighbor;
+		BattleHex currentDest = bestNeighbour;
+
 		while(true)
 		{
 			if(!currentDest.isValid())

+ 16 - 16
AI/BattleAI/BattleExchangeVariant.cpp

@@ -21,7 +21,7 @@ AttackerValue::AttackerValue()
 MoveTarget::MoveTarget()
 	: positions(), cachedAttack(), score(EvaluationResult::INEFFECTIVE_SCORE)
 {
-	turnsToRich = 1;
+	turnsToReach = 1;
 }
 
 float BattleExchangeVariant::trackAttack(
@@ -361,7 +361,8 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
 		float penaltyMultiplier = 1.0f; // Default multiplier, no penalty
 		float closestAllyDistance = std::numeric_limits<float>::max();
 
-		for (const battle::Unit* ally : hb->battleAliveUnits()) {
+		for (const battle::Unit* ally : hb->battleAliveUnits()) 
+		{
 			if (ally == activeStack) 
 				continue;
 			if (ally->unitSide() != activeStack->unitSide()) 
@@ -375,12 +376,13 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
 		}
 
 		// If an ally is closer to the enemy, compute the penaltyMultiplier
-		if (closestAllyDistance < distance) {
+		if (closestAllyDistance < distance) 
+		{
 			penaltyMultiplier = closestAllyDistance / distance; // Ratio of distances
 		}
 
-		auto turnsToRich = (distance - 1) / speed + 1;
-		auto hexes = enemy->getSurroundingHexes();
+		auto turnsToReach = (distance - 1) / speed + 1;
+		const BattleHexArray & hexes = enemy->getSurroundingHexes();
 		auto enemySpeed = enemy->getMovementRange();
 		auto speedRatio = speed / static_cast<float>(enemySpeed);
 		auto multiplier = (speedRatio > 1 ? 1 : speedRatio) * penaltyMultiplier;
@@ -393,16 +395,16 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
 
 			attack.shootersBlockedDmg = 0; // we do not want to count on it, it is not for sure
 
-			auto score = calculateExchange(attack, turnsToRich, targets, damageCache, hb);
+			auto score = calculateExchange(attack, turnsToReach, targets, damageCache, hb);
 
 			score.enemyDamageReduce *= multiplier;
 
 #if BATTLE_TRACE_LEVEL >= 1
-			logAi->trace("Multiplier: %f, turns: %d, current score %f, new score %f", multiplier, turnsToRich, result.score, scoreValue(score));
+			logAi->trace("Multiplier: %f, turns: %d, current score %f, new score %f", multiplier, turnsToReach, result.score, scoreValue(score));
 #endif
 
 			if(result.score < scoreValue(score)
-				|| (result.turnsToRich > turnsToRich && vstd::isAlmostEqual(result.score, scoreValue(score))))
+				|| (result.turnsToReach > turnsToReach && vstd::isAlmostEqual(result.score, scoreValue(score))))
 			{
 				result.score = scoreValue(score);
 				result.positions.clear();
@@ -411,10 +413,8 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
 				logAi->trace("New high score");
 #endif
 
-				for(const BattleHex & initialEnemyHex : enemy->getAttackableHexes(activeStack))
+				for(BattleHex enemyHex : enemy->getAttackableHexes(activeStack))
 				{
-					BattleHex enemyHex = initialEnemyHex;
-
 					while(!flying && dists.distances[enemyHex] > speed && dists.predecessors.at(enemyHex).isValid())
 					{
 						enemyHex = dists.predecessors.at(enemyHex);
@@ -462,7 +462,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
 				}
 
 				result.cachedAttack = attack;
-				result.turnsToRich = turnsToRich;
+				result.turnsToReach = turnsToReach;
 			}
 		}
 	}
@@ -484,7 +484,7 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getAdjacentUnits(cons
 		queue.pop();
 		checkedStacks.push_back(stack);
 
-		auto hexes = stack->getSurroundingHexes();
+		auto const & hexes = stack->getSurroundingHexes();
 		for(auto hex : hexes)
 		{
 			auto neighbour = cb->battleGetUnitByPos(hex);
@@ -511,7 +511,8 @@ ReachabilityData BattleExchangeEvaluator::getExchangeUnits(
 
 	auto hexes = ap.attack.defender->getSurroundingHexes();
 
-	if(!ap.attack.shooting) hexes.insert(ap.from);
+	if(!ap.attack.shooting) 
+		hexes.insert(ap.from);
 
 	std::vector<const battle::Unit *> allReachableUnits = additionalUnits;
 	
@@ -959,8 +960,7 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getOneTurnReachableUn
 
 				if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
 				{
-					const BattleHexArray & neighbours = BattleHexArray::neighbouringTilesCache[hex.hex];
-					for(BattleHex neighbour : neighbours)
+					for(BattleHex neighbour : BattleHexArray::neighbouringTilesCache[hex.hex])
 					{
 						reachable = unitReachability.distances.at(neighbour) <= radius;
 

+ 1 - 1
AI/BattleAI/BattleExchangeVariant.h

@@ -56,7 +56,7 @@ struct MoveTarget
 	float score;
 	BattleHexArray positions;
 	std::optional<AttackPossibility> cachedAttack;
-	uint8_t turnsToRich;
+	uint8_t turnsToReach;
 
 	MoveTarget();
 };

+ 54 - 63
lib/battle/BattleHexArray.cpp

@@ -55,42 +55,11 @@ BattleHex BattleHexArray::getClosestTile(BattleSide side, BattleHex initialPos)
 
 	auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
 	return (bestTile != closestTiles.end()) ? *bestTile : BattleHex();
-
-	//BattleHex initialHex = BattleHex(initialPos);
-	//auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool
-	//{
-	//	return initialHex.getDistance(initialHex, left) < initialHex.getDistance(initialHex, right);
-	//};
-	//BattleHexArray sortedTiles(*this);
-	//boost::sort(sortedTiles, compareDistance); //closest tiles at front
-	//int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
-	//auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
-	//{
-	//	return closestDistance < here.getDistance(initialPos, here);
-	//};
-	//vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
-	//auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool
-	//{
-	//	if(left.getX() != right.getX())
-	//	{
-	//		if(side == BattleSide::ATTACKER)
-	//			return left.getX() > right.getX(); //find furthest right
-	//		else
-	//			return left.getX() < right.getX(); //find furthest left
-	//	}
-	//	else
-	//	{
-	//		//Prefer tiles in the same row.
-	//		return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
-	//	}
-	//};
-	//boost::sort(sortedTiles, compareHorizontal);
-	//return sortedTiles.front();
 }
 
-BattleHexArray::NeighbouringTilesCache BattleHexArray::calculateNeighbouringTiles()
+BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTiles()
 {
-	BattleHexArray::NeighbouringTilesCache ret;
+	BattleHexArray::ArrayOfBattleHexArrays ret;
 
 	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
 	{
@@ -110,50 +79,75 @@ BattleHexArray BattleHexArray::generateNeighbouringTiles(BattleHex hex)
 	BattleHexArray ret;
 	for(auto dir : BattleHex::hexagonalDirections())
 		ret.checkAndPush(hex.cloneInDirection(dir, false));
-
+	
 	return ret;
 }
 
-BattleHexArray BattleHexArray::generateAttackerClosestTilesCache()
+const BattleHexArray & BattleHexArray::getNeighbouringTilesDblWide(BattleHex pos, BattleSide side)
 {
-	assert(!neighbouringTilesCache.empty());
+	static std::array<ArrayOfBattleHexArrays, 2> ret;		// 2 -> only two valid sides: ATTACKER and DEFENDER
+	size_t sideIdx = static_cast<size_t>(side);
+	static bool initialized[2] = { false, false };
 
-	BattleHexArray ret;
+	if(!initialized[sideIdx])
+	{
+		// first run, need to initialize
 
-	ret.resize(GameConstants::BFIELD_SIZE);
+		for(BattleHex hex = 0; hex < GameConstants::BFIELD_SIZE; hex.hex++)
+		{
+			BattleHexArray hexes;
 
-	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
-	{
-		ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::ATTACKER, hex));
+			if(side == BattleSide::ATTACKER)
+			{
+				const BattleHex otherHex = hex - 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(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
+				hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false));
+				hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
+			}
+			else if(side == BattleSide::DEFENDER)
+			{
+				const BattleHex otherHex = hex + 1;
+
+				hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
+
+				for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
+					hexes.checkAndPush(otherHex.cloneInDirection(dir, false));
+
+				hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
+				hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::LEFT, false));
+			}
+			ret[sideIdx][hex.hex] = std::move(hexes);
+		}
+		initialized[sideIdx] = true;
 	}
 
-	return ret;
+	return ret[sideIdx][pos.hex];
 }
 
-BattleHexArray BattleHexArray::generateDefenderClosestTilesCache()
+const BattleHexArray & BattleHexArray::getClosestTilesCache(BattleHex pos, BattleSide side)
 {
 	assert(!neighbouringTilesCache.empty());
 
-	BattleHexArray ret;
-
-	ret.resize(GameConstants::BFIELD_SIZE);
+	static std::array<BattleHexArray, 2> ret;
+	static bool initialized = false;
+	size_t sideIdx = static_cast<size_t>(side);
 
-	for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
+	if(!initialized)
 	{
-		ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::DEFENDER, hex));
-	}
+		ret[sideIdx].resize(GameConstants::BFIELD_SIZE);
 
-	return ret;
-}
+		for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
+		{
+			ret[sideIdx].set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::ATTACKER, hex));
+		}
+		initialized = true;
+	}
 
-BattleHex BattleHexArray::getClosestTileFromAllPossibleNeighbours(BattleSide side, BattleHex pos)
-{
-	if(side == BattleSide::ATTACKER)
-			return closestTilesCacheForAttacker[pos.hex];
-	else if(side == BattleSide::DEFENDER)
-		return closestTilesCacheForDefender[pos.hex];
-	else
-		assert(false);		// we should never be here
+	return ret[sideIdx];
 }
 
 void BattleHexArray::merge(const BattleHexArray & other) noexcept
@@ -182,9 +176,6 @@ void BattleHexArray::clear() noexcept
 	internalStorage.clear();
 }
 
-const BattleHexArray::NeighbouringTilesCache BattleHexArray::neighbouringTilesCache = calculateNeighbouringTiles();
-
-const BattleHexArray BattleHexArray::closestTilesCacheForAttacker = generateAttackerClosestTilesCache();
-const BattleHexArray BattleHexArray::closestTilesCacheForDefender = generateDefenderClosestTilesCache();
+const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::neighbouringTilesCache = calculateNeighbouringTiles();
 
 VCMI_LIB_NAMESPACE_END

+ 6 - 9
lib/battle/BattleHexArray.h

@@ -33,11 +33,9 @@ public:
 	using reverse_iterator = typename StorageType::reverse_iterator;
 	using const_reverse_iterator = typename StorageType::const_reverse_iterator;
 
-	using NeighbouringTilesCache = std::array<BattleHexArray, GameConstants::BFIELD_SIZE>;
+	using ArrayOfBattleHexArrays = std::array<BattleHexArray, GameConstants::BFIELD_SIZE>;
 
-	static const NeighbouringTilesCache neighbouringTilesCache;
-	static const BattleHexArray closestTilesCacheForAttacker;
-	static const BattleHexArray closestTilesCacheForDefender;
+	static const ArrayOfBattleHexArrays neighbouringTilesCache;
 
 	BattleHexArray() noexcept
 	{
@@ -130,7 +128,8 @@ public:
 		return internalStorage.insert(pos, hex);
 	}
 
-	static BattleHex getClosestTileFromAllPossibleNeighbours(BattleSide side, BattleHex pos);
+	static const BattleHexArray & getClosestTilesCache(BattleHex pos, BattleSide side);
+	static const BattleHexArray & getNeighbouringTilesDblWide(BattleHex pos, BattleSide side);
 
 	BattleHex getClosestTile(BattleSide side, BattleHex initialPos) const;
 
@@ -190,7 +189,7 @@ public:
 			logGlobal->warn("BattleHexArray::contains( %d ) - invalid BattleHex!", hex);
 		*/
 		
-		// return true for invalid hexes
+		// returns true also for invalid hexes
 		return true;
 	}
 
@@ -298,10 +297,8 @@ private:
 	}
 
 	/// returns all valid neighbouring tiles
-	static BattleHexArray::NeighbouringTilesCache calculateNeighbouringTiles();
+	static BattleHexArray::ArrayOfBattleHexArrays calculateNeighbouringTiles();
 	static BattleHexArray generateNeighbouringTiles(BattleHex hex);
-	static BattleHexArray generateAttackerClosestTilesCache();
-	static BattleHexArray generateDefenderClosestTilesCache();
 };
 
 VCMI_LIB_NAMESPACE_END

+ 16 - 20
lib/battle/CBattleInfoCallback.cpp

@@ -651,7 +651,7 @@ BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit *
 			if(!otherSt->isValidTarget(false))
 				continue;
 
-			BattleHexArray occupied = otherSt->getHexes();
+			const BattleHexArray & occupied = otherSt->getHexes();
 
 			if(battleCanShoot(unit, otherSt->getPosition()))
 			{
@@ -964,9 +964,7 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
 
 	if(bFieldType != BattleField::NONE)
 	{
-		BattleHexArray impassableHexes = bFieldType.getInfo()->impassableHexes;
-
-		for(auto hex : impassableHexes)
+		for(auto hex : bFieldType.getInfo()->impassableHexes)
 			ret[hex] = EAccessibility::UNAVAILABLE;
 	}
 
@@ -1031,20 +1029,20 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
 
 AccessibilityInfo CBattleInfoCallback::getAccessibility(const battle::Unit * stack) const
 {
-	return getAccessibility(battle::Unit::getHexes(stack->getPosition(), stack->doubleWide(), stack->unitSide()));
+	return getAccessibility(& battle::Unit::getHexes(stack->getPosition(), stack->doubleWide(), stack->unitSide()));
 }
 
-AccessibilityInfo CBattleInfoCallback::getAccessibility(const BattleHexArray & accessibleHexes) const
+AccessibilityInfo CBattleInfoCallback::getAccessibility(const BattleHexArray * accessibleHexes) const
 {
 	auto ret = getAccessibility();
-	for(auto hex : accessibleHexes)
+	for(auto hex : *accessibleHexes)
 		if(hex.isValid())
 			ret[hex] = EAccessibility::ACCESSIBLE;
 
 	return ret;
 }
 
-ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibility, const ReachabilityInfo::Parameters & params) const
+ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const
 {
 	ReachabilityInfo ret;
 	ret.accessibility = accessibility;
@@ -1114,11 +1112,11 @@ bool CBattleInfoCallback::isInObstacle(
 	const BattleHexArray & obstacleHexes,
 	const ReachabilityInfo::Parameters & params) const
 {
-	auto occupiedHexes = battle::Unit::getHexes(hex, params.doubleWide, params.side);
+	const BattleHexArray & occupiedHexes = battle::Unit::getHexes(hex, params.doubleWide, params.side);
 
 	for(auto occupiedHex : occupiedHexes)
 	{
-		if(params.ignoreKnownAccessible && params.knownAccessible.contains(occupiedHex))
+		if(params.ignoreKnownAccessible && params.knownAccessible->contains(occupiedHex))
 			continue;
 
 		if(obstacleHexes.contains(occupiedHex))
@@ -1146,7 +1144,7 @@ BattleHexArray CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective)
 		if(!battleIsObstacleVisibleForSide(*oi, whichSidePerspective))
 			continue;
 
-		for(const auto & hex : oi->getStoppingTile())
+		for(auto hex : oi->getStoppingTile())
 		{
 			if(hex == BattleHex::GATE_BRIDGE && oi->obstacleType == CObstacleInstance::MOAT)
 			{
@@ -1162,7 +1160,6 @@ BattleHexArray CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective)
 std::pair<const battle::Unit *, BattleHex> CBattleInfoCallback::getNearestStack(const battle::Unit * closest) const
 {
 	auto reachability = getReachability(closest);
-	auto avHexes = battleGetAvailableHexes(reachability, closest, false);
 
 	// I hate std::pairs with their undescriptive member names first / second
 	struct DistStack
@@ -1181,7 +1178,7 @@ std::pair<const battle::Unit *, BattleHex> CBattleInfoCallback::getNearestStack(
 
 	for(const battle::Unit * st : possible)
 	{
-		for(BattleHex hex : avHexes)
+		for(BattleHex hex : battleGetAvailableHexes(reachability, closest, false))
 			if(CStack::isMeleeAttackPossible(closest, st, hex))
 			{
 				DistStack hlp = {reachability.distances[hex], hex, st};
@@ -1269,7 +1266,7 @@ ReachabilityInfo CBattleInfoCallback::getReachability(const battle::Unit * unit)
 	return getReachability(params);
 }
 
-ReachabilityInfo CBattleInfoCallback::getReachability(const ReachabilityInfo::Parameters &params) const
+ReachabilityInfo CBattleInfoCallback::getReachability(const ReachabilityInfo::Parameters & params) const
 {
 	if(params.flying)
 		return getFlyingReachability(params);
@@ -1283,7 +1280,7 @@ ReachabilityInfo CBattleInfoCallback::getReachability(const ReachabilityInfo::Pa
 	}
 }
 
-ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityInfo::Parameters &params) const
+ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityInfo::Parameters & params) const
 {
 	ReachabilityInfo ret;
 	ret.accessibility = getAccessibility(params.knownAccessible);
@@ -1340,11 +1337,11 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
 	}
 	if(attacker->hasBonusOfType(BonusType::ATTACKS_ALL_ADJACENT))
 	{
-		boost::copy(attacker->getSurroundingHexes(attackerPos), vstd::set_inserter(at.hostileCreaturePositions));
+		at.hostileCreaturePositions.merge(attacker->getSurroundingHexes(attackerPos));
 	}
 	if(attacker->hasBonusOfType(BonusType::THREE_HEADED_ATTACK))
 	{
-		BattleHexArray hexes = attacker->getSurroundingHexes(attackerPos);
+		const BattleHexArray & hexes = attacker->getSurroundingHexes(attackerPos);
 		for(BattleHex tile : hexes)
 		{
 			if((BattleHex::mutualPosition(tile, destinationTile) > -1 && BattleHex::mutualPosition(tile, attackOriginHex) > -1)) //adjacent both to attacker's head and attacked tile
@@ -1432,9 +1429,8 @@ AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const battle::
 
 	if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !BattleHexArray::neighbouringTilesCache[attackerPos].contains(destinationTile))
 	{
-		auto targetHexes = BattleHexArray::neighbouringTilesCache[destinationTile];
-		targetHexes.insert(destinationTile);
-		boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions));
+		at.hostileCreaturePositions.merge(BattleHexArray::neighbouringTilesCache[destinationTile]);
+		at.hostileCreaturePositions.insert(destinationTile);
 	}
 
 	return at;

+ 1 - 1
lib/battle/CBattleInfoCallback.h

@@ -162,7 +162,7 @@ public:
 	ReachabilityInfo getReachability(const ReachabilityInfo::Parameters & params) const;
 	AccessibilityInfo getAccessibility() const;
 	AccessibilityInfo getAccessibility(const battle::Unit * stack) const; //Hexes occupied by stack will be marked as accessible.
-	AccessibilityInfo getAccessibility(const BattleHexArray & accessibleHexes) const; //given hexes will be marked as accessible
+	AccessibilityInfo getAccessibility(const BattleHexArray * accessibleHexes) const; //given hexes will be marked as accessible
 	std::pair<const battle::Unit *, BattleHex> getNearestStack(const battle::Unit * closest) const;
 
 	BattleHex getAvailableHex(const CreatureID & creID, BattleSide side, int initialPos = -1) const; //find place for adding new stack

+ 1 - 1
lib/battle/ReachabilityInfo.cpp

@@ -21,7 +21,7 @@ ReachabilityInfo::Parameters::Parameters(const battle::Unit * Stack, BattleHex S
 	side(Stack->unitSide()),
 	flying(Stack->hasBonusOfType(BonusType::FLYING))
 {
-	knownAccessible = battle::Unit::getHexes(startPosition, doubleWide, side);
+	knownAccessible = & battle::Unit::getHexes(startPosition, doubleWide, side);
 	destructibleEnemyTurns.fill(-1);
 }
 

+ 1 - 1
lib/battle/ReachabilityInfo.h

@@ -32,7 +32,7 @@ struct DLL_LINKAGE ReachabilityInfo
 		bool flying = false;
 		bool ignoreKnownAccessible = false; //Ignore obstacles if it is in accessible hexes
 		bool bypassEnemyStacks = false; // in case of true will count amount of turns needed to kill enemy and thus move forward
-		BattleHexArray 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)
+		const BattleHexArray * 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)
 		std::array<int8_t, GameConstants::BFIELD_SIZE> destructibleEnemyTurns; // how many turns it is needed to kill enemy on specific hex (index <=> hex)
 
 		BattleHex startPosition; //assumed position of stack

+ 22 - 39
lib/battle/Unit.cpp

@@ -51,53 +51,26 @@ const IBonusBearer* Unit::getBonusBearer() const
 	return this;
 }
 
-BattleHexArray Unit::getSurroundingHexes(BattleHex assumedPosition) const
+const BattleHexArray & Unit::getSurroundingHexes(BattleHex assumedPosition) const
 {
 	BattleHex hex = (assumedPosition != BattleHex::INVALID) ? assumedPosition : getPosition(); //use hypothetical position
 
 	return getSurroundingHexes(hex, doubleWide(), unitSide());
 }
 
-BattleHexArray Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
+const BattleHexArray & Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
 {
-	if(!position.isValid())
-		return { };
-
-	BattleHexArray hexes;
-	if(twoHex)
-	{
-		const BattleHex otherHex = occupiedHex(position, twoHex, side);
-
-		if(side == BattleSide::ATTACKER)
-		{
-			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
-				hexes.checkAndPush(position.cloneInDirection(dir, false));
-
-			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
-			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false));
-			hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
-		}
-		else
-		{
-			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
-
-			for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
-				hexes.checkAndPush(otherHex.cloneInDirection(dir, false));
-
-			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
-			hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::LEFT, false));
-		}
-		return hexes;
-	}
-	else
-	{
+	assert(position.isValid());			// check outside if position isValid
+	
+	if(!twoHex)
 		return BattleHexArray::neighbouringTilesCache[position];
-	}
+
+	return BattleHexArray::getNeighbouringTilesDblWide(position, side);
 }
 
 BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
 {
-	auto defenderHexes = battle::Unit::getHexes(
+	const BattleHexArray & defenderHexes = battle::Unit::getHexes(
 		getPosition(),
 		doubleWide(),
 		unitSide());
@@ -126,25 +99,35 @@ bool Unit::coversPos(BattleHex pos) const
 	return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
 }
 
-BattleHexArray Unit::getHexes() const
+const BattleHexArray & Unit::getHexes() const
 {
 	return getHexes(getPosition(), doubleWide(), unitSide());
 }
 
-BattleHexArray Unit::getHexes(BattleHex assumedPos) const
+const BattleHexArray & Unit::getHexes(BattleHex assumedPos) const
 {
 	return getHexes(assumedPos, doubleWide(), unitSide());
 }
 
-BattleHexArray Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side)
+const BattleHexArray & Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side)
 {
+	static BattleHexArray::ArrayOfBattleHexArrays cache[4];
+	int index = side == BattleSide::ATTACKER ? 0 : 2;
+
+	if(!cache[index + twoHex][assumedPos].empty())
+		return cache[index + twoHex][assumedPos];
+
+	// first run, initialize
+
 	BattleHexArray hexes;
 	hexes.insert(assumedPos);
 
 	if(twoHex)
 		hexes.insert(occupiedHex(assumedPos, twoHex, side));
 
-	return hexes;
+	cache[index + twoHex][assumedPos] = std::move(hexes);
+
+	return cache[index + twoHex][assumedPos];
 }
 
 BattleHex Unit::occupiedHex() const

+ 5 - 5
lib/battle/Unit.h

@@ -127,15 +127,15 @@ public:
 
 	virtual std::string getDescription() const;
 
-	BattleHexArray getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
+	const BattleHexArray & getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
 	BattleHexArray getAttackableHexes(const Unit * attacker) const;
-	static BattleHexArray getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side);
+	static const BattleHexArray & getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side);
 
 	bool coversPos(BattleHex position) const; //checks also if unit is double-wide
 
-	BattleHexArray getHexes() const; //up to two occupied hexes, starting from front
-	BattleHexArray getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
-	static BattleHexArray getHexes(BattleHex assumedPos, bool twoHex, BattleSide side);
+	const BattleHexArray & getHexes() const; //up to two occupied hexes, starting from front
+	const BattleHexArray & getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
+	static const BattleHexArray & getHexes(BattleHex assumedPos, bool twoHex, BattleSide side);
 
 	BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
 	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