1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-19 21:10:12 +02:00

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)
This commit is contained in:
MichalZr6 2025-01-02 23:56:04 +01:00
parent ac8104d56d
commit dad6437661
27 changed files with 338 additions and 312 deletions

@ -384,7 +384,7 @@ AttackPossibility AttackPossibility::evaluate(
affectedUnits = defenderUnits;
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;

@ -215,7 +215,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->getCount(),
(int)bestAttack.from,
(int)bestAttack.attack.attacker->getPosition().hex,
(int)bestAttack.attack.attacker->getPosition(),
bestAttack.attack.chargeDistance,
bestAttack.attack.attacker->getMovementRange(0),
bestAttack.defenderDamageReduce,
@ -252,7 +252,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
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);
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",
stack->getDescription(),
moveTarget.cachedAttack->attack.defender->getDescription(),
moveTarget.cachedAttack->attack.defender->getPosition().hex,
moveTarget.cachedAttack->attack.defender->getPosition(),
moveTarget.score);
return goTowardsNearest(stack, moveTarget.positions, *targets);

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

@ -107,7 +107,8 @@ static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr<CBattleCallback>
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(s->isShooter())
shooters[i]++;

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

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

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

@ -195,7 +195,7 @@ const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) co
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

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

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

@ -9,130 +9,63 @@
*/
#include "StdInc.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)
VCMI_LIB_NAMESPACE_BEGIN
BattleHex BattleHex::getClosestTile(const BattleHexArray & hexes, BattleSide side, BattleHex initialPos)
{
setXY(x, y);
}
if(hexes.empty())
return BattleHex();
BattleHex::BattleHex(std::pair<si16, si16> xy)
{
setXY(xy);
}
BattleHex initialHex = BattleHex(initialPos);
int closestDistance = std::numeric_limits<int>::max();
BattleHexArray closestTiles;
BattleHex::operator si16() const
{
return hex;
}
void BattleHex::setX(si16 x)
{
setXY(x, getY());
}
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());
};
auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
return (bestTile != closestTiles.end()) ? *bestTile : BattleHex();
}
const BattleHexArray & BattleHex::getAllNeighbouringTiles() const
{
return BattleHexArray::getAllNeighbouringTiles(*this);
}
BattleHex & BattleHex::operator+=(BattleHex::EDir dir)
const BattleHexArray & BattleHex::getNeighbouringTiles() const
{
return moveInDirection(dir);
return BattleHexArray::getNeighbouringTiles(*this);
}
BattleHex BattleHex::cloneInDirection(BattleHex::EDir dir, bool hasToBeValid) const
const BattleHexArray & BattleHex::getNeighbouringTilesDblWide(BattleSide side) const
{
BattleHex result(hex);
result.moveInDirection(dir, hasToBeValid);
return result;
}
BattleHex BattleHex::operator+(BattleHex::EDir dir) const
{
return cloneInDirection(dir);
}
BattleHex::EDir BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2)
{
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)
{
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

@ -22,9 +22,13 @@ namespace GameConstants
const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
}
class BattleHexArray;
// 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
static constexpr si16 CASTLE_CENTRAL_TOWER = -2;
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_INNER = 96;
si16 hex;
static constexpr si16 INVALID = -1;
enum EDir
{
NONE = -1,
@ -64,11 +68,25 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
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
{
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;
}
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)
{
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);
}
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>
void serialize(Handler &h)
void serialize(Handler & h)
{
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);

