diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index 874ea8321..1c25b0512 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -280,7 +280,7 @@ int64_t AttackPossibility::evaluateBlockedShootersDmg( std::set 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); diff --git a/AI/BattleAI/BattleEvaluator.cpp b/AI/BattleAI/BattleEvaluator.cpp index 6beb15dc4..408d4d96e 100644 --- a/AI/BattleAI/BattleEvaluator.cpp +++ b/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()) diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index 69bbb1a5f..66fe316f4 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/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::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(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 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 allReachableUnits = additionalUnits; @@ -959,8 +960,7 @@ std::vector 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; diff --git a/AI/BattleAI/BattleExchangeVariant.h b/AI/BattleAI/BattleExchangeVariant.h index 844890778..0aba34749 100644 --- a/AI/BattleAI/BattleExchangeVariant.h +++ b/AI/BattleAI/BattleExchangeVariant.h @@ -56,7 +56,7 @@ struct MoveTarget float score; BattleHexArray positions; std::optional cachedAttack; - uint8_t turnsToRich; + uint8_t turnsToReach; MoveTarget(); }; diff --git a/lib/battle/BattleHexArray.cpp b/lib/battle/BattleHexArray.cpp index e36e98e96..f7bd751e8 100644 --- a/lib/battle/BattleHexArray.cpp +++ b/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) +{ + static std::array ret; // 2 -> only two valid sides: ATTACKER and DEFENDER + size_t sideIdx = static_cast(side); + static bool initialized[2] = { false, false }; + + if(!initialized[sideIdx]) + { + // first run, need to initialize + + 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[sideIdx][hex.hex] = std::move(hexes); + } + initialized[sideIdx] = true; + } + + return ret[sideIdx][pos.hex]; +} + +const BattleHexArray & BattleHexArray::getClosestTilesCache(BattleHex pos, BattleSide side) { assert(!neighbouringTilesCache.empty()); - BattleHexArray ret; + static std::array ret; + static bool initialized = false; + size_t sideIdx = static_cast(side); - ret.resize(GameConstants::BFIELD_SIZE); - - for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++) + if(!initialized) { - ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::ATTACKER, hex)); + ret[sideIdx].resize(GameConstants::BFIELD_SIZE); + + for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++) + { + ret[sideIdx].set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::ATTACKER, hex)); + } + initialized = true; } - return ret; -} - -BattleHexArray BattleHexArray::generateDefenderClosestTilesCache() -{ - assert(!neighbouringTilesCache.empty()); - - BattleHexArray ret; - - ret.resize(GameConstants::BFIELD_SIZE); - - for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++) - { - ret.set(hex, neighbouringTilesCache[hex].getClosestTile(BattleSide::DEFENDER, hex)); - } - - return ret; -} - -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 \ No newline at end of file diff --git a/lib/battle/BattleHexArray.h b/lib/battle/BattleHexArray.h index 4c9810328..5c51198ae 100644 --- a/lib/battle/BattleHexArray.h +++ b/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; + using ArrayOfBattleHexArrays = std::array; - 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 diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index d3e259c8f..c62cfba0e 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/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 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 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 ¶ms) 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 ¶ms) 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; diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index e7c11d5da..3d877a5e1 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/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 getNearestStack(const battle::Unit * closest) const; BattleHex getAvailableHex(const CreatureID & creID, BattleSide side, int initialPos = -1) const; //find place for adding new stack diff --git a/lib/battle/ReachabilityInfo.cpp b/lib/battle/ReachabilityInfo.cpp index c94d8b830..069c9a5cf 100644 --- a/lib/battle/ReachabilityInfo.cpp +++ b/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); } diff --git a/lib/battle/ReachabilityInfo.h b/lib/battle/ReachabilityInfo.h index af09b17e3..29687df66 100644 --- a/lib/battle/ReachabilityInfo.h +++ b/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 destructibleEnemyTurns; // how many turns it is needed to kill enemy on specific hex (index <=> hex) BattleHex startPosition; //assumed position of stack diff --git a/lib/battle/Unit.cpp b/lib/battle/Unit.cpp index 021173901..18a634eef 100644 --- a/lib/battle/Unit.cpp +++ b/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(0); dir <= static_cast(4); dir = static_cast(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(0); dir <= static_cast(4); dir = static_cast(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 diff --git a/lib/battle/Unit.h b/lib/battle/Unit.h index e900eaae3..8973cca73 100644 --- a/lib/battle/Unit.h +++ b/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