diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index 1c25b0512..171cee8a7 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -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> defenderStates; diff --git a/AI/BattleAI/BattleEvaluator.cpp b/AI/BattleAI/BattleEvaluator.cpp index 7cb538a70..2acca4485 100644 --- a/AI/BattleAI/BattleEvaluator.cpp +++ b/AI/BattleAI/BattleEvaluator.cpp @@ -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); diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index 66fe316f4..9c491b412 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -960,7 +960,7 @@ std::vector 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; diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 6e25e8f44..631169143 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -107,7 +107,8 @@ static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr 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]++; diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 4d2f3203a..2eb9d8c9d 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -482,7 +482,7 @@ std::vector> BattleFieldController::getOutsideNeigh { // get all neighbours and their directions - const BattleHexArray & neighbouringTiles = BattleHexArray::getAllNeighbouringTiles(hex); + const BattleHexArray & neighbouringTiles = hex.getAllNeighbouringTiles(); std::vector outsideNeighbourDirections; @@ -492,9 +492,7 @@ std::vector> 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 diff --git a/client/battle/BattleInterface.h b/client/battle/BattleInterface.h index 43a529fd4..a5a2fe538 100644 --- a/client/battle/BattleInterface.h +++ b/client/battle/BattleInterface.h @@ -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; diff --git a/client/battle/BattleObstacleController.h b/client/battle/BattleObstacleController.h index 39119cf32..48a7ebfcd 100644 --- a/client/battle/BattleObstacleController.h +++ b/client/battle/BattleObstacleController.h @@ -13,7 +13,7 @@ VCMI_LIB_NAMESPACE_BEGIN -struct BattleHex; +class BattleHex; struct CObstacleInstance; class JsonNode; class ObstacleChanges; diff --git a/client/battle/BattleSiegeController.cpp b/client/battle/BattleSiegeController.cpp index 2f3c4df5e..cde4aa5c1 100644 --- a/client/battle/BattleSiegeController.cpp +++ b/client/battle/BattleSiegeController.cpp @@ -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 diff --git a/client/battle/BattleStacksController.h b/client/battle/BattleStacksController.h index f13f9fca4..6e79bdd4e 100644 --- a/client/battle/BattleStacksController.h +++ b/client/battle/BattleStacksController.h @@ -13,7 +13,7 @@ VCMI_LIB_NAMESPACE_BEGIN -struct BattleHex; +class BattleHex; class BattleHexArray; class BattleAction; class CStack; diff --git a/lib/battle/AccessibilityInfo.cpp b/lib/battle/AccessibilityInfo.cpp index 43854b026..efb90549a 100644 --- a/lib/battle/AccessibilityInfo.cpp +++ b/lib/battle/AccessibilityInfo.cpp @@ -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) diff --git a/lib/battle/BattleHex.cpp b/lib/battle/BattleHex.cpp index 7e21c4937..1da5de6f8 100644 --- a/lib/battle/BattleHex.cpp +++ b/lib/battle/BattleHex.cpp @@ -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 xy) -{ - setXY(xy); -} + BattleHex initialHex = BattleHex(initialPos); + int closestDistance = std::numeric_limits::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 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 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(hex)); } VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/BattleHex.h b/lib/battle/BattleHex.h index 6cf44fc40..e2226399a 100644 --- a/lib/battle/BattleHex.h +++ b/lib/battle/BattleHex.h @@ -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 xy); - operator si16() const; + BattleHex() + : hex(INVALID) + {} + BattleHex(si16 _hex) + : hex(_hex) + {} + BattleHex(si16 x, si16 y) + { + setXY(x, y); + } + BattleHex(std::pair 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 xy); - si16 getX() const; - si16 getY() const; - std::pair 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 xy) + { + setXY(xy.first, xy.second); + } + + si16 getX() const + { + return hex % GameConstants::BFIELD_WIDTH; + } + + si16 getY() const + { + return hex / GameConstants::BFIELD_WIDTH; + } + + std::pair 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{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 - 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{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); diff --git a/lib/battle/BattleHexArray.cpp b/lib/battle/BattleHexArray.cpp index c53090955..baddf6bf0 100644 --- a/lib/battle/BattleHexArray.cpp +++ b/lib/battle/BattleHexArray.cpp @@ -22,104 +22,6 @@ BattleHexArray::BattleHexArray(std::initializer_list initList) noexce } } -BattleHex BattleHexArray::getClosestTile(BattleSide side, BattleHex initialPos) const -{ - if(this->empty()) - return BattleHex(); - - BattleHex initialHex = BattleHex(initialPos); - int closestDistance = std::numeric_limits::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(0); dir <= static_cast(4); dir = static_cast(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(0); dir <= static_cast(4); dir = static_cast(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 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(0); dir <= static_cast(4); dir = static_cast(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(0); dir <= static_cast(4); dir = static_cast(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 BattleHexArray::neighbouringTilesDblWide = + { + { BattleSide::ATTACKER, precalculateNeighbouringTilesDblWide(BattleSide::ATTACKER) }, + { BattleSide::DEFENDER, precalculateNeighbouringTilesDblWide(BattleSide::DEFENDER) } + }; VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/battle/BattleHexArray.h b/lib/battle/BattleHexArray.h index 2d8c18da2..13d5bd75a 100644 --- a/lib/battle/BattleHexArray.h +++ b/lib/battle/BattleHexArray.h @@ -21,6 +21,7 @@ class DLL_LINKAGE BattleHexArray public: static constexpr uint8_t totalSize = GameConstants::BFIELD_SIZE; using StorageType = boost::container::small_vector; + using ArrayOfBattleHexArrays = std::array; 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; - - static const ArrayOfBattleHexArrays neighbouringTilesCache; - static const std::map neighbouringTilesDblWide; - BattleHexArray() = default; template 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 neighbouringTilesDblWide; + + static ArrayOfBattleHexArrays precalculateNeighbouringTiles(); + static ArrayOfBattleHexArrays precalculateAllNeighbouringTiles(); + static ArrayOfBattleHexArrays precalculateNeighbouringTilesDblWide(BattleSide side); }; VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 4a91d649e..c28165df5 100644 --- a/lib/battle/BattleInfo.cpp +++ b/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)) { - 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 } } diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 83ca6d781..396a9fc98 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/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 { 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); } diff --git a/lib/battle/Destination.h b/lib/battle/Destination.h index 25c7d9f97..2dc0a450c 100644 --- a/lib/battle/Destination.h +++ b/lib/battle/Destination.h @@ -10,7 +10,7 @@ #pragma once -#include "BattleHexArray.h" +#include "BattleHex.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/battle/ReachabilityInfo.cpp b/lib/battle/ReachabilityInfo.cpp index cb36f9973..b16496bce 100644 --- a/lib/battle/ReachabilityInfo.cpp +++ b/lib/battle/ReachabilityInfo.cpp @@ -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) { diff --git a/lib/battle/Unit.cpp b/lib/battle/Unit.cpp index a1f3e86ed..fdb97d4d4 100644 --- a/lib/battle/Unit.cpp +++ b/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) { - 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; diff --git a/lib/networkPacks/PacksForClientBattle.h b/lib/networkPacks/PacksForClientBattle.h index eacd574ef..916bcd857 100644 --- a/lib/networkPacks/PacksForClientBattle.h +++ b/lib/networkPacks/PacksForClientBattle.h @@ -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 { diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 5a66b4331..e448d78ec 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -610,7 +610,7 @@ std::vector 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); } diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 24ddb7846..8b82a2eb3 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -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" diff --git a/lib/spells/effects/Effect.h b/lib/spells/effects/Effect.h index 8cb5f446c..721b8ada0 100644 --- a/lib/spells/effects/Effect.h +++ b/lib/spells/effects/Effect.h @@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN -struct BattleHex; +class BattleHex; class BattleHexArray; class CBattleInfoCallback; class JsonSerializeFormat; diff --git a/lib/spells/effects/LocationEffect.cpp b/lib/spells/effects/LocationEffect.cpp index a1b1c3419..94b391b0b 100644 --- a/lib/spells/effects/LocationEffect.cpp +++ b/lib/spells/effects/LocationEffect.cpp @@ -11,6 +11,7 @@ #include "LocationEffect.h" #include "../ISpellMechanics.h" +#include "battle/BattleHexArray.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/spells/effects/UnitEffect.cpp b/lib/spells/effects/UnitEffect.cpp index 9a1f028e2..30e88a949 100644 --- a/lib/spells/effects/UnitEffect.cpp +++ b/lib/spells/effects/UnitEffect.cpp @@ -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; diff --git a/server/battles/BattleActionProcessor.h b/server/battles/BattleActionProcessor.h index 72744dc96..538f93da4 100644 --- a/server/battles/BattleActionProcessor.h +++ b/server/battles/BattleActionProcessor.h @@ -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; diff --git a/server/battles/BattleFlowProcessor.h b/server/battles/BattleFlowProcessor.h index b628bbc6a..6c50af985 100644 --- a/server/battles/BattleFlowProcessor.h +++ b/server/battles/BattleFlowProcessor.h @@ -13,7 +13,7 @@ VCMI_LIB_NAMESPACE_BEGIN class CStack; -struct BattleHex; +class BattleHex; class BattleHexArray; class BattleAction; class CBattleInfoCallback;