@ -22,104 +22,6 @@ BattleHexArray::BattleHexArray(std::initializer_list<BattleHex> initList) noexce
}
}
BattleHex BattleHexArray::getClosestTile(BattleSide side, BattleHex initialPos) const
{
if(this->empty())
return BattleHex();
BattleHex initialHex = BattleHex(initialPos);
int closestDistance = std::numeric_limits<int>::max();
BattleHexArray closestTiles;
for(auto hex : internalStorage)
{
int distance = initialHex.getDistance(initialHex, hex);
if(distance < closestDistance)
{
closestDistance = distance;
closestTiles.clear();
closestTiles.insert(hex);
}
else if(distance == closestDistance)
closestTiles.insert(hex);
}
auto compareHorizontal = [side, initialPos](const BattleHex & left, const BattleHex & right)
{
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());
};
auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
return (bestTile != closestTiles.end()) ? *bestTile : BattleHex();
}
BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTiles()
{
BattleHexArray::ArrayOfBattleHexArrays ret;
for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
{
BattleHexArray hexes = BattleHexArray::generateNeighbouringTiles(hex);
size_t index = 0;
ret[hex].resize(hexes.size());
for(auto neighbour : hexes)
ret[hex].set(index++, neighbour);
}
return ret;
}
BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::calculateNeighbouringTilesDblWide(BattleSide side)
{
ArrayOfBattleHexArrays ret;
for(BattleHex hex = 0; hex < GameConstants::BFIELD_SIZE; hex.hex++)
{
BattleHexArray hexes;
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[hex.hex] = std::move(hexes);
}
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)
@ -146,9 +48,85 @@ void BattleHexArray::clear() noexcept
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) } };
BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::precalculateNeighbouringTiles()
{
BattleHexArray::ArrayOfBattleHexArrays ret;
for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
{
BattleHexArray hexes;
for(auto dir : BattleHex::hexagonalDirections())
hexes.checkAndPush(BattleHex(hex).cloneInDirection(dir, false));
size_t index = 0;
ret[hex].resize(hexes.size());
for(auto neighbour : hexes)
ret[hex].set(index++, neighbour);
}
return ret;
}
BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::precalculateAllNeighbouringTiles()
{
ArrayOfBattleHexArrays ret;
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;
BattleHex hex(h);
if(side == BattleSide::ATTACKER)
{
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))
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 = h + 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[h] = std::move(hexes);
}
return ret;
}
const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::neighbouringTiles = precalculateNeighbouringTiles();
const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::allNeighbouringTiles = precalculateAllNeighbouringTiles();
const std::map<BattleSide, BattleHexArray::ArrayOfBattleHexArrays> BattleHexArray::neighbouringTilesDblWide =
{
{ BattleSide::ATTACKER, precalculateNeighbouringTilesDblWide(BattleSide::ATTACKER) },
{ BattleSide::DEFENDER, precalculateNeighbouringTilesDblWide(BattleSide::DEFENDER) }
};
VCMI_LIB_NAMESPACE_END

@ -21,6 +21,7 @@ class DLL_LINKAGE BattleHexArray
public:
static constexpr uint8_t totalSize = GameConstants::BFIELD_SIZE;
using StorageType = boost::container::small_vector<BattleHex, 8>;
using ArrayOfBattleHexArrays = std::array<BattleHexArray, totalSize>;
using value_type = BattleHex;
using size_type = StorageType::size_type;
@ -34,11 +35,6 @@ public:
using reverse_iterator = typename StorageType::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;
template <typename Container, typename = std::enable_if_t<
@ -60,28 +56,6 @@ public:
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)
{
if(tile.isAvailable() && !contains(tile))
@ -126,8 +100,6 @@ public:
return internalStorage.insert(pos, hex);
}
BattleHex getClosestTile(BattleSide side, BattleHex initialPos) const;
void insert(const BattleHexArray & other) noexcept;
template <typename Container, typename = std::enable_if_t<
@ -185,6 +157,30 @@ public:
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
{
if(hex.isValid())
@ -301,10 +297,13 @@ private:
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

@ -709,7 +709,7 @@ void BattleInfo::setUnitState(uint32_t id, const JsonNode & data, int64_t health
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
}
}

@ -176,7 +176,7 @@ bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const
bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
{
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)
{
@ -204,7 +204,7 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
while (next != dest)
{
next = BattleHexArray::neighbouringTilesCache[next].getClosestTile(direction, dest);
next = BattleHex::getClosestTile(next.getNeighbouringTiles(), direction, dest);
ret.insert(next);
}
assert(!ret.empty());
@ -1077,9 +1077,9 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo & accessib
if(isInObstacle(curHex, obstacles, checkParams))
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;
@ -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);
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 occupyable.getClosestTile(side, pos);
return BattleHex::getClosestTile(occupyable, side, pos);
}
si8 CBattleInfoCallback::battleGetTacticDist() const
@ -1353,7 +1353,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
}
if(attacker->hasBonusOfType(BonusType::WIDE_BREATH))
{
BattleHexArray hexes = BattleHexArray::neighbouringTilesCache[destinationTile];
BattleHexArray hexes = destinationTile.getNeighbouringTiles();
for(int i = 0; i < hexes.size(); i++)
{
if(hexes.at(i) == attackOriginHex)
@ -1426,9 +1426,9 @@ AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const battle::
AttackableTiles 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);
}

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

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

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

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

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

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

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

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

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

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

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