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

Unit.cpp refactor and some other minor changes

This commit is contained in:
MichalZr6 2024-12-04 21:37:31 +01:00
parent e3516120d8
commit fb9a3da651
12 changed files with 142 additions and 179 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -55,42 +55,11 @@ BattleHex BattleHexArray::getClosestTile(BattleSide side, BattleHex initialPos)
auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal); auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
return (bestTile != closestTiles.end()) ? *bestTile : BattleHex(); 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++) for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
{ {
@ -110,50 +79,75 @@ BattleHexArray BattleHexArray::generateNeighbouringTiles(BattleHex hex)
BattleHexArray ret; BattleHexArray ret;
for(auto dir : BattleHex::hexagonalDirections()) for(auto dir : BattleHex::hexagonalDirections())
ret.checkAndPush(hex.cloneInDirection(dir, false)); ret.checkAndPush(hex.cloneInDirection(dir, false));
return ret; return ret;
} }
BattleHexArray BattleHexArray::generateAttackerClosestTilesCache() const BattleHexArray & BattleHexArray::getNeighbouringTilesDblWide(BattleHex pos, BattleSide side)
{
static std::array<ArrayOfBattleHexArrays, 2> ret; // 2 -> only two valid sides: ATTACKER and DEFENDER
size_t sideIdx = static_cast<size_t>(side);
static bool initialized[2] = { false, false };
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<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
hexes.checkAndPush(hex.cloneInDirection(dir, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
}
else if(side == BattleSide::DEFENDER)
{
const BattleHex otherHex = hex + 1;
hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
hexes.checkAndPush(otherHex.cloneInDirection(dir, false));
hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::LEFT, false));
}
ret[sideIdx][hex.hex] = std::move(hexes);
}
initialized[sideIdx] = true;
}
return ret[sideIdx][pos.hex];
}
const BattleHexArray & BattleHexArray::getClosestTilesCache(BattleHex pos, BattleSide side)
{ {
assert(!neighbouringTilesCache.empty()); assert(!neighbouringTilesCache.empty());
BattleHexArray ret; static std::array<BattleHexArray, 2> ret;
static bool initialized = false;
size_t sideIdx = static_cast<size_t>(side);
ret.resize(GameConstants::BFIELD_SIZE); if(!initialized)
for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
{ {
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; return ret[sideIdx];
}
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
} }
void BattleHexArray::merge(const BattleHexArray & other) noexcept void BattleHexArray::merge(const BattleHexArray & other) noexcept
@ -182,9 +176,6 @@ void BattleHexArray::clear() noexcept
internalStorage.clear(); internalStorage.clear();
} }
const BattleHexArray::NeighbouringTilesCache BattleHexArray::neighbouringTilesCache = calculateNeighbouringTiles(); const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::neighbouringTilesCache = calculateNeighbouringTiles();
const BattleHexArray BattleHexArray::closestTilesCacheForAttacker = generateAttackerClosestTilesCache();
const BattleHexArray BattleHexArray::closestTilesCacheForDefender = generateDefenderClosestTilesCache();
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

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

View File

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

View File

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

View File

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

View File

@ -32,7 +32,7 @@ struct DLL_LINKAGE ReachabilityInfo
bool flying = false; bool flying = false;
bool ignoreKnownAccessible = false; //Ignore obstacles if it is in accessible hexes 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 bool bypassEnemyStacks = false; // in case of true will count amount of turns needed to kill enemy and thus move forward
BattleHexArray knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself) const BattleHexArray * knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself)
std::array<int8_t, GameConstants::BFIELD_SIZE> destructibleEnemyTurns; // how many turns it is needed to kill enemy on specific hex (index <=> hex) std::array<int8_t, GameConstants::BFIELD_SIZE> destructibleEnemyTurns; // how many turns it is needed to kill enemy on specific hex (index <=> hex)
BattleHex startPosition; //assumed position of stack BattleHex startPosition; //assumed position of stack

View File

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

View File

@ -127,15 +127,15 @@ public:
virtual std::string getDescription() const; 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; 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 bool coversPos(BattleHex position) const; //checks also if unit is double-wide
BattleHexArray getHexes() const; //up to two occupied hexes, starting from front const BattleHexArray & getHexes() const; //up to two occupied hexes, starting from front
BattleHexArray getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front const BattleHexArray & getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
static BattleHexArray getHexes(BattleHex assumedPos, bool twoHex, BattleSide side); 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() 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 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