1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-05 00:49:09 +02:00

Merge pull request #5044 from MichalZr6/battle_hex_array

New container for BattleHexes. Refactors aiming for quick-battle better performance.
This commit is contained in:
Ivan Savenko
2025-01-10 15:32:05 +02:00
committed by GitHub
91 changed files with 1271 additions and 836 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 auto & hexes = attacker->getSurroundingHexes(hex);
for(BattleHex tile : hexes) for(BattleHex tile : hexes)
{ {
auto st = state->battleGetUnitByPos(tile, true); auto st = state->battleGetUnitByPos(tile, true);
@ -326,9 +326,9 @@ AttackPossibility AttackPossibility::evaluate(
AttackPossibility bestAp(hex, BattleHex::INVALID, attackInfo); AttackPossibility bestAp(hex, BattleHex::INVALID, attackInfo);
std::vector<BattleHex> defenderHex; BattleHexArray defenderHex;
if(attackInfo.shooting) if(attackInfo.shooting)
defenderHex.push_back(defender->getPosition()); defenderHex.insert(defender->getPosition());
else else
defenderHex = CStack::meleeAttackHexes(attacker, defender, hex); defenderHex = CStack::meleeAttackHexes(attacker, defender, hex);
@ -384,7 +384,7 @@ AttackPossibility AttackPossibility::evaluate(
affectedUnits = defenderUnits; affectedUnits = defenderUnits;
vstd::concatenate(affectedUnits, retaliatedUnits); vstd::concatenate(affectedUnits, retaliatedUnits);
logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex.hex, defHex.hex); logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex, defHex);
std::map<uint32_t, std::shared_ptr<battle::CUnitState>> defenderStates; std::map<uint32_t, std::shared_ptr<battle::CUnitState>> defenderStates;

View File

@ -88,7 +88,7 @@ public:
//void battleResultsApplied() override; //called when all effects of last battle are applied //void battleResultsApplied() override; //called when all effects of last battle are applied
//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied; //void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
//void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn //void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
//void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance) override; //void battleStackMoved(const CStack * stack, BattleHexArray dest, int distance) override;
//void battleSpellCast(const BattleSpellCast *sc) override; //void battleSpellCast(const BattleSpellCast *sc) override;
//void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks //void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//void battleTriggerEffect(const BattleTriggerEffect & bte) override; //void battleTriggerEffect(const BattleTriggerEffect & bte) override;

View File

@ -214,8 +214,8 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
bestAttack.attackerState->unitType()->getJsonKey(), bestAttack.attackerState->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->unitType()->getJsonKey(), bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->getCount(), bestAttack.affectedUnits[0]->getCount(),
(int)bestAttack.from, bestAttack.from.toInt(),
(int)bestAttack.attack.attacker->getPosition().hex, bestAttack.attack.attacker->getPosition().toInt(),
bestAttack.attack.chargeDistance, bestAttack.attack.chargeDistance,
bestAttack.attack.attacker->getMovementRange(0), bestAttack.attack.attacker->getMovementRange(0),
bestAttack.defenderDamageReduce, bestAttack.defenderDamageReduce,
@ -252,7 +252,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
if(siegeDefense) 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); BattleAttackInfo bai(stack, stack, 0, false);
AttackPossibility apDefend(stack->getPosition(), stack->getPosition(), bai); AttackPossibility apDefend(stack->getPosition(), stack->getPosition(), bai);
@ -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())
{ {
@ -286,7 +286,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
"Moving %s towards hex %s[%d], score: %2f", "Moving %s towards hex %s[%d], score: %2f",
stack->getDescription(), stack->getDescription(),
moveTarget.cachedAttack->attack.defender->getDescription(), moveTarget.cachedAttack->attack.defender->getDescription(),
moveTarget.cachedAttack->attack.defender->getPosition().hex, moveTarget.cachedAttack->attack.defender->getPosition(),
moveTarget.score); moveTarget.score);
return goTowardsNearest(stack, moveTarget.positions, *targets); return goTowardsNearest(stack, moveTarget.positions, *targets);
@ -320,9 +320,9 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
return stack->waited() ? BattleAction::makeDefend(stack) : BattleAction::makeWait(stack); return stack->waited() ? BattleAction::makeDefend(stack) : BattleAction::makeWait(stack);
} }
uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start) uint64_t timeElapsed(std::chrono::time_point<std::chrono::steady_clock> start)
{ {
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
} }
@ -355,7 +355,7 @@ BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, BattleHex hex,
} }
} }
BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes, const PotentialTargets & targets) BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, const BattleHexArray & hexes, const PotentialTargets & targets)
{ {
auto reachability = cb->getBattle(battleID)->getReachability(stack); auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false); auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
@ -381,20 +381,20 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
} }
std::vector<BattleHex> targetHexes = hexes; BattleHexArray targetHexes = hexes;
vstd::erase_if(targetHexes, [](const BattleHex & hex) { return !hex.isValid(); }); vstd::erase_if(targetHexes, [](const BattleHex & hex) { return !hex.isValid(); });
std::sort(targetHexes.begin(), targetHexes.end(), [&](BattleHex h1, BattleHex h2) -> bool std::sort(targetHexes.begin(), targetHexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
{ {
return reachability.distances[h1] < reachability.distances[h2]; return reachability.distances[h1.toInt()] < reachability.distances[h2.toInt()];
}); });
BattleHex bestNeighbor = targetHexes.front(); BattleHex bestNeighbour = hexes.front();
if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE) if(reachability.distances[bestNeighbour.toInt()] > GameConstants::BFIELD_SIZE)
{ {
logAi->trace("No richable hexes."); logAi->trace("No reachable hexes.");
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
} }
@ -419,24 +419,19 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
if(stack->hasBonusOfType(BonusType::FLYING)) if(stack->hasBonusOfType(BonusType::FLYING))
{ {
std::set<BattleHex> obstacleHexes; BattleHexArray obstacleHexes;
auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> & obstacleHexes) {
auto affectedHexes = spellObst.getAffectedTiles();
obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend());
};
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.insert(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, std::vector
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, std::vector
} }
else else
{ {
BattleHex currentDest = bestNeighbor; BattleHex currentDest = bestNeighbour;
while(true) while(true)
{ {
if(!currentDest.isValid()) if(!currentDest.isValid())
@ -472,7 +468,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
return moveOrAttack(stack, currentDest, targets); return moveOrAttack(stack, currentDest, targets);
} }
currentDest = reachability.predecessors[currentDest]; currentDest = reachability.predecessors[currentDest.toInt()];
} }
} }

View File

@ -51,7 +51,7 @@ public:
bool attemptCastingSpell(const CStack * stack); bool attemptCastingSpell(const CStack * stack);
bool canCastSpell(); bool canCastSpell();
std::optional<PossibleSpellcast> findBestCreatureSpell(const CStack * stack); std::optional<PossibleSpellcast> findBestCreatureSpell(const CStack * stack);
BattleAction goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes, const PotentialTargets & targets); BattleAction goTowardsNearest(const CStack * stack, const BattleHexArray & hexes, const PotentialTargets & targets);
std::vector<BattleHex> getBrokenWallMoatHexes() const; std::vector<BattleHex> getBrokenWallMoatHexes() const;
bool hasWorkingTowers() const; bool hasWorkingTowers() const;
void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only

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(
@ -310,7 +310,7 @@ ReachabilityInfo getReachabilityWithEnemyBypass(
for(auto & hex : unit->getHexes()) for(auto & hex : unit->getHexes())
if(hex.isAvailable()) //towers can have <0 pos; we don't also want to overwrite side columns if(hex.isAvailable()) //towers can have <0 pos; we don't also want to overwrite side columns
params.destructibleEnemyTurns[hex] = turnsToKill * unit->getMovementRange(); params.destructibleEnemyTurns[hex.toInt()] = turnsToKill * unit->getMovementRange();
} }
params.bypassEnemyStacks = true; params.bypassEnemyStacks = true;
@ -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,15 +413,13 @@ 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.toInt()] > speed && dists.predecessors.at(enemyHex.toInt()).isValid())
while(!flying && dists.distances[enemyHex] > speed && dists.predecessors.at(enemyHex).isValid())
{ {
enemyHex = dists.predecessors.at(enemyHex); enemyHex = dists.predecessors.at(enemyHex.toInt());
if(dists.accessibility[enemyHex] == EAccessibility::ALIVE_STACK) if(dists.accessibility[enemyHex.toInt()] == EAccessibility::ALIVE_STACK)
{ {
auto defenderToBypass = hb->battleGetUnitByPos(enemyHex); auto defenderToBypass = hb->battleGetUnitByPos(enemyHex);
@ -429,7 +429,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
logAi->trace("Found target to bypass at %d", enemyHex.hex); logAi->trace("Found target to bypass at %d", enemyHex.hex);
#endif #endif
auto attackHex = dists.predecessors[enemyHex]; auto attackHex = dists.predecessors[enemyHex.toInt()];
auto baiBypass = BattleAttackInfo(activeStack, defenderToBypass, 0, cb->battleCanShoot(activeStack)); auto baiBypass = BattleAttackInfo(activeStack, defenderToBypass, 0, cb->battleCanShoot(activeStack));
auto attackBypass = AttackPossibility::evaluate(baiBypass, attackHex, damageCache, hb); auto attackBypass = AttackPossibility::evaluate(baiBypass, attackHex, damageCache, hb);
@ -440,7 +440,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
auto bypassScore = calculateExchange( auto bypassScore = calculateExchange(
attackBypass, attackBypass,
dists.distances[attackHex], dists.distances[attackHex.toInt()],
targets, targets,
damageCache, damageCache,
hb, hb,
@ -458,11 +458,11 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
} }
} }
result.positions.push_back(enemyHex); result.positions.insert(enemyHex);
} }
result.cachedAttack = attack; result.cachedAttack = attack;
result.turnsToRich = turnsToRich; result.turnsToReach = turnsToReach;
} }
} }
} }
@ -484,15 +484,15 @@ 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 neighbor = cb->battleGetUnitByPos(hex); auto neighbour = cb->battleGetUnitByPos(hex);
if(neighbor && neighbor->unitSide() == stack->unitSide() && !vstd::contains(checkedStacks, neighbor)) if(neighbour && neighbour->unitSide() == stack->unitSide() && !vstd::contains(checkedStacks, neighbour))
{ {
queue.push(neighbor); queue.push(neighbour);
checkedStacks.push_back(neighbor); checkedStacks.push_back(neighbour);
} }
} }
} }
@ -511,7 +511,8 @@ ReachabilityData BattleExchangeEvaluator::getExchangeUnits(
auto hexes = ap.attack.defender->getSurroundingHexes(); auto hexes = ap.attack.defender->getSurroundingHexes();
if(!ap.attack.shooting) hexes.push_back(ap.from); if(!ap.attack.shooting)
hexes.insert(ap.from);
std::vector<const battle::Unit *> allReachableUnits = additionalUnits; std::vector<const battle::Unit *> allReachableUnits = additionalUnits;
@ -915,8 +916,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(std::shared_ptr<HypotheticBa
} }
} }
} }
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); ++hex)
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
{ {
reachabilityMap[hex] = getOneTurnReachableUnits(0, hex); reachabilityMap[hex] = getOneTurnReachableUnits(0, hex);
} }
@ -951,17 +951,17 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getOneTurnReachableUn
ReachabilityInfo unitReachability = reachabilityIter != reachabilityCache.end() ? reachabilityIter->second : turnBattle.getReachability(unit); ReachabilityInfo unitReachability = reachabilityIter != reachabilityCache.end() ? reachabilityIter->second : turnBattle.getReachability(unit);
bool reachable = unitReachability.distances.at(hex) <= radius; bool reachable = unitReachability.distances.at(hex.toInt()) <= radius;
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK) if(!reachable && unitReachability.accessibility[hex.toInt()] == EAccessibility::ALIVE_STACK)
{ {
const battle::Unit * hexStack = cb->battleGetUnitByPos(hex); const battle::Unit * hexStack = cb->battleGetUnitByPos(hex);
if(hexStack && cb->battleMatchOwner(unit, hexStack, false)) if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
{ {
for(BattleHex neighbor : hex.neighbouringTiles()) for(BattleHex neighbour : hex.getNeighbouringTiles())
{ {
reachable = unitReachability.distances.at(neighbor) <= radius; reachable = unitReachability.distances.at(neighbour.toInt()) <= radius;
if(reachable) break; if(reachable) break;
} }
@ -1008,22 +1008,21 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
auto unitReachability = turnBattle.getReachability(unit); auto unitReachability = turnBattle.getReachability(unit);
auto unitSpeed = unit->getMovementRange(turn); // Cached value, to avoid performance hit auto unitSpeed = unit->getMovementRange(turn); // Cached value, to avoid performance hit
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1) for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex++)
{ {
bool enemyUnit = false; bool enemyUnit = false;
bool reachable = unitReachability.distances.at(hex) <= unitSpeed; bool reachable = unitReachability.distances.at(hex.toInt()) <= unitSpeed;
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK) if(!reachable && unitReachability.accessibility[hex.toInt()] == EAccessibility::ALIVE_STACK)
{ {
const battle::Unit * hexStack = turnBattle.battleGetUnitByPos(hex); const battle::Unit * hexStack = turnBattle.battleGetUnitByPos(hex);
if(hexStack && cb->battleMatchOwner(unit, hexStack, false)) if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
{ {
enemyUnit = true; enemyUnit = true;
for(BattleHex neighbour : hex.getNeighbouringTiles())
for(BattleHex neighbor : hex.neighbouringTiles())
{ {
reachable = unitReachability.distances.at(neighbor) <= unitSpeed; reachable = unitReachability.distances.at(neighbour.toInt()) <= unitSpeed;
if(reachable) break; if(reachable) break;
} }

View File

@ -54,9 +54,9 @@ struct AttackerValue
struct MoveTarget struct MoveTarget
{ {
float score; float score;
std::vector<BattleHex> positions; BattleHexArray positions;
std::optional<AttackPossibility> cachedAttack; std::optional<AttackPossibility> cachedAttack;
uint8_t turnsToRich; uint8_t turnsToReach;
MoveTarget(); MoveTarget();
}; };

View File

@ -50,7 +50,7 @@ PotentialTargets::PotentialTargets(
auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility
{ {
int distance = hex.isValid() ? reachability.distances[hex] : 0; int distance = hex.isValid() ? reachability.distances[hex.toInt()] : 0;
auto bai = BattleAttackInfo(attackerInfo, defender, distance, shooting); auto bai = BattleAttackInfo(attackerInfo, defender, distance, shooting);
return AttackPossibility::evaluate(bai, hex, damageCache, state); return AttackPossibility::evaluate(bai, hex, damageCache, state);

View File

@ -69,7 +69,7 @@ public:
const CStack * s; const CStack * s;
int adi; int adi;
int adr; int adr;
std::vector<BattleHex> attackFrom; //for melee fight BattleHexArray attackFrom; //for melee fight
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0) EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
{} {}
void calcDmg(std::shared_ptr<CBattleCallback> cb, const BattleID & battleID, const CStack * ourStack) void calcDmg(std::shared_ptr<CBattleCallback> cb, const BattleID & battleID, const CStack * ourStack)
@ -107,7 +107,8 @@ static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr<CBattleCallback>
for(int i = 0; i < 2; i++) for(int i = 0; i < 2; i++)
{ {
for (auto & neighbour : (i ? h2 : h1).neighbouringTiles()) BattleHex hex = i ? h2 : h1;
for (auto neighbour : hex.getNeighbouringTiles())
if(const auto * s = cb->getBattle(battleID)->battleGetUnitByPos(neighbour)) if(const auto * s = cb->getBattle(battleID)->battleGetUnitByPos(neighbour))
if(s->isShooter()) if(s->isShooter())
shooters[i]++; shooters[i]++;
@ -157,7 +158,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
} }
else else
{ {
std::vector<BattleHex> avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false); BattleHexArray avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false);
for (BattleHex hex : avHexes) for (BattleHex hex : avHexes)
{ {
@ -170,7 +171,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
i = enemiesReachable.begin() + (enemiesReachable.size() - 1); i = enemiesReachable.begin() + (enemiesReachable.size() - 1);
} }
i->attackFrom.push_back(hex); i->attackFrom.insert(hex);
} }
} }
@ -247,7 +248,7 @@ void CStupidAI::battleNewRound(const BattleID & battleID)
print("battleNewRound called"); print("battleNewRound called");
} }
void CStupidAI::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) void CStupidAI::battleStackMoved(const BattleID & battleID, const CStack * stack, const BattleHexArray & dest, int distance, bool teleport)
{ {
print("battleStackMoved called"); print("battleStackMoved called");
} }
@ -278,7 +279,7 @@ void CStupidAI::print(const std::string &text) const
logAi->trace("CStupidAI [%p]: %s", this, text); logAi->trace("CStupidAI [%p]: %s", this, text);
} }
BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> hexes) const BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stack, BattleHexArray hexes) const
{ {
auto reachability = cb->getBattle(battleID)->getReachability(stack); auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false); auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
@ -290,12 +291,12 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
std::sort(hexes.begin(), hexes.end(), [&](BattleHex h1, BattleHex h2) -> bool std::sort(hexes.begin(), hexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
{ {
return reachability.distances[h1] < reachability.distances[h2]; return reachability.distances[h1.toInt()] < reachability.distances[h2.toInt()];
}); });
for(auto hex : hexes) for(auto hex : hexes)
{ {
if(vstd::contains(avHexes, hex)) if(avHexes.contains(hex))
{ {
if(stack->position == hex) if(stack->position == hex)
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
@ -310,9 +311,9 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
} }
} }
BattleHex bestNeighbor = hexes.front(); BattleHex bestneighbour = hexes.front();
if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE) if(reachability.distances[bestneighbour.toInt()] > GameConstants::BFIELD_SIZE)
{ {
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
} }
@ -323,14 +324,14 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
// We just check all available hexes and pick the one closest to the target. // We just check all available hexes and pick the one closest to the target.
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
{ {
return BattleHex::getDistance(bestNeighbor, hex); return BattleHex::getDistance(bestneighbour, hex);
}); });
return BattleAction::makeMove(stack, *nearestAvailableHex); return BattleAction::makeMove(stack, *nearestAvailableHex);
} }
else else
{ {
BattleHex currentDest = bestNeighbor; BattleHex currentDest = bestneighbour;
while(1) while(1)
{ {
if(!currentDest.isValid()) if(!currentDest.isValid())
@ -339,14 +340,14 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
} }
if(vstd::contains(avHexes, currentDest)) if(avHexes.contains(currentDest))
{ {
if(stack->position == currentDest) if(stack->position == currentDest)
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
return BattleAction::makeMove(stack, currentDest); return BattleAction::makeMove(stack, currentDest);
} }
currentDest = reachability.predecessors[currentDest]; currentDest = reachability.predecessors[currentDest.toInt()];
} }
} }
} }

View File

@ -43,7 +43,7 @@ public:
//void battleResultsApplied() override; //called when all effects of last battle are applied //void battleResultsApplied() override; //called when all effects of last battle are applied
void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied;
void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override; void battleStackMoved(const BattleID & battleID, const CStack * stack, const BattleHexArray & dest, int distance, bool teleport) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override; void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//void battleTriggerEffect(const BattleTriggerEffect & bte) override; //void battleTriggerEffect(const BattleTriggerEffect & bte) override;
@ -51,6 +51,6 @@ public:
void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack
private: private:
BattleAction goTowards(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> hexes) const; BattleAction goTowards(const BattleID & battleID, const CStack * stack, BattleHexArray hexes) const;
}; };

View File

@ -846,7 +846,7 @@ void CPlayerInterface::battleLogMessage(const BattleID & battleID, const std::ve
battleInt->displayBattleLog(lines); battleInt->displayBattleLog(lines);
} }
void CPlayerInterface::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) void CPlayerInterface::battleStackMoved(const BattleID & battleID, const CStack * stack, const BattleHexArray & dest, int distance, bool teleport)
{ {
EVENT_HANDLER_CALLED_BY_CLIENT; EVENT_HANDLER_CALLED_BY_CLIENT;
BATTLE_EVENT_POSSIBLE_RETURN; BATTLE_EVENT_POSSIBLE_RETURN;

View File

@ -154,7 +154,7 @@ protected: // Call-ins from server, should not be called directly, but only via
void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; used for HP regen handling void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; used for HP regen handling
void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
void battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines) override; void battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines) override;
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override; void battleStackMoved(const BattleID & battleID, const CStack * stack, const BattleHexArray & dest, int distance, bool teleport) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override; void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override; //called when a specific effect is set to stacks void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override; //called when a specific effect is set to stacks
void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte) override; //various one-shot effect void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte) override; //various one-shot effect

View File

@ -522,7 +522,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
{ {
const auto * attacker = owner.stacksController->getActiveStack(); const auto * attacker = owner.stacksController->getActiveStack();
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex); BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
int distance = attacker->position.isValid() ? owner.getBattle()->battleGetDistances(attacker, attacker->getPosition())[attackFromHex] : 0; int distance = attacker->position.isValid() ? owner.getBattle()->battleGetDistances(attacker, attacker->getPosition())[attackFromHex.toInt()] : 0;
DamageEstimation retaliation; DamageEstimation retaliation;
BattleAttackInfo attackInfo(attacker, targetStack, distance, false ); BattleAttackInfo attackInfo(attacker, targetStack, distance, false );
DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation); DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation);
@ -723,11 +723,11 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
{ {
if(owner.stacksController->getActiveStack()->doubleWide()) if(owner.stacksController->getActiveStack()->doubleWide())
{ {
std::vector<BattleHex> acc = owner.getBattle()->battleGetAvailableHexes(owner.stacksController->getActiveStack(), false); BattleHexArray acc = owner.getBattle()->battleGetAvailableHexes(owner.stacksController->getActiveStack(), false);
BattleHex shiftedDest = targetHex.cloneInDirection(owner.stacksController->getActiveStack()->destShiftDir(), false); BattleHex shiftedDest = targetHex.cloneInDirection(owner.stacksController->getActiveStack()->destShiftDir(), false);
if(vstd::contains(acc, targetHex)) if(acc.contains(targetHex))
owner.giveCommand(EActionType::WALK, targetHex); owner.giveCommand(EActionType::WALK, targetHex);
else if(vstd::contains(acc, shiftedDest)) else if(acc.contains(shiftedDest))
owner.giveCommand(EActionType::WALK, shiftedDest); owner.giveCommand(EActionType::WALK, shiftedDest);
} }
else else
@ -1008,12 +1008,12 @@ bool BattleActionsController::isCastingPossibleHere(const CSpell * currentSpell,
bool BattleActionsController::canStackMoveHere(const CStack * stackToMove, BattleHex myNumber) const bool BattleActionsController::canStackMoveHere(const CStack * stackToMove, BattleHex myNumber) const
{ {
std::vector<BattleHex> acc = owner.getBattle()->battleGetAvailableHexes(stackToMove, false); BattleHexArray acc = owner.getBattle()->battleGetAvailableHexes(stackToMove, false);
BattleHex shiftedDest = myNumber.cloneInDirection(stackToMove->destShiftDir(), false); BattleHex shiftedDest = myNumber.cloneInDirection(stackToMove->destShiftDir(), false);
if (vstd::contains(acc, myNumber)) if (acc.contains(myNumber))
return true; return true;
else if (stackToMove->doubleWide() && vstd::contains(acc, shiftedDest)) else if (stackToMove->doubleWide() && acc.contains(shiftedDest))
return true; return true;
else else
return false; return false;
@ -1126,4 +1126,4 @@ void BattleActionsController::pushFrontPossibleAction(PossiblePlayerBattleAction
void BattleActionsController::resetCurrentStackPossibleActions() void BattleActionsController::resetCurrentStackPossibleActions()
{ {
possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack()); possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack());
} }

View File

@ -174,7 +174,7 @@ AttackAnimation::AttackAnimation(BattleInterface & owner, const CStack *attacker
attackingStack(attacker) attackingStack(attacker)
{ {
assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n"); assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n");
attackingStackPosBeforeReturn = attackingStack->getPosition(); attackingStackPosBeforeReturn = attackingStack->getPosition().toInt();
} }
HittedAnimation::HittedAnimation(BattleInterface & owner, const CStack * stack) HittedAnimation::HittedAnimation(BattleInterface & owner, const CStack * stack)
@ -422,7 +422,7 @@ MovementAnimation::~MovementAnimation()
CCS->soundh->stopSound(moveSoundHandler); CCS->soundh->stopSound(moveSoundHandler);
} }
MovementAnimation::MovementAnimation(BattleInterface & owner, const CStack *stack, std::vector<BattleHex> _destTiles, int _distance) MovementAnimation::MovementAnimation(BattleInterface & owner, const CStack *stack, const BattleHexArray & _destTiles, int _distance)
: StackMoveAnimation(owner, stack, stack->getPosition(), _destTiles.front()), : StackMoveAnimation(owner, stack, stack->getPosition(), _destTiles.front()),
destTiles(_destTiles), destTiles(_destTiles),
currentMoveIndex(0), currentMoveIndex(0),
@ -892,17 +892,17 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
logAnim->debug("CPointEffectAnimation::init: effect %s", animationName.getName()); logAnim->debug("CPointEffectAnimation::init: effect %s", animationName.getName());
} }
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects, bool reversed): EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, const BattleHexArray & hexes, int effects, bool reversed):
EffectAnimation(owner, animationName, effects, 1.0f, reversed) EffectAnimation(owner, animationName, effects, 1.0f, reversed)
{ {
battlehexes = hex; battlehexes = hexes;
} }
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex, int effects, float transparencyFactor, bool reversed): EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex, int effects, float transparencyFactor, bool reversed):
EffectAnimation(owner, animationName, effects, transparencyFactor, reversed) EffectAnimation(owner, animationName, effects, transparencyFactor, reversed)
{ {
assert(hex.isValid()); assert(hex.isValid());
battlehexes.push_back(hex); battlehexes.insert(hex);
} }
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos, int effects, bool reversed): EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos, int effects, bool reversed):
@ -921,7 +921,7 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
EffectAnimation(owner, animationName, effects, 1.0f, reversed) EffectAnimation(owner, animationName, effects, 1.0f, reversed)
{ {
assert(hex.isValid()); assert(hex.isValid());
battlehexes.push_back(hex); battlehexes.insert(hex);
positions.push_back(pos); positions.push_back(pos);
} }

View File

@ -9,7 +9,7 @@
*/ */
#pragma once #pragma once
#include "../../lib/battle/BattleHex.h" #include "../../lib/battle/BattleHexArray.h"
#include "../../lib/filesystem/ResourcePath.h" #include "../../lib/filesystem/ResourcePath.h"
#include "BattleConstants.h" #include "BattleConstants.h"
@ -143,7 +143,7 @@ class MovementAnimation : public StackMoveAnimation
private: private:
int moveSoundHandler; // sound handler used when moving a unit int moveSoundHandler; // sound handler used when moving a unit
std::vector<BattleHex> destTiles; //full path, includes already passed hexes const BattleHexArray & destTiles; //full path, includes already passed hexes
ui32 currentMoveIndex; // index of nextHex in destTiles ui32 currentMoveIndex; // index of nextHex in destTiles
double begX, begY; // starting position double begX, begY; // starting position
@ -159,7 +159,7 @@ public:
bool init() override; bool init() override;
void tick(uint32_t msPassed) override; void tick(uint32_t msPassed) override;
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance); MovementAnimation(BattleInterface & owner, const CStack *_stack, const BattleHexArray & _destTiles, int _distance);
~MovementAnimation(); ~MovementAnimation();
}; };
@ -316,7 +316,7 @@ class EffectAnimation : public BattleAnimation
std::shared_ptr<CAnimation> animation; std::shared_ptr<CAnimation> animation;
std::vector<Point> positions; std::vector<Point> positions;
std::vector<BattleHex> battlehexes; BattleHexArray battlehexes;
bool alignToBottom() const; bool alignToBottom() const;
bool waitForSound() const; bool waitForSound() const;
@ -339,14 +339,14 @@ public:
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects = 0, float transparencyFactor = 1.f, bool reversed = false); EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects = 0, float transparencyFactor = 1.f, bool reversed = false);
/// Create animation positioned at point(s). Note that positions must be are absolute, including battleint position offset /// Create animation positioned at point(s). Note that positions must be are absolute, including battleint position offset
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos , int effects = 0, bool reversed = false); EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos , int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos , int effects = 0, bool reversed = false); EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos , int effects = 0, bool reversed = false);
/// Create animation positioned at certain hex(es) /// Create animation positioned at certain hex(es)
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex , int effects = 0, float transparencyFactor = 1.0f, bool reversed = false); EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex , int effects = 0, float transparencyFactor = 1.0f, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects = 0, bool reversed = false); EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, const BattleHexArray & hexes, int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, BattleHex hex, int effects = 0, bool reversed = false); EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, BattleHex hex, int effects = 0, bool reversed = false);
~EffectAnimation(); ~EffectAnimation();
bool init() override; bool init() override;

View File

@ -267,7 +267,7 @@ void BattleFieldController::showBackgroundImageWithHexes(Canvas & canvas)
void BattleFieldController::redrawBackgroundWithHexes() void BattleFieldController::redrawBackgroundWithHexes()
{ {
const CStack *activeStack = owner.stacksController->getActiveStack(); const CStack *activeStack = owner.stacksController->getActiveStack();
std::vector<BattleHex> attackableHexes; BattleHexArray attackableHexes;
if(activeStack) if(activeStack)
occupiableHexes = owner.getBattle()->battleGetAvailableHexes(activeStack, false, true, &attackableHexes); occupiableHexes = owner.getBattle()->battleGetAvailableHexes(activeStack, false, true, &attackableHexes);
@ -280,8 +280,8 @@ void BattleFieldController::redrawBackgroundWithHexes()
// show shaded hexes for active's stack valid movement and the hexes that it can attack // show shaded hexes for active's stack valid movement and the hexes that it can attack
if(settings["battle"]["stackRange"].Bool()) if(settings["battle"]["stackRange"].Bool())
{ {
std::vector<BattleHex> hexesToShade = occupiableHexes; BattleHexArray hexesToShade = occupiableHexes;
hexesToShade.insert(hexesToShade.end(), attackableHexes.begin(), attackableHexes.end()); hexesToShade.insert(attackableHexes);
for(BattleHex hex : hexesToShade) for(BattleHex hex : hexesToShade)
{ {
showHighlightedHex(*backgroundWithHexes, cellShade, hex, false); showHighlightedHex(*backgroundWithHexes, cellShade, hex, false);
@ -312,9 +312,9 @@ void BattleFieldController::showHighlightedHex(Canvas & canvas, std::shared_ptr<
canvas.draw(cellBorder, hexPos); canvas.draw(cellBorder, hexPos);
} }
std::set<BattleHex> BattleFieldController::getHighlightedHexesForActiveStack() BattleHexArray BattleFieldController::getHighlightedHexesForActiveStack()
{ {
std::set<BattleHex> result; BattleHexArray result;
if(!owner.stacksController->getActiveStack()) if(!owner.stacksController->getActiveStack())
return result; return result;
@ -324,16 +324,16 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForActiveStack()
auto hoveredHex = getHoveredHex(); auto hoveredHex = getHoveredHex();
std::set<BattleHex> set = owner.getBattle()->battleGetAttackedHexes(owner.stacksController->getActiveStack(), hoveredHex); BattleHexArray set = owner.getBattle()->battleGetAttackedHexes(owner.stacksController->getActiveStack(), hoveredHex);
for(BattleHex hex : set) for(BattleHex hex : set)
result.insert(hex); result.insert(hex);
return result; return result;
} }
std::set<BattleHex> BattleFieldController::getMovementRangeForHoveredStack() BattleHexArray BattleFieldController::getMovementRangeForHoveredStack()
{ {
std::set<BattleHex> result; BattleHexArray result;
if (!owner.stacksController->getActiveStack()) if (!owner.stacksController->getActiveStack())
return result; return result;
@ -344,16 +344,16 @@ std::set<BattleHex> BattleFieldController::getMovementRangeForHoveredStack()
auto hoveredStack = getHoveredStack(); auto hoveredStack = getHoveredStack();
if(hoveredStack) if(hoveredStack)
{ {
std::vector<BattleHex> v = owner.getBattle()->battleGetAvailableHexes(hoveredStack, true, true, nullptr); BattleHexArray v = owner.getBattle()->battleGetAvailableHexes(hoveredStack, true, true, nullptr);
for(BattleHex hex : v) for(BattleHex hex : v)
result.insert(hex); result.insert(hex);
} }
return result; return result;
} }
std::set<BattleHex> BattleFieldController::getHighlightedHexesForSpellRange() BattleHexArray BattleFieldController::getHighlightedHexesForSpellRange()
{ {
std::set<BattleHex> result; BattleHexArray result;
auto hoveredHex = getHoveredHex(); auto hoveredHex = getHoveredHex();
const spells::Caster *caster = nullptr; const spells::Caster *caster = nullptr;
@ -378,7 +378,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForSpellRange()
return result; return result;
} }
std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget() BattleHexArray BattleFieldController::getHighlightedHexesForMovementTarget()
{ {
const CStack * stack = owner.stacksController->getActiveStack(); const CStack * stack = owner.stacksController->getActiveStack();
auto hoveredHex = getHoveredHex(); auto hoveredHex = getHoveredHex();
@ -386,7 +386,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
if(!stack) if(!stack)
return {}; return {};
std::vector<BattleHex> availableHexes = owner.getBattle()->battleGetAvailableHexes(stack, false, false, nullptr); BattleHexArray availableHexes = owner.getBattle()->battleGetAvailableHexes(stack, false, false, nullptr);
auto hoveredStack = owner.getBattle()->battleGetStackByPos(hoveredHex, true); auto hoveredStack = owner.getBattle()->battleGetStackByPos(hoveredHex, true);
if(owner.getBattle()->battleCanAttack(stack, hoveredStack, hoveredHex)) if(owner.getBattle()->battleCanAttack(stack, hoveredStack, hoveredHex))
@ -402,7 +402,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
} }
} }
if(vstd::contains(availableHexes, hoveredHex)) if(availableHexes.contains(hoveredHex))
{ {
if(stack->doubleWide()) if(stack->doubleWide())
return {hoveredHex, stack->occupiedHex(hoveredHex)}; return {hoveredHex, stack->occupiedHex(hoveredHex)};
@ -412,7 +412,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
if(stack->doubleWide()) if(stack->doubleWide())
{ {
for(auto const & hex : availableHexes) for(auto hex : availableHexes)
{ {
if(stack->occupiedHex(hex) == hoveredHex) if(stack->occupiedHex(hex) == hoveredHex)
return {hoveredHex, hex}; return {hoveredHex, hex};
@ -424,9 +424,9 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
// Range limit highlight helpers // Range limit highlight helpers
std::vector<BattleHex> BattleFieldController::getRangeHexes(BattleHex sourceHex, uint8_t distance) BattleHexArray BattleFieldController::getRangeHexes(BattleHex sourceHex, uint8_t distance)
{ {
std::vector<BattleHex> rangeHexes; BattleHexArray rangeHexes;
if (!settings["battle"]["rangeLimitHighlightOnHover"].Bool() && !GH.isKeyboardShiftDown()) if (!settings["battle"]["rangeLimitHighlightOnHover"].Bool() && !GH.isKeyboardShiftDown())
return rangeHexes; return rangeHexes;
@ -436,27 +436,27 @@ std::vector<BattleHex> BattleFieldController::getRangeHexes(BattleHex sourceHex,
{ {
BattleHex hex(i); BattleHex hex(i);
if(hex.isAvailable() && BattleHex::getDistance(sourceHex, hex) <= distance) if(hex.isAvailable() && BattleHex::getDistance(sourceHex, hex) <= distance)
rangeHexes.push_back(hex); rangeHexes.insert(hex);
} }
return rangeHexes; return rangeHexes;
} }
std::vector<BattleHex> BattleFieldController::getRangeLimitHexes(BattleHex hoveredHex, std::vector<BattleHex> rangeHexes, uint8_t distanceToLimit) BattleHexArray BattleFieldController::getRangeLimitHexes(BattleHex hoveredHex, const BattleHexArray & rangeHexes, uint8_t distanceToLimit)
{ {
std::vector<BattleHex> rangeLimitHexes; BattleHexArray rangeLimitHexes;
// from range hexes get only the ones at the limit // from range hexes get only the ones at the limit
for(auto & hex : rangeHexes) for(auto & hex : rangeHexes)
{ {
if(BattleHex::getDistance(hoveredHex, hex) == distanceToLimit) if(BattleHex::getDistance(hoveredHex, hex) == distanceToLimit)
rangeLimitHexes.push_back(hex); rangeLimitHexes.insert(hex);
} }
return rangeLimitHexes; return rangeLimitHexes;
} }
bool BattleFieldController::IsHexInRangeLimit(BattleHex hex, std::vector<BattleHex> & rangeLimitHexes, int * hexIndexInRangeLimit) bool BattleFieldController::IsHexInRangeLimit(BattleHex hex, const BattleHexArray & rangeLimitHexes, int * hexIndexInRangeLimit)
{ {
bool hexInRangeLimit = false; bool hexInRangeLimit = false;
@ -470,18 +470,19 @@ bool BattleFieldController::IsHexInRangeLimit(BattleHex hex, std::vector<BattleH
return hexInRangeLimit; return hexInRangeLimit;
} }
std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeighbourDirectionsForLimitHexes(std::vector<BattleHex> wholeRangeHexes, std::vector<BattleHex> rangeLimitHexes) std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeighbourDirectionsForLimitHexes(
const BattleHexArray & wholeRangeHexes, const BattleHexArray & rangeLimitHexes)
{ {
std::vector<std::vector<BattleHex::EDir>> output; std::vector<std::vector<BattleHex::EDir>> output;
if(wholeRangeHexes.empty()) if(wholeRangeHexes.empty())
return output; return output;
for(auto & hex : rangeLimitHexes) for(auto hex : rangeLimitHexes)
{ {
// get all neighbours and their directions // get all neighbours and their directions
auto neighbouringTiles = hex.allNeighbouringTiles(); const BattleHexArray & neighbouringTiles = hex.getAllNeighbouringTiles();
std::vector<BattleHex::EDir> outsideNeighbourDirections; std::vector<BattleHex::EDir> outsideNeighbourDirections;
@ -491,9 +492,7 @@ std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeigh
if(!neighbouringTiles[direction].isAvailable()) if(!neighbouringTiles[direction].isAvailable())
continue; continue;
auto it = std::find(wholeRangeHexes.begin(), wholeRangeHexes.end(), neighbouringTiles[direction]); if(!wholeRangeHexes.contains(neighbouringTiles[direction]))
if(it == wholeRangeHexes.end())
outsideNeighbourDirections.push_back(BattleHex::EDir(direction)); // push direction outsideNeighbourDirections.push_back(BattleHex::EDir(direction)); // push direction
} }
@ -525,9 +524,9 @@ std::vector<std::shared_ptr<IImage>> BattleFieldController::calculateRangeLimitH
return output; return output;
} }
void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, std::vector<BattleHex> & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights) void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, BattleHexArray & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights)
{ {
std::vector<BattleHex> rangeHexes = getRangeHexes(hoveredHex, distance); BattleHexArray rangeHexes = getRangeHexes(hoveredHex, distance);
rangeLimitHexes = getRangeLimitHexes(hoveredHex, rangeHexes, distance); rangeLimitHexes = getRangeLimitHexes(hoveredHex, rangeHexes, distance);
std::vector<std::vector<BattleHex::EDir>> rangeLimitNeighbourDirections = getOutsideNeighbourDirectionsForLimitHexes(rangeHexes, rangeLimitHexes); std::vector<std::vector<BattleHex::EDir>> rangeLimitNeighbourDirections = getOutsideNeighbourDirectionsForLimitHexes(rangeHexes, rangeLimitHexes);
rangeLimitHexesHighlights = calculateRangeLimitHighlightImages(rangeLimitNeighbourDirections, rangeLimitImages); rangeLimitHexesHighlights = calculateRangeLimitHighlightImages(rangeLimitNeighbourDirections, rangeLimitImages);
@ -535,18 +534,18 @@ void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distan
void BattleFieldController::showHighlightedHexes(Canvas & canvas) void BattleFieldController::showHighlightedHexes(Canvas & canvas)
{ {
std::vector<BattleHex> rangedFullDamageLimitHexes; BattleHexArray rangedFullDamageLimitHexes;
std::vector<BattleHex> shootingRangeLimitHexes; BattleHexArray shootingRangeLimitHexes;
std::vector<std::shared_ptr<IImage>> rangedFullDamageLimitHexesHighlights; std::vector<std::shared_ptr<IImage>> rangedFullDamageLimitHexesHighlights;
std::vector<std::shared_ptr<IImage>> shootingRangeLimitHexesHighlights; std::vector<std::shared_ptr<IImage>> shootingRangeLimitHexesHighlights;
std::set<BattleHex> hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack(); BattleHexArray hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack();
std::set<BattleHex> hoveredSpellHexes = getHighlightedHexesForSpellRange(); BattleHexArray hoveredSpellHexes = getHighlightedHexesForSpellRange();
std::set<BattleHex> hoveredMoveHexes = getHighlightedHexesForMovementTarget(); BattleHexArray hoveredMoveHexes = getHighlightedHexesForMovementTarget();
BattleHex hoveredHex = getHoveredHex(); BattleHex hoveredHex = getHoveredHex();
std::set<BattleHex> hoveredMouseHex = hoveredHex.isValid() ? std::set<BattleHex>({ hoveredHex }) : std::set<BattleHex>(); BattleHexArray hoveredMouseHex = hoveredHex.isValid() ? BattleHexArray({ hoveredHex }) : BattleHexArray();
const CStack * hoveredStack = getHoveredStack(); const CStack * hoveredStack = getHoveredStack();
if(!hoveredStack && hoveredHex == BattleHex::INVALID) if(!hoveredStack && hoveredHex == BattleHex::INVALID)
@ -573,8 +572,8 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
for(int hex = 0; hex < GameConstants::BFIELD_SIZE; ++hex) for(int hex = 0; hex < GameConstants::BFIELD_SIZE; ++hex)
{ {
bool stackMovement = hoveredStackMovementRangeHexes.count(hex); bool stackMovement = hoveredStackMovementRangeHexes.contains(hex);
bool mouse = hoveredMouseHexes.count(hex); bool mouse = hoveredMouseHexes.contains(hex);
// calculate if hex is Ranged Full Damage Limit and its position in highlight array // calculate if hex is Ranged Full Damage Limit and its position in highlight array
int hexIndexInRangedFullDamageLimit = 0; int hexIndexInRangedFullDamageLimit = 0;
@ -679,7 +678,7 @@ BattleHex BattleFieldController::getHexAtPosition(Point hoverPos)
BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber) BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber)
{ {
const bool doubleWide = owner.stacksController->getActiveStack()->doubleWide(); const bool doubleWide = owner.stacksController->getActiveStack()->doubleWide();
auto neighbours = myNumber.allNeighbouringTiles(); const BattleHexArray & neighbours = myNumber.getAllNeighbouringTiles();
// 0 1 // 0 1
// 5 x 2 // 5 x 2
// 4 3 // 4 3
@ -696,18 +695,18 @@ BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber)
// | - - | - - | - - | - o o | o o - | - - | - - | o o // | - - | - - | - - | - o o | o o - | - - | - - | o o
for (size_t i : { 1, 2, 3}) for (size_t i : { 1, 2, 3})
attackAvailability[i] = vstd::contains(occupiableHexes, neighbours[i]) && vstd::contains(occupiableHexes, neighbours[i].cloneInDirection(BattleHex::RIGHT, false)); attackAvailability[i] = occupiableHexes.contains(neighbours[i]) && occupiableHexes.contains(neighbours[i].cloneInDirection(BattleHex::RIGHT, false));
for (size_t i : { 4, 5, 0}) for (size_t i : { 4, 5, 0})
attackAvailability[i] = vstd::contains(occupiableHexes, neighbours[i]) && vstd::contains(occupiableHexes, neighbours[i].cloneInDirection(BattleHex::LEFT, false)); attackAvailability[i] = occupiableHexes.contains(neighbours[i]) && occupiableHexes.contains(neighbours[i].cloneInDirection(BattleHex::LEFT, false));
attackAvailability[6] = vstd::contains(occupiableHexes, neighbours[0]) && vstd::contains(occupiableHexes, neighbours[1]); attackAvailability[6] = occupiableHexes.contains(neighbours[0]) && occupiableHexes.contains(neighbours[1]);
attackAvailability[7] = vstd::contains(occupiableHexes, neighbours[3]) && vstd::contains(occupiableHexes, neighbours[4]); attackAvailability[7] = occupiableHexes.contains(neighbours[3]) && occupiableHexes.contains(neighbours[4]);
} }
else else
{ {
for (size_t i = 0; i < 6; ++i) for (size_t i = 0; i < 6; ++i)
attackAvailability[i] = vstd::contains(occupiableHexes, neighbours[i]); attackAvailability[i] = occupiableHexes.contains(neighbours[i]);
attackAvailability[6] = false; attackAvailability[6] = false;
attackAvailability[7] = false; attackAvailability[7] = false;
@ -819,6 +818,9 @@ BattleHex BattleFieldController::fromWhichHexAttack(BattleHex attackTarget)
bool BattleFieldController::isTileAttackable(const BattleHex & number) const bool BattleFieldController::isTileAttackable(const BattleHex & number) const
{ {
if(!number.isValid())
return false;
for (auto & elem : occupiableHexes) for (auto & elem : occupiableHexes)
{ {
if (BattleHex::mutualPosition(elem, number) != -1 || elem == number) if (BattleHex::mutualPosition(elem, number) != -1 || elem == number)
@ -837,7 +839,7 @@ void BattleFieldController::updateAccessibleHexes()
bool BattleFieldController::stackCountOutsideHex(const BattleHex & number) const bool BattleFieldController::stackCountOutsideHex(const BattleHex & number) const
{ {
return stackCountOutsideHexes[number]; return stackCountOutsideHexes[number.toInt()];
} }
void BattleFieldController::showAll(Canvas & to) void BattleFieldController::showAll(Canvas & to)

View File

@ -9,7 +9,7 @@
*/ */
#pragma once #pragma once
#include "../../lib/battle/BattleHex.h" #include "../../lib/battle/BattleHexArray.h"
#include "../../lib/Point.h" #include "../../lib/Point.h"
#include "../gui/CIntObject.h" #include "../gui/CIntObject.h"
@ -50,39 +50,39 @@ class BattleFieldController : public CIntObject
BattleHex hoveredHex; BattleHex hoveredHex;
/// hexes to which currently active stack can move /// hexes to which currently active stack can move
std::vector<BattleHex> occupiableHexes; BattleHexArray occupiableHexes;
/// hexes that when in front of a unit cause it's amount box to move back /// hexes that when in front of a unit cause it's amount box to move back
std::array<bool, GameConstants::BFIELD_SIZE> stackCountOutsideHexes; std::array<bool, GameConstants::BFIELD_SIZE> stackCountOutsideHexes;
void showHighlightedHex(Canvas & to, std::shared_ptr<IImage> highlight, BattleHex hex, bool darkBorder); void showHighlightedHex(Canvas & to, std::shared_ptr<IImage> highlight, BattleHex hex, bool darkBorder);
std::set<BattleHex> getHighlightedHexesForActiveStack(); BattleHexArray getHighlightedHexesForActiveStack();
std::set<BattleHex> getMovementRangeForHoveredStack(); BattleHexArray getMovementRangeForHoveredStack();
std::set<BattleHex> getHighlightedHexesForSpellRange(); BattleHexArray getHighlightedHexesForSpellRange();
std::set<BattleHex> getHighlightedHexesForMovementTarget(); BattleHexArray getHighlightedHexesForMovementTarget();
// Range limit highlight helpers // Range limit highlight helpers
/// get all hexes within a certain distance of given hex /// get all hexes within a certain distance of given hex
std::vector<BattleHex> getRangeHexes(BattleHex sourceHex, uint8_t distance); BattleHexArray getRangeHexes(BattleHex sourceHex, uint8_t distance);
/// get only hexes at the limit of a range /// get only hexes at the limit of a range
std::vector<BattleHex> getRangeLimitHexes(BattleHex hoveredHex, std::vector<BattleHex> hexRange, uint8_t distanceToLimit); BattleHexArray getRangeLimitHexes(BattleHex hoveredHex, const BattleHexArray & hexRange, uint8_t distanceToLimit);
/// calculate if a hex is in range limit and return its index in range /// calculate if a hex is in range limit and return its index in range
bool IsHexInRangeLimit(BattleHex hex, std::vector<BattleHex> & rangeLimitHexes, int * hexIndexInRangeLimit); bool IsHexInRangeLimit(BattleHex hex, const BattleHexArray & rangeLimitHexes, int * hexIndexInRangeLimit);
/// get an array that has for each hex in range, an array with all directions where an outside neighbour hex exists /// get an array that has for each hex in range, an array with all directions where an outside neighbour hex exists
std::vector<std::vector<BattleHex::EDir>> getOutsideNeighbourDirectionsForLimitHexes(std::vector<BattleHex> rangeHexes, std::vector<BattleHex> rangeLimitHexes); std::vector<std::vector<BattleHex::EDir>> getOutsideNeighbourDirectionsForLimitHexes(const BattleHexArray & rangeHexes, const BattleHexArray & rangeLimitHexes);
/// calculates what image to use as range limit, depending on the direction of neighbors /// calculates what image to use as range limit, depending on the direction of neighbours
/// a mask is used internally to mark the directions of all neighbours /// a mask is used internally to mark the directions of all neighbours
/// based on this mask the corresponding image is selected /// based on this mask the corresponding image is selected
std::vector<std::shared_ptr<IImage>> calculateRangeLimitHighlightImages(std::vector<std::vector<BattleHex::EDir>> hexesNeighbourDirections, std::shared_ptr<CAnimation> limitImages); std::vector<std::shared_ptr<IImage>> calculateRangeLimitHighlightImages(std::vector<std::vector<BattleHex::EDir>> hexesNeighbourDirections, std::shared_ptr<CAnimation> limitImages);
/// calculates all hexes for a range limit and what images to be shown as highlight for each of the hexes /// calculates all hexes for a range limit and what images to be shown as highlight for each of the hexes
void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, std::vector<BattleHex> & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights); void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, BattleHexArray & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights);
void showBackground(Canvas & canvas); void showBackground(Canvas & canvas);
void showBackgroundImage(Canvas & canvas); void showBackgroundImage(Canvas & canvas);

View File

@ -216,7 +216,7 @@ void BattleInterface::stackActivated(const CStack *stack)
stacksController->stackActivated(stack); stacksController->stackActivated(stack);
} }
void BattleInterface::stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance, bool teleport) void BattleInterface::stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance, bool teleport)
{ {
if (teleport) if (teleport)
stacksController->stackTeleported(stack, destHex, distance); stacksController->stackTeleported(stack, destHex, distance);

View File

@ -10,6 +10,7 @@
#pragma once #pragma once
#include "BattleConstants.h" #include "BattleConstants.h"
#include "../lib/battle/BattleHex.h"
#include "../gui/CIntObject.h" #include "../gui/CIntObject.h"
#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation #include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
#include "../ConditionalWait.h" #include "../ConditionalWait.h"
@ -27,7 +28,6 @@ class BattleAction;
class CGTownInstance; class CGTownInstance;
struct CatapultAttack; struct CatapultAttack;
struct BattleTriggerEffect; struct BattleTriggerEffect;
struct BattleHex;
struct InfoAboutHero; struct InfoAboutHero;
class ObstacleChanges; class ObstacleChanges;
class CPlayerBattleCallback; class CPlayerBattleCallback;
@ -202,7 +202,7 @@ public:
void stackAdded(const CStack * stack); //new stack appeared on battlefield void stackAdded(const CStack * stack); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
void stackActivated(const CStack *stack); //active stack has been changed void stackActivated(const CStack *stack); //active stack has been changed
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance, bool teleport); //stack with id number moved to destHex void stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance, bool teleport); //stack with id number moved to destHex
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
void stackAttacking(const StackAttackInfo & attackInfo); //called when stack with id ID is attacking something on hex dest void stackAttacking(const StackAttackInfo & attackInfo); //called when stack with id ID is attacking something on hex dest
void newRoundFirst(); void newRoundFirst();

View File

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

View File

@ -185,7 +185,7 @@ BattleSiegeController::BattleSiegeController(BattleInterface & owner, const CGTo
const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) const const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) const
{ {
switch (position) switch (position.toInt())
{ {
case BattleHex::CASTLE_CENTRAL_TOWER: case BattleHex::CASTLE_CENTRAL_TOWER:
return town->fortificationsLevel().citadelShooter.toCreature(); return town->fortificationsLevel().citadelShooter.toCreature();
@ -195,14 +195,14 @@ const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) co
return town->fortificationsLevel().lowerTowerShooter.toCreature(); 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.toInt()));
} }
Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const
{ {
// Turret positions are read out of the config/wall_pos.txt // Turret positions are read out of the config/wall_pos.txt
int posID = 0; int posID = 0;
switch (position) switch (position.toInt())
{ {
case BattleHex::CASTLE_CENTRAL_TOWER: // keep creature case BattleHex::CASTLE_CENTRAL_TOWER: // keep creature
posID = EWallVisual::CREATURE_KEEP; posID = EWallVisual::CREATURE_KEEP;

View File

@ -491,7 +491,7 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
owner.waitForAnimations(); owner.waitForAnimations();
} }
void BattleStacksController::stackTeleported(const CStack *stack, std::vector<BattleHex> destHex, int distance) void BattleStacksController::stackTeleported(const CStack *stack, const BattleHexArray & destHex, int distance)
{ {
assert(destHex.size() > 0); assert(destHex.size() > 0);
//owner.checkForAnimations(); // NOTE: at this point spellcast animations were added, but not executed //owner.checkForAnimations(); // NOTE: at this point spellcast animations were added, but not executed
@ -508,7 +508,7 @@ void BattleStacksController::stackTeleported(const CStack *stack, std::vector<Ba
// animations will be executed by spell // animations will be executed by spell
} }
void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance) void BattleStacksController::stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance)
{ {
assert(destHex.size() > 0); assert(destHex.size() > 0);
owner.checkForAnimations(); owner.checkForAnimations();

View File

@ -13,7 +13,8 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex; class BattleHex;
class BattleHexArray;
class BattleAction; class BattleAction;
class CStack; class CStack;
class CSpell; class CSpell;
@ -109,8 +110,8 @@ public:
void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
void stackActivated(const CStack *stack); //active stack has been changed void stackActivated(const CStack *stack); //active stack has been changed
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex void stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance); //stack with id number moved to destHex
void stackTeleported(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex void stackTeleported(const CStack *stack, const BattleHexArray & destHex, int distance); //stack with id number moved to destHex
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
void stackAttacking(const StackAttackInfo & info); //called when stack with id ID is attacking something on hex dest void stackAttacking(const StackAttackInfo & info); //called when stack with id ID is attacking something on hex dest

View File

@ -88,14 +88,15 @@ namespace RandomGeneratorUtil
return container.size() - 1; return container.size() - 1;
} }
template<typename T> template<typename Container>
void randomShuffle(std::vector<T> & container, vstd::RNG & rand) void randomShuffle(Container & container, vstd::RNG & rand)
{ {
int64_t n = (container.end() - container.begin()); int64_t n = std::distance(container.begin(), container.end());
for(int64_t i = n-1; i>0; --i) for(int64_t i = n - 1; i > 0; --i)
{ {
std::swap(container.begin()[i],container.begin()[rand.nextInt64(0, i)]); auto randIndex = rand.nextInt64(0, i);
std::swap(*(container.begin() + i), *(container.begin() + randIndex));
} }
} }
} }

View File

@ -38,7 +38,7 @@ std::shared_ptr<BattleFieldInfo> BattleFieldHandler::loadFromJson(const std::str
info->isSpecial = json["isSpecial"].Bool(); info->isSpecial = json["isSpecial"].Bool();
for(auto node : json["impassableHexes"].Vector()) for(auto node : json["impassableHexes"].Vector())
info->impassableHexes.emplace_back(node.Integer()); info->impassableHexes.insert(node.Integer());
info->openingSoundFilename = AudioPath::fromJson(json["openingSound"]); info->openingSoundFilename = AudioPath::fromJson(json["openingSound"]);
info->musicFilename = AudioPath::fromJson(json["music"]); info->musicFilename = AudioPath::fromJson(json["music"]);

View File

@ -14,7 +14,7 @@
#include "bonuses/Bonus.h" #include "bonuses/Bonus.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "IHandlerBase.h" #include "IHandlerBase.h"
#include "battle/BattleHex.h" #include "battle/BattleHexArray.h"
#include "filesystem/ResourcePath.h" #include "filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -31,7 +31,7 @@ public:
std::string identifier; std::string identifier;
std::string icon; std::string icon;
si32 iconIndex; si32 iconIndex;
std::vector<BattleHex> impassableHexes; BattleHexArray impassableHexes;
AudioPath openingSoundFilename; AudioPath openingSoundFilename;
AudioPath musicFilename; AudioPath musicFilename;

View File

@ -204,7 +204,7 @@ void CAdventureAI::battleObstaclesChanged(const BattleID & battleID, const std::
battleAI->battleObstaclesChanged(battleID, obstacles); battleAI->battleObstaclesChanged(battleID, obstacles);
} }
void CAdventureAI::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) void CAdventureAI::battleStackMoved(const BattleID & battleID, const CStack * stack, const BattleHexArray & dest, int distance, bool teleport)
{ {
battleAI->battleStackMoved(battleID, stack, dest, distance, teleport); battleAI->battleStackMoved(battleID, stack, dest, distance, teleport);
} }

View File

@ -151,7 +151,7 @@ public:
void actionFinished(const BattleID & battleID, const BattleAction &action) override; void actionFinished(const BattleID & battleID, const BattleAction &action) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override; void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;
void battleObstaclesChanged(const BattleID & battleID, const std::vector<ObstacleChanges> & obstacles) override; void battleObstaclesChanged(const BattleID & battleID, const std::vector<ObstacleChanges> & obstacles) override;
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override; void battleStackMoved(const BattleID & battleID, const CStack * stack, const BattleHexArray & dest, int distance, bool teleport) override;
void battleAttack(const BattleID & battleID, const BattleAttack *ba) override; void battleAttack(const BattleID & battleID, const BattleAttack *ba) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override; void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override; void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override;

View File

@ -47,6 +47,7 @@ set(lib_MAIN_SRCS
battle/BattleAction.cpp battle/BattleAction.cpp
battle/BattleAttackInfo.cpp battle/BattleAttackInfo.cpp
battle/BattleHex.cpp battle/BattleHex.cpp
battle/BattleHexArray.cpp
battle/BattleInfo.cpp battle/BattleInfo.cpp
battle/BattleLayout.cpp battle/BattleLayout.cpp
battle/BattleProxy.cpp battle/BattleProxy.cpp
@ -413,6 +414,7 @@ set(lib_MAIN_HEADERS
battle/BattleAction.h battle/BattleAction.h
battle/BattleAttackInfo.h battle/BattleAttackInfo.h
battle/BattleHex.h battle/BattleHex.h
battle/BattleHexArray.h
battle/BattleInfo.h battle/BattleInfo.h
battle/BattleLayout.h battle/BattleLayout.h
battle/BattleSide.h battle/BattleSide.h

View File

@ -242,25 +242,25 @@ void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, const
bsa.newState.operation = UnitChanges::EOperation::RESET_STATE; bsa.newState.operation = UnitChanges::EOperation::RESET_STATE;
} }
std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos, BattleHex defenderPos) BattleHexArray CStack::meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos, BattleHex defenderPos)
{ {
int mask = 0; int mask = 0;
std::vector<BattleHex> res; BattleHexArray res;
if (!attackerPos.isValid()) if (!attackerPos.isValid())
attackerPos = attacker->getPosition(); attackerPos = attacker->getPosition();
if (!defenderPos.isValid()) if (!defenderPos.isValid())
defenderPos = defender->getPosition(); defenderPos = defender->getPosition();
BattleHex otherAttackerPos = attackerPos + (attacker->unitSide() == BattleSide::ATTACKER ? -1 : 1); BattleHex otherAttackerPos = attackerPos.toInt() + (attacker->unitSide() == BattleSide::ATTACKER ? -1 : 1);
BattleHex otherDefenderPos = defenderPos + (defender->unitSide() == BattleSide::ATTACKER ? -1 : 1); BattleHex otherDefenderPos = defenderPos.toInt() + (defender->unitSide() == BattleSide::ATTACKER ? -1 : 1);
if(BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front if(BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front
{ {
if((mask & 1) == 0) if((mask & 1) == 0)
{ {
mask |= 1; mask |= 1;
res.push_back(defenderPos); res.insert(defenderPos);
} }
} }
if (attacker->doubleWide() //back <=> front if (attacker->doubleWide() //back <=> front
@ -269,7 +269,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 1) == 0) if((mask & 1) == 0)
{ {
mask |= 1; mask |= 1;
res.push_back(defenderPos); res.insert(defenderPos);
} }
} }
if (defender->doubleWide()//front <=> back if (defender->doubleWide()//front <=> back
@ -278,7 +278,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 2) == 0) if((mask & 2) == 0)
{ {
mask |= 2; mask |= 2;
res.push_back(otherDefenderPos); res.insert(otherDefenderPos);
} }
} }
if (defender->doubleWide() && attacker->doubleWide()//back <=> back if (defender->doubleWide() && attacker->doubleWide()//back <=> back
@ -287,7 +287,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 2) == 0) if((mask & 2) == 0)
{ {
mask |= 2; mask |= 2;
res.push_back(otherDefenderPos); res.insert(otherDefenderPos);
} }
} }

View File

@ -60,7 +60,7 @@ public:
std::vector<SpellID> activeSpells() const; //returns vector of active spell IDs sorted by time of cast std::vector<SpellID> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
const CGHeroInstance * getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise const CGHeroInstance * getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise
static std::vector<BattleHex> meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID); static BattleHexArray meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
static bool isMeleeAttackPossible(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID); static bool isMeleeAttackPossible(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
BattleHex::EDir destShiftDir() const; BattleHex::EDir destShiftDir() const;
@ -146,4 +146,4 @@ private:
const BattleInfo * battle; //do not serialize const BattleInfo * battle; //do not serialize
}; };
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -10,7 +10,7 @@
#pragma once #pragma once
#include "networkPacks/EInfoWindowMode.h" #include "networkPacks/EInfoWindowMode.h"
#include "battle/BattleHex.h" #include "battle/BattleHexArray.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "int3.h" #include "int3.h"
@ -63,7 +63,7 @@ public:
virtual void battleNewRoundFirst(const BattleID & battleID){}; //called at the beginning of each turn before changes are applied; virtual void battleNewRoundFirst(const BattleID & battleID){}; //called at the beginning of each turn before changes are applied;
virtual void battleNewRound(const BattleID & battleID){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn virtual void battleNewRound(const BattleID & battleID){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
virtual void battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines){}; virtual void battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines){};
virtual void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport){}; virtual void battleStackMoved(const BattleID & battleID, const CStack * stack, const BattleHexArray & dest, int distance, bool teleport){};
virtual void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc){}; virtual void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc){};
virtual void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse){};//called when a specific effect is set to stacks virtual void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse){};//called when a specific effect is set to stacks
virtual void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte){}; //called for various one-shot effects virtual void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte){}; //called for various one-shot effects

View File

@ -55,26 +55,25 @@ Obstacle ObstacleInfo::getId() const
return obstacle; return obstacle;
} }
std::vector<BattleHex> ObstacleInfo::getBlocked(BattleHex hex) const BattleHexArray ObstacleInfo::getBlocked(BattleHex hex) const
{ {
std::vector<BattleHex> ret;
if(isAbsoluteObstacle) if(isAbsoluteObstacle)
{ {
assert(!hex.isValid()); assert(!hex.isValid());
range::copy(blockedTiles, std::back_inserter(ret)); return BattleHexArray(blockedTiles);
return ret;
} }
BattleHexArray ret;
for(int offset : blockedTiles) for(int offset : blockedTiles)
{ {
BattleHex toBlock = hex + offset; BattleHex toBlock = hex.toInt() + offset;
if((hex.getY() & 1) && !(toBlock.getY() & 1)) if((hex.getY() & 1) && !(toBlock.getY() & 1))
toBlock += BattleHex::LEFT; toBlock += BattleHex::LEFT;
if(!toBlock.isValid()) if(!toBlock.isValid())
logGlobal->error("Misplaced obstacle!"); logGlobal->error("Misplaced obstacle!");
else else
ret.push_back(toBlock); ret.insert(toBlock);
} }
return ret; return ret;

View File

@ -13,7 +13,7 @@
#include <vcmi/Entity.h> #include <vcmi/Entity.h>
#include "GameConstants.h" #include "GameConstants.h"
#include "IHandlerBase.h" #include "IHandlerBase.h"
#include "battle/BattleHex.h" #include "battle/BattleHexArray.h"
#include "filesystem/ResourcePath.h" #include "filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -54,7 +54,7 @@ public:
void registerIcons(const IconRegistar & cb) const override; void registerIcons(const IconRegistar & cb) const override;
Obstacle getId() const override; Obstacle getId() const override;
std::vector<BattleHex> getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex' BattleHexArray getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex'
bool isAppropriate(const TerrainId terrainType, const BattleField & specialBattlefield) const; bool isAppropriate(const TerrainId terrainType, const BattleField & specialBattlefield) const;
}; };

View File

@ -18,13 +18,14 @@ VCMI_LIB_NAMESPACE_BEGIN
bool AccessibilityInfo::tileAccessibleWithGate(BattleHex tile, BattleSide side) const bool AccessibilityInfo::tileAccessibleWithGate(BattleHex tile, BattleSide side) const
{ {
//at(otherHex) != EAccessibility::ACCESSIBLE && (at(otherHex) != EAccessibility::GATE || side != BattleSide::DEFENDER) //at(otherHex) != EAccessibility::ACCESSIBLE && (at(otherHex) != EAccessibility::GATE || side != BattleSide::DEFENDER)
auto accessibility = at(tile); const auto & accessibility = at(tile.toInt());
if(accessibility == EAccessibility::ALIVE_STACK) if(accessibility == EAccessibility::ALIVE_STACK)
{ {
auto destructible = destructibleEnemyTurns.find(tile); if(!destructibleEnemyTurns)
return false;
return destructible != destructibleEnemyTurns.end(); return destructibleEnemyTurns->at(tile.toInt()) >= 0;
} }
if(accessibility != EAccessibility::ACCESSIBLE) if(accessibility != EAccessibility::ACCESSIBLE)

View File

@ -27,15 +27,16 @@ enum class EAccessibility
DESTRUCTIBLE_WALL, DESTRUCTIBLE_WALL,
GATE, //sieges -> gate opens only for defender stacks GATE, //sieges -> gate opens only for defender stacks
UNAVAILABLE, //indestructible wall parts, special battlefields (like boat-to-boat) UNAVAILABLE, //indestructible wall parts, special battlefields (like boat-to-boat)
SIDE_COLUMN //used for first and last columns of hexes that are unavailable but wat machines can stand there SIDE_COLUMN //used for first and last columns of hexes that are unavailable but war machines can stand there
}; };
using TAccessibilityArray = std::array<EAccessibility, GameConstants::BFIELD_SIZE>; using TAccessibilityArray = std::array<EAccessibility, GameConstants::BFIELD_SIZE>;
using TBattlefieldTurnsArray = std::array<int8_t, GameConstants::BFIELD_SIZE>;
struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray
{ {
std::map<BattleHex, ui8> destructibleEnemyTurns; std::shared_ptr<const TBattlefieldTurnsArray> destructibleEnemyTurns; //used only as a view for destructibleEnemyTurns from ReachabilityInfo::Parameters
public: public:
bool accessible(BattleHex tile, const battle::Unit * stack) const; //checks for both tiles if stack is double wide bool accessible(BattleHex tile, const battle::Unit * stack) const; //checks for both tiles if stack is double wide

View File

@ -9,237 +9,63 @@
*/ */
#include "StdInc.h" #include "StdInc.h"
#include "BattleHex.h" #include "BattleHex.h"
#include "BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
BattleHex::BattleHex() : hex(INVALID) {} BattleHex BattleHex::getClosestTile(BattleSide side, BattleHex initialPos, const BattleHexArray & hexes)
BattleHex::BattleHex(si16 _hex) : hex(_hex) {}
BattleHex::BattleHex(si16 x, si16 y)
{ {
setXY(x, y); if(hexes.empty())
} return BattleHex();
BattleHex::BattleHex(std::pair<si16, si16> xy)
{
setXY(xy);
}
BattleHex::operator si16() const
{
return hex;
}
bool BattleHex::isValid() const
{
return hex >= 0 && hex < GameConstants::BFIELD_SIZE;
}
bool BattleHex::isAvailable() const
{
return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH-1;
}
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)
{
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 BattleHex::setXY(std::pair<si16, si16> xy)
{
setXY(xy.first, xy.second);
}
si16 BattleHex::getX() const
{
return hex % GameConstants::BFIELD_WIDTH;
}
si16 BattleHex::getY() const
{
return hex / GameConstants::BFIELD_WIDTH;
}
std::pair<si16, si16> BattleHex::getXY() const
{
return std::make_pair(getX(), getY());
}
BattleHex& BattleHex::moveInDirection(EDir dir, bool hasToBeValid)
{
si16 x = getX();
si16 y = getY();
switch(dir)
{
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 &BattleHex::operator+=(BattleHex::EDir dir)
{
return moveInDirection(dir);
}
BattleHex BattleHex::cloneInDirection(BattleHex::EDir dir, bool hasToBeValid) const
{
BattleHex result(hex);
result.moveInDirection(dir, hasToBeValid);
return result;
}
BattleHex BattleHex::operator+(BattleHex::EDir dir) const
{
return cloneInDirection(dir);
}
std::vector<BattleHex> BattleHex::neighbouringTiles() const
{
std::vector<BattleHex> ret;
ret.reserve(6);
for(auto dir : hexagonalDirections())
checkAndPush(cloneInDirection(dir, false), ret);
return ret;
}
std::vector<BattleHex> BattleHex::allNeighbouringTiles() const
{
std::vector<BattleHex> ret;
ret.resize(6);
for(auto dir : hexagonalDirections())
ret[dir] = cloneInDirection(dir, false);
return ret;
}
BattleHex::EDir BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2)
{
for(auto dir : hexagonalDirections())
if(hex2 == hex1.cloneInDirection(dir, false))
return dir;
return NONE;
}
uint8_t BattleHex::getDistance(BattleHex hex1, BattleHex hex2)
{
int y1 = hex1.getY();
int y2 = hex2.getY();
// FIXME: why there was * 0.5 instead of / 2?
int x1 = static_cast<int>(hex1.getX() + y1 / 2);
int x2 = static_cast<int>(hex2.getX() + y2 / 2);
int xDst = x2 - x1;
int yDst = y2 - y1;
if ((xDst >= 0 && yDst >= 0) || (xDst < 0 && yDst < 0))
return std::max(std::abs(xDst), std::abs(yDst));
return std::abs(xDst) + std::abs(yDst);
}
void BattleHex::checkAndPush(BattleHex tile, std::vector<BattleHex> & ret)
{
if(tile.isAvailable())
ret.push_back(tile);
}
BattleHex BattleHex::getClosestTile(BattleSide side, BattleHex initialPos, std::set<BattleHex> & possibilities)
{
std::vector<BattleHex> sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :(
BattleHex initialHex = BattleHex(initialPos); BattleHex initialHex = BattleHex(initialPos);
auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool int closestDistance = std::numeric_limits<int>::max();
BattleHexArray closestTiles;
for(auto hex : hexes)
{ {
return initialHex.getDistance (initialHex, left) < initialHex.getDistance (initialHex, right); int distance = initialHex.getDistance(initialHex, hex);
}; if(distance < closestDistance)
boost::sort (sortedTiles, compareDistance); //closest tiles at front {
int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away closestDistance = distance;
auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool closestTiles.clear();
{ closestTiles.insert(hex);
return closestDistance < here.getDistance (initialPos, here); }
}; else if(distance == closestDistance)
vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting closestTiles.insert(hex);
auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool }
auto compareHorizontal = [side, initialPos](const BattleHex & left, const BattleHex & right)
{ {
if(left.getX() != right.getX()) if(left.getX() != right.getX())
{ {
if(side == BattleSide::ATTACKER) return (side == BattleSide::ATTACKER) ? (left.getX() > right.getX()) : (left.getX() < right.getX());
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());
} }
return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
}; };
boost::sort (sortedTiles, compareHorizontal);
return sortedTiles.front(); auto bestTile = std::min_element(closestTiles.begin(), closestTiles.end(), compareHorizontal);
return (bestTile != closestTiles.end()) ? *bestTile : BattleHex();
}
const BattleHexArray & BattleHex::getAllNeighbouringTiles() const noexcept
{
return BattleHexArray::getAllNeighbouringTiles(*this);
}
const BattleHexArray & BattleHex::getNeighbouringTiles() const noexcept
{
return BattleHexArray::getNeighbouringTiles(*this);
}
const BattleHexArray & BattleHex::getNeighbouringTilesDoubleWide(BattleSide side) const noexcept
{
return BattleHexArray::getNeighbouringTilesDoubleWide(*this, side);
} }
std::ostream & operator<<(std::ostream & os, const BattleHex & hex) 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() % hex.toInt());
} }
static BattleHex::NeighbouringTilesCache calculateNeighbouringTiles()
{
BattleHex::NeighbouringTilesCache ret;
ret.resize(GameConstants::BFIELD_SIZE);
for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
{
auto hexes = BattleHex(hex).neighbouringTiles();
size_t index = 0;
for(auto neighbour : hexes)
ret[hex].at(index++) = neighbour;
}
return ret;
}
const BattleHex::NeighbouringTilesCache BattleHex::neighbouringTilesCache = calculateNeighbouringTiles();
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -22,9 +22,14 @@ namespace GameConstants
const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT; const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
} }
// for battle stacks' positions class BattleHexArray;
struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design
// for battle stacks' positions; valid hexes are from 0 to 186; available are only those not in first and last column
// castle towers are -2, -3 and -4
class DLL_LINKAGE BattleHex
{ {
public:
// helpers for siege // helpers for siege
static constexpr si16 CASTLE_CENTRAL_TOWER = -2; static constexpr si16 CASTLE_CENTRAL_TOWER = -2;
static constexpr si16 CASTLE_BOTTOM_TOWER = -3; static constexpr si16 CASTLE_BOTTOM_TOWER = -3;
@ -46,8 +51,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_OUTER = 95;
static constexpr si16 GATE_INNER = 96; static constexpr si16 GATE_INNER = 96;
si16 hex;
static constexpr si16 INVALID = -1; static constexpr si16 INVALID = -1;
enum EDir enum EDir
{ {
NONE = -1, NONE = -1,
@ -64,52 +69,209 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
BOTTOM BOTTOM
}; };
BattleHex(); BattleHex() noexcept
BattleHex(si16 _hex); : hex(INVALID)
BattleHex(si16 x, si16 y); {}
BattleHex(std::pair<si16, si16> xy); BattleHex(si16 _hex) noexcept
operator si16() const; : hex(_hex)
bool isValid() const; {}
bool isAvailable() const; //valid position not in first or last column BattleHex(si16 x, si16 y)
void setX(si16 x); {
void setY(si16 y); setXY(x, y);
void setXY(si16 x, si16 y, bool hasToBeValid = true); }
void setXY(std::pair<si16, si16> xy); BattleHex(std::pair<si16, si16> xy)
si16 getX() const; {
si16 getY() const; setXY(xy);
std::pair<si16, si16> getXY() const; }
BattleHex& moveInDirection(EDir dir, bool hasToBeValid = true);
BattleHex& operator+=(EDir dir);
BattleHex cloneInDirection(EDir dir, bool hasToBeValid = true) const;
BattleHex operator+(EDir dir) const;
/// returns all valid neighbouring tiles [[nodiscard]] bool isValid() const noexcept
std::vector<BattleHex> neighbouringTiles() const; {
return hex >= 0 && hex < GameConstants::BFIELD_SIZE;
}
[[nodiscard]] bool isAvailable() const noexcept //valid position not in first or last column
{
return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH - 1;
}
/// returns all tiles, unavailable tiles will be set as invalid void setX(si16 x)
/// order of returned tiles matches EDir enim {
std::vector<BattleHex> allNeighbouringTiles() const; setXY(x, getY());
}
static EDir mutualPosition(BattleHex hex1, BattleHex hex2); void setY(si16 y)
static uint8_t getDistance(BattleHex hex1, BattleHex hex2); {
static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret); setXY(getX(), y);
static BattleHex getClosestTile(BattleSide side, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad }
void setXY(si16 x, si16 y, bool hasToBeValid = true)
{
if(hasToBeValid)
{
if(x < 0 || x >= GameConstants::BFIELD_WIDTH || y < 0 || y >= GameConstants::BFIELD_HEIGHT)
throw std::runtime_error("Valid hex required");
}
hex = x + y * GameConstants::BFIELD_WIDTH;
}
void setXY(std::pair<si16, si16> xy)
{
setXY(xy.first, xy.second);
}
[[nodiscard]] si16 getX() const noexcept
{
return hex % GameConstants::BFIELD_WIDTH;
}
[[nodiscard]] si16 getY() const noexcept
{
return hex / GameConstants::BFIELD_WIDTH;
}
[[nodiscard]] std::pair<si16, si16> getXY() const noexcept
{
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;
}
[[nodiscard]] BattleHex cloneInDirection(EDir dir, bool hasToBeValid = true) const
{
BattleHex result(hex);
result.moveInDirection(dir, hasToBeValid);
return result;
}
[[nodiscard]] static uint8_t getDistance(BattleHex hex1, BattleHex hex2) noexcept
{
int y1 = hex1.getY();
int y2 = hex2.getY();
int x1 = hex1.getX() + y1 / 2;
int x2 = hex2.getX() + y2 / 2;
int xDst = x2 - x1;
int yDst = y2 - y1;
if((xDst >= 0 && yDst >= 0) || (xDst < 0 && yDst < 0))
return std::max(std::abs(xDst), std::abs(yDst));
return std::abs(xDst) + std::abs(yDst);
}
[[nodiscard]] static BattleHex getClosestTile(BattleSide side, BattleHex initialPos, const BattleHexArray & hexes);
//Constexpr defined array with all directions used in battle
[[nodiscard]] static constexpr auto hexagonalDirections() noexcept
{
return std::array<EDir,6>{TOP_LEFT, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT};
}
[[nodiscard]] 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
[[nodiscard]] const BattleHexArray & getAllNeighbouringTiles() const noexcept;
/// get (precomputed) only valid and available surrounding tiles
[[nodiscard]] const BattleHexArray & getNeighbouringTiles() const noexcept;
/// get (precomputed) only valid and available surrounding tiles for double wide creatures
[[nodiscard]] const BattleHexArray & getNeighbouringTilesDoubleWide(BattleSide side) const noexcept;
/// get integer hex value
[[nodiscard]] si16 toInt() const noexcept
{
return hex;
}
BattleHex & operator+=(EDir dir)
{
return moveInDirection(dir);
}
[[nodiscard]] BattleHex operator+(EDir dir) const
{
return cloneInDirection(dir);
}
// Prefix increment
BattleHex & operator++() noexcept
{
++hex;
return *this;
}
// Postfix increment
BattleHex operator++(int) noexcept
{
BattleHex temp = *this;
++hex;
return temp;
}
[[nodiscard]] bool operator ==(BattleHex other) const noexcept
{
return hex == other.hex;
}
[[nodiscard]] bool operator !=(BattleHex other) const noexcept
{
return hex != other.hex;
}
[[nodiscard]] bool operator <(BattleHex other) const noexcept
{
return hex < other.hex;
}
template <typename Handler> template <typename Handler>
void serialize(Handler &h) void serialize(Handler & h)
{ {
h & hex; h & hex;
} }
using NeighbouringTiles = std::array<BattleHex, 6>;
using NeighbouringTilesCache = std::vector<NeighbouringTiles>;
static const NeighbouringTilesCache neighbouringTilesCache;
private: private:
//Constexpr defined array with all directions used in battle
static constexpr auto hexagonalDirections() { si16 hex;
return std::array<EDir,6>{BattleHex::TOP_LEFT, BattleHex::TOP_RIGHT, BattleHex::RIGHT, BattleHex::BOTTOM_RIGHT, BattleHex::BOTTOM_LEFT, BattleHex::LEFT};
}
}; };
DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex); DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex);

View File

@ -0,0 +1,132 @@
/*
* BattleHexArray.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN
BattleHexArray::BattleHexArray(std::initializer_list<BattleHex> initList) noexcept
: BattleHexArray()
{
for(auto hex : initList)
{
insert(hex);
}
}
void BattleHexArray::insert(const BattleHexArray & other) noexcept
{
for(auto hex : other)
{
insert(hex);
}
}
void BattleHexArray::erase(iterator first, iterator last) noexcept
{
for(auto it = first; it != last && it != internalStorage.end(); ++it)
{
presenceFlags[it->toInt()] = 0;
}
internalStorage.erase(first, last);
}
void BattleHexArray::clear() noexcept
{
for(auto hex : internalStorage)
presenceFlags[hex.toInt()] = 0;
internalStorage.clear();
}
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::precalculateNeighbouringTilesDoubleWide(BattleSide side)
{
ArrayOfBattleHexArrays ret;
for(si16 h = 0; h < GameConstants::BFIELD_SIZE; h++)
{
BattleHexArray hexes;
BattleHex hex(h);
if(side == BattleSide::ATTACKER)
{
const BattleHex otherHex = h - 1;
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
hexes.checkAndPush(hex.cloneInDirection(dir, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
}
else if(side == BattleSide::DEFENDER)
{
const BattleHex otherHex = h + 1;
hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
hexes.checkAndPush(otherHex.cloneInDirection(dir, false));
hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::LEFT, false));
}
ret[h] = std::move(hexes);
}
return ret;
}
const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::neighbouringTiles = precalculateNeighbouringTiles();
const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::allNeighbouringTiles = precalculateAllNeighbouringTiles();
const std::map<BattleSide, BattleHexArray::ArrayOfBattleHexArrays> BattleHexArray::neighbouringTilesDoubleWide =
{
{ BattleSide::ATTACKER, precalculateNeighbouringTilesDoubleWide(BattleSide::ATTACKER) },
{ BattleSide::DEFENDER, precalculateNeighbouringTilesDoubleWide(BattleSide::DEFENDER) }
};
VCMI_LIB_NAMESPACE_END

317
lib/battle/BattleHexArray.h Normal file
View File

@ -0,0 +1,317 @@
/*
* BattleHexArray.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "BattleHex.h"
#include <boost/container/small_vector.hpp>
VCMI_LIB_NAMESPACE_BEGIN
/// Class representing an array of unique BattleHex objects
class DLL_LINKAGE BattleHexArray
{
public:
static constexpr uint8_t totalSize = GameConstants::BFIELD_SIZE;
using StorageType = boost::container::small_vector<BattleHex, 8>;
using ArrayOfBattleHexArrays = std::array<BattleHexArray, totalSize>;
using value_type = BattleHex;
using size_type = StorageType::size_type;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using difference_type = typename StorageType::difference_type;
using iterator = typename StorageType::iterator;
using const_iterator = typename StorageType::const_iterator;
using reverse_iterator = typename StorageType::reverse_iterator;
using const_reverse_iterator = typename StorageType::const_reverse_iterator;
BattleHexArray() = default;
template <typename Container, typename = std::enable_if_t<
std::is_convertible_v<typename Container::value_type, BattleHex>>>
BattleHexArray(const Container & container) noexcept
: BattleHexArray()
{
for(auto value : container)
{
insert(value);
}
}
void resize(size_type size)
{
clear();
internalStorage.resize(size);
}
BattleHexArray(std::initializer_list<BattleHex> initList) noexcept;
void checkAndPush(BattleHex tile)
{
if(tile.isAvailable() && !contains(tile))
{
presenceFlags[tile.toInt()] = true;
internalStorage.emplace_back(tile);
}
}
void insert(BattleHex hex) noexcept
{
if(contains(hex))
return;
presenceFlags[hex.toInt()] = true;
internalStorage.emplace_back(hex);
}
void set(size_type index, BattleHex hex)
{
if(index >= internalStorage.size())
{
logGlobal->error("Invalid BattleHexArray::set index parameter. It is " + std::to_string(index)
+ " and current size is " + std::to_string(internalStorage.size()));
throw std::out_of_range("Invalid BattleHexArray::set index parameter. It is " + std::to_string(index)
+ " and current size is " + std::to_string(internalStorage.size()));
}
if(contains(hex))
return;
presenceFlags[hex.toInt()] = true;
internalStorage[index] = hex;
}
iterator insert(iterator pos, BattleHex hex) noexcept
{
if(contains(hex))
return pos;
presenceFlags[hex.toInt()] = true;
return internalStorage.insert(pos, hex);
}
void insert(const BattleHexArray & other) noexcept;
template <typename Container, typename = std::enable_if_t<
std::is_convertible_v<typename Container::value_type, BattleHex>>>
void insert(const Container & container) noexcept
{
for(auto value : container)
{
insert(value);
}
}
void clear() noexcept;
inline void erase(size_type index) noexcept
{
assert(index < totalSize);
internalStorage[index] = BattleHex::INVALID;
presenceFlags[index] = 0;
}
void erase(iterator first, iterator last) noexcept;
inline void pop_back() noexcept
{
presenceFlags[internalStorage.back().toInt()] = false;
internalStorage.pop_back();
}
inline std::vector<BattleHex> toVector() const noexcept
{
return std::vector<BattleHex>(internalStorage.begin(), internalStorage.end());
}
template <typename Predicate>
iterator findIf(Predicate predicate) noexcept
{
return std::find_if(begin(), end(), predicate);
}
template <typename Predicate>
const_iterator findIf(Predicate predicate) const noexcept
{
return std::find_if(begin(), end(), predicate);
}
template <typename Predicate>
BattleHexArray filterBy(Predicate predicate) const noexcept
{
BattleHexArray filtered;
for(auto hex : internalStorage)
{
if(predicate(hex))
{
filtered.insert(hex);
}
}
return filtered;
}
/// get (precomputed) all possible surrounding tiles
static const BattleHexArray & getAllNeighbouringTiles(BattleHex hex) noexcept
{
assert(hex.isValid());
return allNeighbouringTiles[hex.toInt()];
}
/// get (precomputed) only valid and available surrounding tiles
static const BattleHexArray & getNeighbouringTiles(BattleHex hex) noexcept
{
assert(hex.isValid());
return neighbouringTiles[hex.toInt()];
}
/// get (precomputed) only valid and available surrounding tiles for double wide creatures
static const BattleHexArray & getNeighbouringTilesDoubleWide(BattleHex hex, BattleSide side) noexcept
{
assert(hex.isValid() && (side == BattleSide::ATTACKER || side == BattleSide::DEFENDER));
return neighbouringTilesDoubleWide.at(side)[hex.toInt()];
}
[[nodiscard]] inline bool contains(BattleHex hex) const noexcept
{
if(hex.isValid())
return presenceFlags[hex.toInt()];
/*
if(!isTower(hex))
logGlobal->warn("BattleHexArray::contains( %d ) - invalid BattleHex!", hex);
*/
// returns true also for invalid hexes
return true;
}
template <typename Serializer>
void serialize(Serializer & s)
{
s & internalStorage;
if(!s.saving)
{
for(auto hex : internalStorage)
presenceFlags[hex.toInt()] = true;
}
}
[[nodiscard]] inline const BattleHex & back() const noexcept
{
return internalStorage.back();
}
[[nodiscard]] inline const BattleHex & front() const noexcept
{
return internalStorage.front();
}
[[nodiscard]] inline const BattleHex & operator[](size_type index) const noexcept
{
return internalStorage[index];
}
[[nodiscard]] inline const BattleHex & at(size_type index) const
{
return internalStorage.at(index);
}
[[nodiscard]] inline size_type size() const noexcept
{
return internalStorage.size();
}
[[nodiscard]] inline iterator begin() noexcept
{
return internalStorage.begin();
}
[[nodiscard]] inline const_iterator begin() const noexcept
{
return internalStorage.begin();
}
[[nodiscard]] inline bool empty() const noexcept
{
return internalStorage.empty();
}
[[nodiscard]] inline iterator end() noexcept
{
return internalStorage.end();
}
[[nodiscard]] inline const_iterator end() const noexcept
{
return internalStorage.end();
}
[[nodiscard]] inline reverse_iterator rbegin() noexcept
{
return reverse_iterator(end());
}
[[nodiscard]] inline const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
[[nodiscard]] inline reverse_iterator rend() noexcept
{
return reverse_iterator(begin());
}
[[nodiscard]] inline const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
bool operator ==(const BattleHexArray & other) const noexcept
{
if(internalStorage != other.internalStorage || presenceFlags != other.presenceFlags)
return false;
return true;
}
private:
StorageType internalStorage;
std::bitset<totalSize> presenceFlags;
[[nodiscard]] inline bool isNotValidForInsertion(BattleHex hex) const
{
if(isTower(hex))
return true;
if(!hex.isValid())
{
//logGlobal->warn("BattleHexArray::insert( %d ) - invalid BattleHex!", hex);
return true;
}
return contains(hex) || internalStorage.size() >= totalSize;
}
[[nodiscard]] inline bool isTower(BattleHex hex) const
{
return hex == BattleHex::CASTLE_CENTRAL_TOWER || hex == BattleHex::CASTLE_UPPER_TOWER || hex == BattleHex::CASTLE_BOTTOM_TOWER;
}
static const ArrayOfBattleHexArrays neighbouringTiles;
static const ArrayOfBattleHexArrays allNeighbouringTiles;
static const std::map<BattleSide, ArrayOfBattleHexArrays> neighbouringTilesDoubleWide;
static ArrayOfBattleHexArrays precalculateNeighbouringTiles();
static ArrayOfBattleHexArrays precalculateAllNeighbouringTiles();
static ArrayOfBattleHexArrays precalculateNeighbouringTilesDoubleWide(BattleSide side);
};
VCMI_LIB_NAMESPACE_END

View File

@ -46,7 +46,7 @@ CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base,
assert(!owner.isValidPlayer() || (base.armyObj && base.armyObj->tempOwner == owner)); assert(!owner.isValidPlayer() || (base.armyObj && base.armyObj->tempOwner == owner));
auto * ret = new CStack(&base, owner, id, side, slot); auto * ret = new CStack(&base, owner, id, side, slot);
ret->initialPosition = getAvailableHex(base.getCreatureID(), side, position); //TODO: what if no free tile on battlefield was found? ret->initialPosition = getAvailableHex(base.getCreatureID(), side, position.toInt()); //TODO: what if no free tile on battlefield was found?
stacks.push_back(ret); stacks.push_back(ret);
return ret; return ret;
} }
@ -207,7 +207,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
r.rand(1,8); //battle sound ID to play... can't do anything with it here r.rand(1,8); //battle sound ID to play... can't do anything with it here
int tilesToBlock = r.rand(5,12); int tilesToBlock = r.rand(5,12);
std::vector<BattleHex> blockedTiles; BattleHexArray blockedTiles;
auto appropriateAbsoluteObstacle = [&](int id) auto appropriateAbsoluteObstacle = [&](int id)
{ {
@ -232,7 +232,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
currentBattle->obstacles.push_back(obstPtr); currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles()) for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked); blockedTiles.insert(blocked);
tilesToBlock -= Obstacle(obstPtr->ID).getInfo()->blockedTiles.size() / 2; tilesToBlock -= Obstacle(obstPtr->ID).getInfo()->blockedTiles.size() / 2;
} }
catch(RangeGenerator::ExhaustedPossibilities &) catch(RangeGenerator::ExhaustedPossibilities &)
@ -259,14 +259,14 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
return false; return false;
if(pos.getX() + obi.width > 15) if(pos.getX() + obi.width > 15)
return false; return false;
if(vstd::contains(blockedTiles, pos)) if(blockedTiles.contains(pos))
return false; return false;
for(BattleHex blocked : obi.getBlocked(pos)) for(BattleHex blocked : obi.getBlocked(pos))
{ {
if(tileAccessibility[blocked] == EAccessibility::UNAVAILABLE) //for ship-to-ship battlefield - exclude hardcoded unavailable tiles if(tileAccessibility[blocked.toInt()] == EAccessibility::UNAVAILABLE) //for ship-to-ship battlefield - exclude hardcoded unavailable tiles
return false; return false;
if(vstd::contains(blockedTiles, blocked)) if(blockedTiles.contains(blocked))
return false; return false;
int x = blocked.getX(); int x = blocked.getX();
if(x <= 2 || x >= 14) if(x <= 2 || x >= 14)
@ -285,7 +285,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
currentBattle->obstacles.push_back(obstPtr); currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles()) for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked); blockedTiles.insert(blocked);
tilesToBlock -= static_cast<int>(obi.blockedTiles.size()); tilesToBlock -= static_cast<int>(obi.blockedTiles.size());
} }
} }
@ -709,7 +709,7 @@ void BattleInfo::setUnitState(uint32_t id, const JsonNode & data, int64_t health
if(!accessibility.accessible(changedStack->getPosition(), changedStack)) 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 return; //position is already occupied
} }
} }

View File

@ -43,19 +43,15 @@ static BattleHex lineToWallHex(int line) //returns hex with wall in given line (
static bool sameSideOfWall(BattleHex pos1, BattleHex pos2) static bool sameSideOfWall(BattleHex pos1, BattleHex pos2)
{ {
const int wallInStackLine = lineToWallHex(pos1.getY()); const bool stackLeft = pos1 < lineToWallHex(pos1.getY());
const int wallInDestLine = lineToWallHex(pos2.getY()); const bool destLeft = pos2 < lineToWallHex(pos2.getY());
const bool stackLeft = pos1 < wallInStackLine;
const bool destLeft = pos2 < wallInDestLine;
return stackLeft == destLeft; return stackLeft == destLeft;
} }
static bool isInsideWalls(BattleHex pos) static bool isInsideWalls(BattleHex pos)
{ {
const int wallInStackLine = lineToWallHex(pos.getY()); return lineToWallHex(pos.getY()) < pos;
return wallInStackLine < pos;
} }
// parts of wall // parts of wall
@ -79,9 +75,10 @@ static const std::pair<int, EWallPart> wallParts[] =
static EWallPart hexToWallPart(BattleHex hex) static EWallPart hexToWallPart(BattleHex hex)
{ {
si16 hexValue = hex.toInt();
for(const auto & elem : wallParts) for(const auto & elem : wallParts)
{ {
if(elem.first == hex) if(elem.first == hexValue)
return elem.second; return elem.second;
} }
@ -147,25 +144,25 @@ ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const spells::Caster *
return ESpellCastProblem::OK; return ESpellCastProblem::OK;
} }
std::pair< std::vector<BattleHex>, int > CBattleInfoCallback::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const std::pair< BattleHexArray, int > CBattleInfoCallback::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const
{ {
auto reachability = getReachability(stack); auto reachability = getReachability(stack);
if(reachability.predecessors[dest] == -1) //cannot reach destination if(reachability.predecessors[dest.toInt()] == -1) //cannot reach destination
{ {
return std::make_pair(std::vector<BattleHex>(), 0); return std::make_pair(BattleHexArray(), 0);
} }
//making the Path //making the Path
std::vector<BattleHex> path; BattleHexArray path;
BattleHex curElem = dest; BattleHex curElem = dest;
while(curElem != start) while(curElem != start)
{ {
path.push_back(curElem); path.insert(curElem);
curElem = reachability.predecessors[curElem]; curElem = reachability.predecessors[curElem.toInt()];
} }
return std::make_pair(path, reachability.distances[dest]); return std::make_pair(path, reachability.distances[dest.toInt()]);
} }
bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const
@ -176,7 +173,7 @@ bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const
bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
{ {
if (!from.isAvailable() || !dest.isAvailable()) 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.toInt()) + " and " + std::to_string(dest.toInt()) + ") received in battleHasPenaltyOnLine!" );
auto isTileBlocked = [&](BattleHex tile) auto isTileBlocked = [&](BattleHex tile)
{ {
@ -191,23 +188,21 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
return isWallPartAttackable(wallPart); return isWallPartAttackable(wallPart);
}; };
// Count wall penalty requirement by shortest path, not by arbitrary line, to avoid various OH3 bugs // Count wall penalty requirement by shortest path, not by arbitrary line, to avoid various OH3 bugs
auto getShortestPath = [](BattleHex from, BattleHex dest) -> std::vector<BattleHex> auto getShortestPath = [](BattleHex from, BattleHex dest) -> BattleHexArray
{ {
//Out early //Out early
if(from == dest) if(from == dest)
return {}; return {};
std::vector<BattleHex> ret; BattleHexArray ret;
auto next = from; auto next = from;
//Not a real direction, only to indicate to which side we should search closest tile //Not a real direction, only to indicate to which side we should search closest tile
auto direction = from.getX() > dest.getX() ? BattleSide::DEFENDER : BattleSide::ATTACKER; auto direction = from.getX() > dest.getX() ? BattleSide::DEFENDER : BattleSide::ATTACKER;
while (next != dest) while (next != dest)
{ {
auto tiles = next.neighbouringTiles(); next = BattleHex::getClosestTile(direction, dest, next.getNeighbouringTiles());
std::set<BattleHex> possibilities = {tiles.begin(), tiles.end()}; ret.insert(next);
next = BattleHex::getClosestTile(direction, dest, possibilities);
ret.push_back(next);
} }
assert(!ret.empty()); assert(!ret.empty());
ret.pop_back(); //Remove destination hex ret.pop_back(); //Remove destination hex
@ -227,7 +222,7 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
auto obstacles = battleGetAllObstaclesOnPos(hex, false); auto obstacles = battleGetAllObstaclesOnPos(hex, false);
if(hex != BattleHex::GATE_BRIDGE || (battleIsGatePassable())) if(hex.toInt() != BattleHex::GATE_BRIDGE || (battleIsGatePassable()))
for(const auto & obst : obstacles) for(const auto & obst : obstacles)
if(obst->obstacleType == CObstacleInstance::MOAT) if(obst->obstacleType == CObstacleInstance::MOAT)
pathHasMoat |= true; pathHasMoat |= true;
@ -318,9 +313,9 @@ PossiblePlayerBattleAction CBattleInfoCallback::getCasterAction(const CSpell * s
return PossiblePlayerBattleAction(spellSelMode, spell->id); return PossiblePlayerBattleAction(spellSelMode, spell->id);
} }
std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos) const BattleHexArray CBattleInfoCallback::battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos) const
{ {
std::set<BattleHex> attackedHexes; BattleHexArray attackedHexes;
RETURN_IF_NOT_BATTLE(attackedHexes); RETURN_IF_NOT_BATTLE(attackedHexes);
AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos); AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos);
@ -347,7 +342,7 @@ const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyA
{ {
RETURN_IF_NOT_BATTLE(nullptr); RETURN_IF_NOT_BATTLE(nullptr);
for(const auto * s : battleGetAllStacks(true)) for(const auto * s : battleGetAllStacks(true))
if(vstd::contains(s->getHexes(), pos) && (!onlyAlive || s->alive())) if(s->getHexes().contains(pos) && (!onlyAlive || s->alive()))
return s; return s;
return nullptr; return nullptr;
@ -569,21 +564,21 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector<battle::Units> & turns,
battleGetTurnOrder(turns, maxUnits, maxTurns, actualTurn + 1, sideThatLastMoved); battleGetTurnOrder(turns, maxUnits, maxTurns, actualTurn + 1, sideThatLastMoved);
} }
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const
{ {
RETURN_IF_NOT_BATTLE(std::vector<BattleHex>()); RETURN_IF_NOT_BATTLE(BattleHexArray());
if(!unit->getPosition().isValid()) //turrets if(!unit->getPosition().isValid()) //turrets
return std::vector<BattleHex>(); return BattleHexArray();
auto reachability = getReachability(unit); auto reachability = getReachability(unit);
return battleGetAvailableHexes(reachability, unit, obtainMovementRange); return battleGetAvailableHexes(reachability, unit, obtainMovementRange);
} }
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const
{ {
std::vector<BattleHex> ret; BattleHexArray ret;
RETURN_IF_NOT_BATTLE(ret); RETURN_IF_NOT_BATTLE(ret);
if(!unit->getPosition().isValid()) //turrets if(!unit->getPosition().isValid()) //turrets
@ -612,28 +607,27 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const Reacha
continue; continue;
} }
ret.emplace_back(i); ret.insert(i);
} }
return ret; return ret;
} }
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, std::vector<BattleHex> * attackable) const BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, BattleHexArray * attackable) const
{ {
std::vector<BattleHex> ret = battleGetAvailableHexes(unit, obtainMovementRange); BattleHexArray ret = battleGetAvailableHexes(unit, obtainMovementRange);
if(ret.empty()) if(ret.empty())
return ret; return ret;
if(addOccupiable && unit->doubleWide()) if(addOccupiable && unit->doubleWide())
{ {
std::vector<BattleHex> occupiable; BattleHexArray occupiable;
occupiable.reserve(ret.size());
for(auto hex : ret) for(auto hex : ret)
occupiable.push_back(unit->occupiedHex(hex)); occupiable.insert(unit->occupiedHex(hex));
vstd::concatenate(ret, occupiable); ret.insert(occupiable);
} }
@ -643,36 +637,33 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle
{ {
// Return true if given hex has at least one available neighbour. // Return true if given hex has at least one available neighbour.
// Available hexes are already present in ret vector. // Available hexes are already present in ret vector.
auto availableNeighbor = boost::find_if(ret, [=] (BattleHex availableHex) auto availableNeighbour = boost::find_if(ret, [=] (BattleHex availableHex)
{ {
return BattleHex::mutualPosition(hex, availableHex) >= 0; return BattleHex::mutualPosition(hex, availableHex) >= 0;
}); });
return availableNeighbor != ret.end(); return availableNeighbour != ret.end();
}; };
for(const auto * otherSt : battleAliveUnits(otherSide(unit->unitSide()))) for(const auto * otherSt : battleAliveUnits(otherSide(unit->unitSide())))
{ {
if(!otherSt->isValidTarget(false)) if(!otherSt->isValidTarget(false))
continue; continue;
std::vector<BattleHex> occupied = otherSt->getHexes(); const BattleHexArray & occupied = otherSt->getHexes();
if(battleCanShoot(unit, otherSt->getPosition())) if(battleCanShoot(unit, otherSt->getPosition()))
{ {
attackable->insert(attackable->end(), occupied.begin(), occupied.end()); attackable->insert(occupied);
continue; continue;
} }
for(BattleHex he : occupied) for(BattleHex he : occupied)
{ {
if(meleeAttackable(he)) if(meleeAttackable(he))
attackable->push_back(he); attackable->insert(he);
} }
} }
} }
//adding occupiable likely adds duplicates to ret -> clean it up
boost::sort(ret);
ret.erase(boost::unique(ret).end(), ret.end());
return ret; return ret;
} }
@ -792,7 +783,7 @@ DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit *
{ {
RETURN_IF_NOT_BATTLE({}); RETURN_IF_NOT_BATTLE({});
auto reachability = battleGetDistances(attacker, attacker->getPosition()); auto reachability = battleGetDistances(attacker, attacker->getPosition());
int movementRange = attackerPosition.isValid() ? reachability[attackerPosition] : 0; int movementRange = attackerPosition.isValid() ? reachability[attackerPosition.toInt()] : 0;
return battleEstimateDamage(attacker, defender, movementRange, retaliationDmg); return battleEstimateDamage(attacker, defender, movementRange, retaliationDmg);
} }
@ -857,8 +848,8 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
RETURN_IF_NOT_BATTLE(obstacles); RETURN_IF_NOT_BATTLE(obstacles);
for(auto & obs : battleGetAllObstacles()) for(auto & obs : battleGetAllObstacles())
{ {
if(vstd::contains(obs->getBlockedTiles(), tile) if(obs->getBlockedTiles().contains(tile)
|| (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile))) || (!onlyBlocking && obs->getAffectedTiles().contains(tile)))
{ {
obstacles.push_back(obs); obstacles.push_back(obs);
} }
@ -866,18 +857,18 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
return obstacles; return obstacles;
} }
std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const
{ {
auto affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>(); auto affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
RETURN_IF_NOT_BATTLE(affectedObstacles); RETURN_IF_NOT_BATTLE(affectedObstacles);
if(unit->alive()) if(unit->alive())
{ {
if(!passed.count(unit->getPosition())) if(!passed.contains(unit->getPosition()))
affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false); affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false);
if(unit->doubleWide()) if(unit->doubleWide())
{ {
BattleHex otherHex = unit->occupiedHex(); BattleHex otherHex = unit->occupiedHex();
if(otherHex.isValid() && !passed.count(otherHex)) if(otherHex.isValid() && !passed.contains(otherHex))
for(auto & i : battleGetAllObstaclesOnPos(otherHex, false)) for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
if(!vstd::contains(affectedObstacles, i)) if(!vstd::contains(affectedObstacles, i))
affectedObstacles.push_back(i); affectedObstacles.push_back(i);
@ -891,7 +882,7 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAl
return affectedObstacles; return affectedObstacles;
} }
bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed) const bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const BattleHexArray & passed) const
{ {
if(!unit.alive()) if(!unit.alive())
return false; return false;
@ -961,8 +952,8 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
//removing accessibility for side columns of hexes //removing accessibility for side columns of hexes
for(int y = 0; y < GameConstants::BFIELD_HEIGHT; y++) for(int y = 0; y < GameConstants::BFIELD_HEIGHT; y++)
{ {
ret[BattleHex(GameConstants::BFIELD_WIDTH - 1, y)] = EAccessibility::SIDE_COLUMN; ret[BattleHex(GameConstants::BFIELD_WIDTH - 1, y).toInt()] = EAccessibility::SIDE_COLUMN;
ret[BattleHex(0, y)] = EAccessibility::SIDE_COLUMN; ret[BattleHex(0, y).toInt()] = EAccessibility::SIDE_COLUMN;
} }
//special battlefields with logically unavailable tiles //special battlefields with logically unavailable tiles
@ -970,10 +961,8 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
if(bFieldType != BattleField::NONE) if(bFieldType != BattleField::NONE)
{ {
std::vector<BattleHex> impassableHexes = bFieldType.getInfo()->impassableHexes; for(auto hex : bFieldType.getInfo()->impassableHexes)
ret[hex.toInt()] = EAccessibility::UNAVAILABLE;
for(auto hex : impassableHexes)
ret[hex] = EAccessibility::UNAVAILABLE;
} }
//gate -> should be before stacks //gate -> should be before stacks
@ -998,14 +987,14 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
{ {
for(auto hex : unit->getHexes()) for(auto hex : unit->getHexes())
if(hex.isAvailable()) //towers can have <0 pos; we don't also want to overwrite side columns if(hex.isAvailable()) //towers can have <0 pos; we don't also want to overwrite side columns
ret[hex] = EAccessibility::ALIVE_STACK; ret[hex.toInt()] = EAccessibility::ALIVE_STACK;
} }
//obstacles //obstacles
for(const auto &obst : battleGetAllObstacles()) for(const auto &obst : battleGetAllObstacles())
{ {
for(auto hex : obst->getBlockedTiles()) for(auto hex : obst->getBlockedTiles())
ret[hex] = EAccessibility::OBSTACLE; ret[hex.toInt()] = EAccessibility::OBSTACLE;
} }
//walls //walls
@ -1028,7 +1017,7 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
for(const auto & elem : lockedIfNotDestroyed) for(const auto & elem : lockedIfNotDestroyed)
{ {
if(battleGetWallState(elem.first) != EWallState::DESTROYED) if(battleGetWallState(elem.first) != EWallState::DESTROYED)
ret[elem.second] = EAccessibility::DESTRUCTIBLE_WALL; ret[elem.second.toInt()] = EAccessibility::DESTRUCTIBLE_WALL;
} }
} }
@ -1040,17 +1029,17 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility(const battle::Unit * sta
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 std::vector<BattleHex> & 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.toInt()] = 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;
@ -1062,7 +1051,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
if(!params.startPosition.isValid()) //if got call for arrow turrets if(!params.startPosition.isValid()) //if got call for arrow turrets
return ret; return ret;
const std::set<BattleHex> obstacles = getStoppers(params.perspective); const BattleHexArray obstacles = getStoppers(params.perspective);
auto checkParams = params; auto checkParams = params;
checkParams.ignoreKnownAccessible = true; //Ignore starting hexes obstacles checkParams.ignoreKnownAccessible = true; //Ignore starting hexes obstacles
@ -1070,7 +1059,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
//first element //first element
hexq.push(params.startPosition); hexq.push(params.startPosition);
ret.distances[params.startPosition] = 0; ret.distances[params.startPosition.toInt()] = 0;
std::array<bool, GameConstants::BFIELD_SIZE> accessibleCache{}; std::array<bool, GameConstants::BFIELD_SIZE> accessibleCache{};
for(int hex = 0; hex < GameConstants::BFIELD_SIZE; hex++) for(int hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
@ -1085,32 +1074,29 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
if(isInObstacle(curHex, obstacles, checkParams)) if(isInObstacle(curHex, obstacles, checkParams))
continue; continue;
const int costToNeighbour = ret.distances.at(curHex.hex) + 1; const int costToNeighbour = ret.distances.at(curHex.toInt()) + 1;
for(BattleHex neighbour : BattleHex::neighbouringTilesCache[curHex.hex]) for(BattleHex neighbour : curHex.getNeighbouringTiles())
{ {
if(neighbour.isValid()) auto additionalCost = 0;
if(params.bypassEnemyStacks)
{ {
auto additionalCost = 0; auto enemyToBypass = params.destructibleEnemyTurns.at(neighbour.toInt());
if(params.bypassEnemyStacks) if(enemyToBypass >= 0)
{ {
auto enemyToBypass = params.destructibleEnemyTurns.find(neighbour); additionalCost = enemyToBypass;
if(enemyToBypass != params.destructibleEnemyTurns.end())
{
additionalCost = enemyToBypass->second;
}
} }
}
const int costFoundSoFar = ret.distances[neighbour.hex]; const int costFoundSoFar = ret.distances[neighbour.toInt()];
if(accessibleCache[neighbour.hex] && costToNeighbour + additionalCost < costFoundSoFar) if(accessibleCache[neighbour.toInt()] && costToNeighbour + additionalCost < costFoundSoFar)
{ {
hexq.push(neighbour); hexq.push(neighbour);
ret.distances[neighbour.hex] = costToNeighbour + additionalCost; ret.distances[neighbour.toInt()] = costToNeighbour + additionalCost;
ret.predecessors[neighbour.hex] = curHex; ret.predecessors[neighbour.toInt()] = curHex;
}
} }
} }
} }
@ -1120,17 +1106,16 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
bool CBattleInfoCallback::isInObstacle( bool CBattleInfoCallback::isInObstacle(
BattleHex hex, BattleHex hex,
const std::set<BattleHex> & obstacles, const BattleHexArray & obstacleHexes,
const ReachabilityInfo::Parameters & params) const const ReachabilityInfo::Parameters & params) const
{ {
auto occupiedHexes = battle::Unit::getHexes(hex, params.doubleWide, params.side);
for(auto occupiedHex : occupiedHexes) for(auto occupiedHex : battle::Unit::getHexes(hex, params.doubleWide, params.side))
{ {
if(params.ignoreKnownAccessible && vstd::contains(params.knownAccessible, occupiedHex)) if(params.ignoreKnownAccessible && params.knownAccessible->contains(occupiedHex))
continue; continue;
if(vstd::contains(obstacles, occupiedHex)) if(obstacleHexes.contains(occupiedHex))
{ {
if(occupiedHex == BattleHex::GATE_BRIDGE) if(occupiedHex == BattleHex::GATE_BRIDGE)
{ {
@ -1145,9 +1130,9 @@ bool CBattleInfoCallback::isInObstacle(
return false; return false;
} }
std::set<BattleHex> CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective) const BattleHexArray CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective) const
{ {
std::set<BattleHex> ret; BattleHexArray ret;
RETURN_IF_NOT_BATTLE(ret); RETURN_IF_NOT_BATTLE(ret);
for(auto &oi : battleGetAllObstacles(whichSidePerspective)) for(auto &oi : battleGetAllObstacles(whichSidePerspective))
@ -1155,7 +1140,7 @@ std::set<BattleHex> CBattleInfoCallback::getStoppers(BattleSide whichSidePerspec
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)
{ {
@ -1193,7 +1178,7 @@ std::pair<const battle::Unit *, BattleHex> CBattleInfoCallback::getNearestStack(
for(BattleHex hex : avHexes) for(BattleHex hex : avHexes)
if(CStack::isMeleeAttackPossible(closest, st, hex)) if(CStack::isMeleeAttackPossible(closest, st, hex))
{ {
DistStack hlp = {reachability.distances[hex], hex, st}; DistStack hlp = {reachability.distances[hex.toInt()], hex, st};
stackPairs.push_back(hlp); stackPairs.push_back(hlp);
} }
} }
@ -1225,7 +1210,7 @@ BattleHex CBattleInfoCallback::getAvailableHex(const CreatureID & creID, BattleS
auto accessibility = getAccessibility(); auto accessibility = getAccessibility();
std::set<BattleHex> occupyable; BattleHexArray occupyable;
for(int i = 0; i < accessibility.size(); i++) for(int i = 0; i < accessibility.size(); i++)
if(accessibility.accessible(i, twoHex, side)) if(accessibility.accessible(i, twoHex, side))
occupyable.insert(i); occupyable.insert(i);
@ -1278,24 +1263,27 @@ 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);
else else
{ {
auto accessibility = getAccessibility(params.knownAccessible); auto accessibility = getAccessibility(* params.knownAccessible);
accessibility.destructibleEnemyTurns = params.destructibleEnemyTurns; accessibility.destructibleEnemyTurns = std::shared_ptr<const TBattlefieldTurnsArray>(
& params.destructibleEnemyTurns,
[](const TBattlefieldTurnsArray *) { }
);
return makeBFS(accessibility, params); return makeBFS(accessibility, params);
} }
} }
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);
for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
{ {
@ -1338,9 +1326,9 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
AttackableTiles at; AttackableTiles at;
RETURN_IF_NOT_BATTLE(at); RETURN_IF_NOT_BATTLE(at);
BattleHex attackOriginHex = (attackerPos != BattleHex::INVALID) ? attackerPos : attacker->getPosition(); //real or hypothetical (cursor) position BattleHex attackOriginHex = (attackerPos.toInt() != BattleHex::INVALID) ? attackerPos : attacker->getPosition(); //real or hypothetical (cursor) position
defenderPos = (defenderPos != BattleHex::INVALID) ? defenderPos : defender->getPosition(); //real or hypothetical (cursor) position defenderPos = (defenderPos.toInt() != BattleHex::INVALID) ? defenderPos : defender->getPosition(); //real or hypothetical (cursor) position
bool reverse = isToReverse(attacker, defender, attackerPos, defenderPos); bool reverse = isToReverse(attacker, defender, attackerPos, defenderPos);
if(reverse && attacker->doubleWide()) if(reverse && attacker->doubleWide())
@ -1349,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.insert(attacker->getSurroundingHexes(attackerPos));
} }
if(attacker->hasBonusOfType(BonusType::THREE_HEADED_ATTACK)) if(attacker->hasBonusOfType(BonusType::THREE_HEADED_ATTACK))
{ {
std::vector<BattleHex> 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
@ -1366,12 +1354,12 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
} }
if(attacker->hasBonusOfType(BonusType::WIDE_BREATH)) if(attacker->hasBonusOfType(BonusType::WIDE_BREATH))
{ {
std::vector<BattleHex> hexes = destinationTile.neighbouringTiles(); BattleHexArray hexes = destinationTile.getNeighbouringTiles();
for(int i = 0; i<hexes.size(); i++) for(int i = 0; i < hexes.size(); i++)
{ {
if(hexes.at(i) == attackOriginHex) if(hexes.at(i) == attackOriginHex)
{ {
hexes.erase(hexes.begin() + i); hexes.erase(i);
i = 0; i = 0;
} }
} }
@ -1439,11 +1427,10 @@ AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const battle::
AttackableTiles at; AttackableTiles at;
RETURN_IF_NOT_BATTLE(at); RETURN_IF_NOT_BATTLE(at);
if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile)) if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !attackerPos.getNeighbouringTiles().contains(destinationTile))
{ {
std::vector<BattleHex> targetHexes = destinationTile.neighbouringTiles(); at.hostileCreaturePositions.insert(destinationTile.getNeighbouringTiles());
targetHexes.push_back(destinationTile); at.hostileCreaturePositions.insert(destinationTile);
boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions));
} }
return at; return at;
@ -1480,9 +1467,9 @@ std::vector<const battle::Unit*> CBattleInfoCallback::getAttackedBattleUnits(
for (BattleHex hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide())) for (BattleHex hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide()))
{ {
if (vstd::contains(at.hostileCreaturePositions, hex)) if (at.hostileCreaturePositions.contains(hex))
return true; return true;
if (vstd::contains(at.friendlyCreaturePositions, hex)) if (at.friendlyCreaturePositions.contains(hex))
return true; return true;
} }
return false; return false;
@ -1671,15 +1658,15 @@ bool CBattleInfoCallback::isWallPartAttackable(EWallPart wallPart) const
return false; return false;
} }
std::vector<BattleHex> CBattleInfoCallback::getAttackableBattleHexes() const BattleHexArray CBattleInfoCallback::getAttackableBattleHexes() const
{ {
std::vector<BattleHex> attackableBattleHexes; BattleHexArray attackableBattleHexes;
RETURN_IF_NOT_BATTLE(attackableBattleHexes); RETURN_IF_NOT_BATTLE(attackableBattleHexes);
for(const auto & wallPartPair : wallParts) for(const auto & wallPartPair : wallParts)
{ {
if(isWallPartAttackable(wallPartPair.second)) if(isWallPartAttackable(wallPartPair.second))
attackableBattleHexes.emplace_back(wallPartPair.first); attackableBattleHexes.insert(wallPartPair.first);
} }
return attackableBattleHexes; return attackableBattleHexes;

View File

@ -38,8 +38,8 @@ namespace spells
struct DLL_LINKAGE AttackableTiles struct DLL_LINKAGE AttackableTiles
{ {
std::set<BattleHex> hostileCreaturePositions; BattleHexArray hostileCreaturePositions;
std::set<BattleHex> friendlyCreaturePositions; //for Dragon Breath BattleHexArray friendlyCreaturePositions; //for Dragon Breath
template <typename Handler> void serialize(Handler &h) template <typename Handler> void serialize(Handler &h)
{ {
h & hostileCreaturePositions; h & hostileCreaturePositions;
@ -59,9 +59,9 @@ public:
std::optional<BattleSide> battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw std::optional<BattleSide> battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw
std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override; std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override;
std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const override; std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const override;
//Handle obstacle damage here, requires SpellCastEnvironment //Handle obstacle damage here, requires SpellCastEnvironment
bool handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed = {}) const; bool handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const BattleHexArray & passed = {}) const;
const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const;
@ -75,20 +75,20 @@ public:
void battleGetTurnOrder(std::vector<battle::Units> & out, const size_t maxUnits, const int maxTurns, const int turn = 0, BattleSide lastMoved = BattleSide::NONE) const; void battleGetTurnOrder(std::vector<battle::Units> & out, const size_t maxUnits, const int maxTurns, const int turn = 0, BattleSide lastMoved = BattleSide::NONE) const;
///returns reachable hexes (valid movement destinations), DOES contain stack current position ///returns reachable hexes (valid movement destinations), DOES contain stack current position
std::vector<BattleHex> battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, std::vector<BattleHex> * attackable) const; BattleHexArray battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, BattleHexArray * attackable) const;
///returns reachable hexes (valid movement destinations), DOES contain stack current position (lite version) ///returns reachable hexes (valid movement destinations), DOES contain stack current position (lite version)
std::vector<BattleHex> battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const; BattleHexArray battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const;
std::vector<BattleHex> battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const; BattleHexArray battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const;
int battleGetSurrenderCost(const PlayerColor & Player) const; //returns cost of surrendering battle, -1 if surrendering is not possible int battleGetSurrenderCost(const PlayerColor & Player) const; //returns cost of surrendering battle, -1 if surrendering is not possible
ReachabilityInfo::TDistances battleGetDistances(const battle::Unit * unit, BattleHex assumedPosition) const; ReachabilityInfo::TDistances battleGetDistances(const battle::Unit * unit, BattleHex assumedPosition) const;
std::set<BattleHex> battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; BattleHexArray battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const;
bool isEnemyUnitWithinSpecifiedRange(BattleHex attackerPosition, const battle::Unit * defenderUnit, unsigned int range) const; bool isEnemyUnitWithinSpecifiedRange(BattleHex attackerPosition, const battle::Unit * defenderUnit, unsigned int range) const;
bool isHexWithinSpecifiedRange(BattleHex attackerPosition, BattleHex targetPosition, unsigned int range) const; bool isHexWithinSpecifiedRange(BattleHex attackerPosition, BattleHex targetPosition, unsigned int range) const;
std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const; std::pair< BattleHexArray, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const;
bool battleCanTargetEmptyHex(const battle::Unit * attacker) const; //determines of stack with given ID can target empty hex to attack - currently used only for SPELL_LIKE_ATTACK shooting bool battleCanTargetEmptyHex(const battle::Unit * attacker) const; //determines of stack with given ID can target empty hex to attack - currently used only for SPELL_LIKE_ATTACK shooting
bool battleCanAttack(const battle::Unit * stack, const battle::Unit * target, BattleHex dest) const; //determines if stack with given ID can attack target at the selected destination bool battleCanAttack(const battle::Unit * stack, const battle::Unit * target, BattleHex dest) const; //determines if stack with given ID can attack target at the selected destination
@ -116,7 +116,7 @@ public:
EWallPart battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found EWallPart battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
bool isWallPartPotentiallyAttackable(EWallPart wallPart) const; // returns true if the wall part is potentially attackable (independent of wall state), false if not bool isWallPartPotentiallyAttackable(EWallPart wallPart) const; // returns true if the wall part is potentially attackable (independent of wall state), false if not
bool isWallPartAttackable(EWallPart wallPart) const; // returns true if the wall part is actually attackable, false if not bool isWallPartAttackable(EWallPart wallPart) const; // returns true if the wall part is actually attackable, false if not
std::vector<BattleHex> getAttackableBattleHexes() const; BattleHexArray getAttackableBattleHexes() const;
si8 battleMinSpellLevel(BattleSide side) const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned si8 battleMinSpellLevel(BattleSide side) const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
si8 battleMaxSpellLevel(BattleSide side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned si8 battleMaxSpellLevel(BattleSide side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
@ -162,15 +162,15 @@ 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 std::vector<BattleHex> & 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
protected: protected:
ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const; ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const;
ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const; ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const;
bool isInObstacle(BattleHex hex, const std::set<BattleHex> & obstacles, const ReachabilityInfo::Parameters & params) const; bool isInObstacle(BattleHex hex, const BattleHexArray & obstacles, const ReachabilityInfo::Parameters & params) const;
std::set<BattleHex> getStoppers(BattleSide whichSidePerspective) const; //get hexes with stopping obstacles (quicksands) BattleHexArray getStoppers(BattleSide whichSidePerspective) const; //get hexes with stopping obstacles (quicksands)
}; };
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -24,21 +24,21 @@ const ObstacleInfo & CObstacleInstance::getInfo() const
return *Obstacle(ID).getInfo(); return *Obstacle(ID).getInfo();
} }
std::vector<BattleHex> CObstacleInstance::getBlockedTiles() const BattleHexArray CObstacleInstance::getBlockedTiles() const
{ {
if(blocksTiles()) if(blocksTiles())
return getAffectedTiles(); return getAffectedTiles();
return std::vector<BattleHex>(); return BattleHexArray();
} }
std::vector<BattleHex> CObstacleInstance::getStoppingTile() const BattleHexArray CObstacleInstance::getStoppingTile() const
{ {
if(stopsMovement()) if(stopsMovement())
return getAffectedTiles(); return getAffectedTiles();
return std::vector<BattleHex>(); return BattleHexArray();
} }
std::vector<BattleHex> CObstacleInstance::getAffectedTiles() const BattleHexArray CObstacleInstance::getAffectedTiles() const
{ {
switch(obstacleType) switch(obstacleType)
{ {
@ -47,7 +47,7 @@ std::vector<BattleHex> CObstacleInstance::getAffectedTiles() const
return getInfo().getBlocked(pos); return getInfo().getBlocked(pos);
default: default:
assert(0); assert(0);
return std::vector<BattleHex>(); return BattleHexArray();
} }
} }
@ -113,7 +113,9 @@ void CObstacleInstance::serializeJson(JsonSerializeFormat & handler)
animationYOffset -= 42; animationYOffset -= 42;
//We need only a subset of obstacle info for correct render //We need only a subset of obstacle info for correct render
handler.serializeInt("position", pos); si16 posValue = pos.toInt();
handler.serializeInt("position", posValue);
pos = posValue;
handler.serializeInt("animationYOffset", animationYOffset); handler.serializeInt("animationYOffset", animationYOffset);
handler.serializeBool("hidden", hidden); handler.serializeBool("hidden", hidden);
handler.serializeBool("needAnimationOffsetFix", needAnimationOffsetFix); handler.serializeBool("needAnimationOffsetFix", needAnimationOffsetFix);
@ -188,7 +190,9 @@ void SpellCreatedObstacle::fromInfo(const ObstacleChanges & info)
void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler) void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler)
{ {
handler.serializeInt("spell", ID); handler.serializeInt("spell", ID);
handler.serializeInt("position", pos); si16 posValue = pos.toInt();
handler.serializeInt("position", posValue);
pos = posValue;
handler.serializeInt("turnsRemaining", turnsRemaining); handler.serializeInt("turnsRemaining", turnsRemaining);
handler.serializeInt("casterSpellPower", casterSpellPower); handler.serializeInt("casterSpellPower", casterSpellPower);
@ -216,11 +220,15 @@ void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler)
customSizeJson.syncSize(customSize, JsonNode::JsonType::DATA_INTEGER); customSizeJson.syncSize(customSize, JsonNode::JsonType::DATA_INTEGER);
for(size_t index = 0; index < customSizeJson.size(); index++) for(size_t index = 0; index < customSizeJson.size(); index++)
customSizeJson.serializeInt(index, customSize.at(index)); {
si16 hex = customSize.at(index).toInt();
customSizeJson.serializeInt(index, hex);
customSize.set(index, hex);
}
} }
} }
std::vector<BattleHex> SpellCreatedObstacle::getAffectedTiles() const BattleHexArray SpellCreatedObstacle::getAffectedTiles() const
{ {
return customSize; return customSize;
} }
@ -258,4 +266,4 @@ int SpellCreatedObstacle::getAnimationYOffset(int imageHeight) const
return offset; return offset;
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -8,7 +8,7 @@
* *
*/ */
#pragma once #pragma once
#include "BattleHex.h" #include "BattleHexArray.h"
#include "../constants/EntityIdentifiers.h" #include "../constants/EntityIdentifiers.h"
#include "../filesystem/ResourcePath.h" #include "../filesystem/ResourcePath.h"
@ -39,8 +39,8 @@ struct DLL_LINKAGE CObstacleInstance : public Serializeable
const ObstacleInfo &getInfo() const; //allowed only when not generated by spell (usual or absolute) const ObstacleInfo &getInfo() const; //allowed only when not generated by spell (usual or absolute)
std::vector<BattleHex> getBlockedTiles() const; BattleHexArray getBlockedTiles() const;
std::vector<BattleHex> getStoppingTile() const; //hexes that will stop stack move BattleHexArray getStoppingTile() const; //hexes that will stop stack move
//The two functions below describe how the obstacle affects affected tiles //The two functions below describe how the obstacle affects affected tiles
//additional effects (like hurting stack or disappearing) are hardcoded for appropriate obstacleTypes //additional effects (like hurting stack or disappearing) are hardcoded for appropriate obstacleTypes
@ -49,7 +49,7 @@ struct DLL_LINKAGE CObstacleInstance : public Serializeable
virtual bool triggersEffects() const; virtual bool triggersEffects() const;
virtual SpellID getTrigger() const; virtual SpellID getTrigger() const;
virtual std::vector<BattleHex> getAffectedTiles() const; virtual BattleHexArray getAffectedTiles() const;
virtual bool visibleForSide(BattleSide side, bool hasNativeStack) const; //0 attacker virtual bool visibleForSide(BattleSide side, bool hasNativeStack) const; //0 attacker
virtual void battleTurnPassed(){}; virtual void battleTurnPassed(){};
@ -97,11 +97,11 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
int animationYOffset; int animationYOffset;
std::vector<BattleHex> customSize; BattleHexArray customSize;
SpellCreatedObstacle(); SpellCreatedObstacle();
std::vector<BattleHex> getAffectedTiles() const override; BattleHexArray getAffectedTiles() const override;
bool visibleForSide(BattleSide side, bool hasNativeStack) const override; bool visibleForSide(BattleSide side, bool hasNativeStack) const override;
bool blocksTiles() const override; bool blocksTiles() const override;

View File

@ -800,7 +800,9 @@ void CUnitState::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("cloneID", cloneID); handler.serializeInt("cloneID", cloneID);
handler.serializeInt("position", position); si16 posValue = position.toInt();
handler.serializeInt("position", posValue);
position = posValue;
} }
void CUnitState::localInit(const IUnitEnvironment * env_) void CUnitState::localInit(const IUnitEnvironment * env_)

View File

@ -45,7 +45,7 @@ DamageRange DamageCalculator::getBaseDamageSingle() const
const auto * town = callback.battleGetDefendedTown(); const auto * town = callback.battleGetDefendedTown();
assert(town); assert(town);
switch(info.attacker->getPosition()) switch(info.attacker->getPosition().toInt())
{ {
case BattleHex::CASTLE_CENTRAL_TOWER: case BattleHex::CASTLE_CENTRAL_TOWER:
return town->getKeepDamageRange(); return town->getKeepDamageRange();

View File

@ -11,7 +11,7 @@
#pragma once #pragma once
#include "GameConstants.h" #include "GameConstants.h"
#include "BattleHex.h" #include "BattleHexArray.h"
#include <vcmi/Entity.h> #include <vcmi/Entity.h>
@ -81,7 +81,7 @@ public:
//blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands)
virtual std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0; virtual std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0;
virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const = 0; virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const = 0;
}; };

View File

@ -21,7 +21,8 @@ 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);
} }
ReachabilityInfo::ReachabilityInfo() ReachabilityInfo::ReachabilityInfo()
@ -32,22 +33,22 @@ ReachabilityInfo::ReachabilityInfo()
bool ReachabilityInfo::isReachable(BattleHex hex) const bool ReachabilityInfo::isReachable(BattleHex hex) const
{ {
return distances[hex] < INFINITE_DIST; return distances[hex.toInt()] < INFINITE_DIST;
} }
uint32_t ReachabilityInfo::distToNearestNeighbour( uint32_t ReachabilityInfo::distToNearestNeighbour(
const std::vector<BattleHex> & targetHexes, const BattleHexArray & targetHexes,
BattleHex * chosenHex) const BattleHex * chosenHex) const
{ {
uint32_t ret = 1000000; uint32_t ret = 1000000;
for(auto targetHex : targetHexes) for(auto targetHex : targetHexes)
{ {
for(auto & n : targetHex.neighbouringTiles()) for(auto & n : targetHex.getNeighbouringTiles())
{ {
if(distances[n] < ret) if(distances[n.toInt()] < ret)
{ {
ret = distances[n]; ret = distances[n.toInt()];
if(chosenHex) if(chosenHex)
*chosenHex = n; *chosenHex = n;
} }
@ -70,16 +71,14 @@ uint32_t ReachabilityInfo::distToNearestNeighbour(
{ {
// It can be back to back attack o==o or head to head =oo=. // It can be back to back attack o==o or head to head =oo=.
// In case of back-to-back the distance between heads (unit positions) may be up to 3 tiles // In case of back-to-back the distance between heads (unit positions) may be up to 3 tiles
vstd::concatenate(attackableHexes, battle::Unit::getHexes(defender->occupiedHex(), true, defender->unitSide())); attackableHexes.insert(battle::Unit::getHexes(defender->occupiedHex(), true, defender->unitSide()));
} }
else else
{ {
vstd::concatenate(attackableHexes, battle::Unit::getHexes(defender->getPosition(), true, defender->unitSide())); attackableHexes.insert(battle::Unit::getHexes(defender->getPosition(), true, defender->unitSide()));
} }
} }
vstd::removeDuplicates(attackableHexes);
vstd::erase_if(attackableHexes, [defender](BattleHex h) -> bool vstd::erase_if(attackableHexes, [defender](BattleHex h) -> bool
{ {
return h.getY() != defender->getPosition().getY() || !h.isAvailable(); return h.getY() != defender->getPosition().getY() || !h.isAvailable();

View File

@ -8,12 +8,14 @@
* *
*/ */
#pragma once #pragma once
#include "BattleHex.h" #include "BattleHexArray.h"
#include "CBattleInfoEssentials.h" #include "CBattleInfoEssentials.h"
#include "AccessibilityInfo.h" #include "AccessibilityInfo.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class BattleHexArray;
// Reachability info is result of BFS calculation. It's dependent on stack (it's owner, whether it's flying), // Reachability info is result of BFS calculation. It's dependent on stack (it's owner, whether it's flying),
// startPosition and perspective. // startPosition and perspective.
struct DLL_LINKAGE ReachabilityInfo struct DLL_LINKAGE ReachabilityInfo
@ -30,13 +32,16 @@ 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
std::vector<BattleHex> 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::map<BattleHex, ui8> destructibleEnemyTurns; // hom many turns it is needed to kill enemy on specific hex TBattlefieldTurnsArray 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
BattleSide perspective = BattleSide::ALL_KNOWING; //some obstacles (eg. quicksands) may be invisible for some side BattleSide perspective = BattleSide::ALL_KNOWING; //some obstacles (eg. quicksands) may be invisible for some side
Parameters() = default; Parameters()
{
destructibleEnemyTurns.fill(-1);
}
Parameters(const battle::Unit * Stack, BattleHex StartPosition); Parameters(const battle::Unit * Stack, BattleHex StartPosition);
}; };
@ -50,7 +55,7 @@ struct DLL_LINKAGE ReachabilityInfo
bool isReachable(BattleHex hex) const; bool isReachable(BattleHex hex) const;
uint32_t distToNearestNeighbour( uint32_t distToNearestNeighbour(
const std::vector<BattleHex> & targetHexes, const BattleHexArray & targetHexes,
BattleHex * chosenHex = nullptr) const; BattleHex * chosenHex = nullptr) const;
uint32_t distToNearestNeighbour( uint32_t distToNearestNeighbour(

View File

@ -51,55 +51,29 @@ const IBonusBearer* Unit::getBonusBearer() const
return this; return this;
} }
std::vector<BattleHex> 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.toInt() != BattleHex::INVALID) ? assumedPosition : getPosition(); //use hypothetical position
return getSurroundingHexes(hex, doubleWide(), unitSide()); return getSurroundingHexes(hex, doubleWide(), unitSide());
} }
std::vector<BattleHex> Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side) const BattleHexArray & Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
{ {
std::vector<BattleHex> hexes; if(!twoHex)
if(twoHex) return position.getNeighbouringTiles();
{
const BattleHex otherHex = occupiedHex(position, twoHex, side);
if(side == BattleSide::ATTACKER) return position.getNeighbouringTilesDoubleWide(side);
{
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
BattleHex::checkAndPush(position.cloneInDirection(dir, false), hexes);
BattleHex::checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), hexes);
BattleHex::checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false), hexes);
BattleHex::checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false), hexes);
}
else
{
BattleHex::checkAndPush(position.cloneInDirection(BattleHex::EDir::TOP_LEFT, false), hexes);
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
BattleHex::checkAndPush(otherHex.cloneInDirection(dir, false), hexes);
BattleHex::checkAndPush(position.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), hexes);
BattleHex::checkAndPush(position.cloneInDirection(BattleHex::EDir::LEFT, false), hexes);
}
return hexes;
}
else
{
return position.neighbouringTiles();
}
} }
std::vector<BattleHex> 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());
std::vector<BattleHex> targetableHexes; BattleHexArray targetableHexes;
for(auto defenderHex : defenderHexes) for(auto defenderHex : defenderHexes)
{ {
@ -112,11 +86,9 @@ std::vector<BattleHex> Unit::getAttackableHexes(const Unit * attacker) const
hexes.pop_back(); hexes.pop_back();
for(auto hex : hexes) for(auto hex : hexes)
vstd::concatenate(targetableHexes, hex.neighbouringTiles()); targetableHexes.insert(hex.getNeighbouringTiles());
} }
vstd::removeDuplicates(targetableHexes);
return targetableHexes; return targetableHexes;
} }
@ -125,25 +97,35 @@ bool Unit::coversPos(BattleHex pos) const
return getPosition() == pos || (doubleWide() && (occupiedHex() == pos)); return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
} }
std::vector<BattleHex> Unit::getHexes() const const BattleHexArray & Unit::getHexes() const
{ {
return getHexes(getPosition(), doubleWide(), unitSide()); return getHexes(getPosition(), doubleWide(), unitSide());
} }
std::vector<BattleHex> Unit::getHexes(BattleHex assumedPos) const const BattleHexArray & Unit::getHexes(BattleHex assumedPos) const
{ {
return getHexes(assumedPos, doubleWide(), unitSide()); return getHexes(assumedPos, doubleWide(), unitSide());
} }
std::vector<BattleHex> Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side) const BattleHexArray & Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side)
{ {
std::vector<BattleHex> hexes; static BattleHexArray::ArrayOfBattleHexArrays precomputed[4];
hexes.push_back(assumedPos); int index = side == BattleSide::ATTACKER ? 0 : 2;
if(!precomputed[index + twoHex][assumedPos.toInt()].empty())
return precomputed[index + twoHex][assumedPos.toInt()];
// first run, compute
BattleHexArray hexes;
hexes.insert(assumedPos);
if(twoHex) if(twoHex)
hexes.push_back(occupiedHex(assumedPos, twoHex, side)); hexes.insert(occupiedHex(assumedPos, twoHex, side));
return hexes; precomputed[index + twoHex][assumedPos.toInt()] = std::move(hexes);
return precomputed[index + twoHex][assumedPos.toInt()];
} }
BattleHex Unit::occupiedHex() const BattleHex Unit::occupiedHex() const
@ -161,9 +143,9 @@ BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, BattleSide side)
if(twoHex) if(twoHex)
{ {
if(side == BattleSide::ATTACKER) if(side == BattleSide::ATTACKER)
return assumedPos - 1; return assumedPos.toInt() - 1;
else else
return assumedPos + 1; return assumedPos.toInt() + 1;
} }
else else
{ {
@ -219,7 +201,9 @@ void UnitInfo::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("count", count); handler.serializeInt("count", count);
handler.serializeId("type", type, CreatureID(CreatureID::NONE)); handler.serializeId("type", type, CreatureID(CreatureID::NONE));
handler.serializeInt("side", side); handler.serializeInt("side", side);
handler.serializeInt("position", position); si16 positionValue = position.toInt();
handler.serializeInt("position", positionValue);
position = positionValue;
handler.serializeBool("summoned", summoned); handler.serializeBool("summoned", summoned);
} }

View File

@ -17,7 +17,7 @@
#include "../bonuses/IBonusBearer.h" #include "../bonuses/IBonusBearer.h"
#include "IUnitInfo.h" #include "IUnitInfo.h"
#include "BattleHex.h" #include "BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -128,15 +128,15 @@ public:
virtual std::string getDescription() const; virtual std::string getDescription() const;
std::vector<BattleHex> 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
std::vector<BattleHex> getAttackableHexes(const Unit * attacker) const; BattleHexArray getAttackableHexes(const Unit * attacker) const;
static std::vector<BattleHex> 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
std::vector<BattleHex> getHexes() const; //up to two occupied hexes, starting from front const BattleHexArray & getHexes() const; //up to two occupied hexes, starting from front
std::vector<BattleHex> 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 std::vector<BattleHex> 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

View File

@ -142,7 +142,7 @@ class JsonNode;
BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\ BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\ BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\
BONUS_NAME(SYNERGY_TARGET) /* dummy skill for alternative upgrades mod */\ BONUS_NAME(SYNERGY_TARGET) /* dummy skill for alternative upgrades mod */\
BONUS_NAME(SHOOTS_ALL_ADJACENT) /* H4 Cyclops-like shoot (attacks all hexes neighboring with target) without spell-like mechanics */\ BONUS_NAME(SHOOTS_ALL_ADJACENT) /* H4 Cyclops-like shoot (attacks all hexes neighbouring with target) without spell-like mechanics */\
BONUS_NAME(BLOCK_MAGIC_BELOW) /*blocks casting spells of the level < value */ \ BONUS_NAME(BLOCK_MAGIC_BELOW) /*blocks casting spells of the level < value */ \
BONUS_NAME(DESTRUCTION) /*kills extra units after hit, subtype = 0 - kill percentage of units, 1 - kill amount, val = chance in percent to trigger, additional info - amount/percentage to kill*/ \ BONUS_NAME(DESTRUCTION) /*kills extra units after hit, subtype = 0 - kill percentage of units, 1 - kill amount, val = chance in percent to trigger, additional info - amount/percentage to kill*/ \
BONUS_NAME(SPECIAL_CRYSTAL_GENERATION) /*crystal dragon crystal generation*/ \ BONUS_NAME(SPECIAL_CRYSTAL_GENERATION) /*crystal dragon crystal generation*/ \

View File

@ -220,13 +220,14 @@ ILimiter::EDecision UnitOnHexLimiter::limit(const BonusLimitationContext &contex
return ILimiter::EDecision::DISCARD; return ILimiter::EDecision::DISCARD;
auto accept = false; auto accept = false;
for (const auto & hex : stack->getHexes())
accept |= !!applicableHexes.count(hex); for (auto hex : stack->getHexes())
accept |= applicableHexes.contains(hex);
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD; return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
} }
UnitOnHexLimiter::UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes): UnitOnHexLimiter::UnitOnHexLimiter(const BattleHexArray & applicableHexes):
applicableHexes(applicableHexes) applicableHexes(applicableHexes)
{ {
} }
@ -236,8 +237,8 @@ JsonNode UnitOnHexLimiter::toJsonNode() const
JsonNode root; JsonNode root;
root["type"].String() = "UNIT_ON_HEXES"; root["type"].String() = "UNIT_ON_HEXES";
for(const auto & hex : applicableHexes) for(auto hex : applicableHexes)
root["parameters"].Vector().emplace_back(hex); root["parameters"].Vector().emplace_back(hex.toInt());
return root; return root;
} }

View File

@ -10,7 +10,7 @@
#include "Bonus.h" #include "Bonus.h"
#include "../battle/BattleHex.h" #include "../battle/BattleHexArray.h"
#include "../serializer/Serializeable.h" #include "../serializer/Serializeable.h"
#include "../constants/Enumerations.h" #include "../constants/Enumerations.h"
@ -263,9 +263,9 @@ public:
class DLL_LINKAGE UnitOnHexLimiter : public ILimiter //works only on selected hexes class DLL_LINKAGE UnitOnHexLimiter : public ILimiter //works only on selected hexes
{ {
public: public:
std::set<BattleHex> applicableHexes; BattleHexArray applicableHexes;
UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes = {}); UnitOnHexLimiter(const BattleHexArray & applicableHexes = {});
EDecision limit(const BonusLimitationContext &context) const override; EDecision limit(const BonusLimitationContext &context) const override;
JsonNode toJsonNode() const override; JsonNode toJsonNode() const override;

View File

@ -278,7 +278,7 @@ CGHeroInstance * CMap::getHero(HeroTypeID heroID)
bool CMap::isCoastalTile(const int3 & pos) const bool CMap::isCoastalTile(const int3 & pos) const
{ {
//todo: refactoring: extract neighbor tile iterator and use it in GameState //todo: refactoring: extract neighbour tile iterator and use it in GameState
static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) }; int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };

View File

@ -11,6 +11,7 @@
#include "NetPacksBase.h" #include "NetPacksBase.h"
#include "BattleChanges.h" #include "BattleChanges.h"
#include "../battle/BattleHexArray.h"
#include "../battle/BattleAction.h" #include "../battle/BattleAction.h"
#include "../texts/MetaString.h" #include "../texts/MetaString.h"
@ -170,10 +171,10 @@ struct DLL_LINKAGE BattleStackMoved : public CPackForClient
{ {
BattleID battleID = BattleID::NONE; BattleID battleID = BattleID::NONE;
ui32 stack = 0; ui32 stack = 0;
std::vector<BattleHex> tilesToMove; BattleHexArray tilesToMove;
int distance = 0; int distance = 0;
bool teleporting = false; bool teleporting = false;
void applyGs(CGameState * gs) override; void applyGs(CGameState * gs) override;
void applyBattle(IBattleState * battleState); void applyBattle(IBattleState * battleState);

View File

@ -117,6 +117,7 @@ Path Path::search(const Tileset & dst, bool straight, std::function<float(const
return; return;
float movementCost = moveCostFunction(currentNode, pos); float movementCost = moveCostFunction(currentNode, pos);
float distance = distances[currentNode] + movementCost; //we prefer to use already free paths float distance = distances[currentNode] + movementCost; //we prefer to use already free paths
int bestDistanceSoFar = std::numeric_limits<int>::max(); int bestDistanceSoFar = std::numeric_limits<int>::max();
auto it = distances.find(pos); auto it = distances.find(pos);

View File

@ -508,16 +508,16 @@ bool BattleSpellMechanics::counteringSelector(const Bonus * bonus) const
return false; return false;
} }
std::set<BattleHex> BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex) const BattleHexArray BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex) const
{ {
using namespace SRSLPraserHelpers; using namespace SRSLPraserHelpers;
std::set<BattleHex> ret; BattleHexArray ret;
std::vector<int> rng = owner->getLevelInfo(getRangeLevel()).range; std::vector<int> rng = owner->getLevelInfo(getRangeLevel()).range;
for(auto & elem : rng) for(auto & elem : rng)
{ {
std::set<ui16> curLayer = getInRange(centralHex, elem, elem); std::set<ui16> curLayer = getInRange(centralHex.toInt(), elem, elem);
//adding obtained hexes //adding obtained hexes
for(const auto & curLayer_it : curLayer) for(const auto & curLayer_it : curLayer)
ret.insert(curLayer_it); ret.insert(curLayer_it);
@ -604,14 +604,12 @@ std::vector<Destination> BattleSpellMechanics::getPossibleDestinations(size_t in
if(fast) if(fast)
{ {
auto stacks = battle()->battleGetAllStacks(); auto stacks = battle()->battleGetAllStacks();
std::set<BattleHex> hexesToCheck; BattleHexArray hexesToCheck;
for(auto stack : stacks) for(auto stack : stacks)
{ {
hexesToCheck.insert(stack->getPosition()); hexesToCheck.insert(stack->getPosition());
hexesToCheck.insert(stack->getPosition().getNeighbouringTiles());
for(auto adjacent : stack->getPosition().neighbouringTiles())
hexesToCheck.insert(adjacent);
} }
for(auto hex : hexesToCheck) for(auto hex : hexesToCheck)
@ -661,17 +659,17 @@ bool BattleSpellMechanics::isReceptive(const battle::Unit * target) const
return targetCondition->isReceptive(this, target); return targetCondition->isReceptive(this, target);
} }
std::vector<BattleHex> BattleSpellMechanics::rangeInHexes(BattleHex centralHex) const BattleHexArray BattleSpellMechanics::rangeInHexes(BattleHex centralHex) const
{ {
if(isMassive() || !centralHex.isValid()) if(isMassive() || !centralHex.isValid())
return std::vector<BattleHex>(1, BattleHex::INVALID); return BattleHexArray();
Target aimPoint; Target aimPoint;
aimPoint.push_back(Destination(centralHex)); aimPoint.push_back(Destination(centralHex));
Target spellTarget = transformSpellTarget(aimPoint); Target spellTarget = transformSpellTarget(aimPoint);
std::set<BattleHex> effectRange; BattleHexArray effectRange;
effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * effect, bool & stop) effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * effect, bool & stop)
{ {
@ -681,12 +679,7 @@ std::vector<BattleHex> BattleSpellMechanics::rangeInHexes(BattleHex centralHex)
} }
}); });
std::vector<BattleHex> ret; return effectRange;
ret.reserve(effectRange.size());
std::copy(effectRange.begin(), effectRange.end(), std::back_inserter(ret));
return ret;
} }
const Spell * BattleSpellMechanics::getSpell() const const Spell * BattleSpellMechanics::getSpell() const

View File

@ -56,7 +56,7 @@ public:
bool isReceptive(const battle::Unit * target) const override; bool isReceptive(const battle::Unit * target) const override;
/// Returns list of hexes that are affected by spell assuming cast at centralHex /// Returns list of hexes that are affected by spell assuming cast at centralHex
std::vector<BattleHex> rangeInHexes(BattleHex centralHex) const override; BattleHexArray rangeInHexes(BattleHex centralHex) const override;
const Spell * getSpell() const override; const Spell * getSpell() const override;
@ -75,7 +75,7 @@ private:
void doRemoveEffects(ServerCallback * server, const std::vector<const battle::Unit *> & targets, const CSelector & selector); void doRemoveEffects(ServerCallback * server, const std::vector<const battle::Unit *> & targets, const CSelector & selector);
std::set<BattleHex> spellRangeInHexes(BattleHex centralHex) const; BattleHexArray spellRangeInHexes(BattleHex centralHex) const;
Target transformSpellTarget(const Target & aimPoint) const; Target transformSpellTarget(const Target & aimPoint) const;
}; };

View File

@ -357,7 +357,7 @@ int32_t CSpell::getLevelPower(const int32_t skillLevel) const
si32 CSpell::getProbability(const FactionID & factionId) const si32 CSpell::getProbability(const FactionID & factionId) const
{ {
if(!vstd::contains(probabilities,factionId)) if(!vstd::contains(probabilities, factionId))
{ {
return defaultProbability; return defaultProbability;
} }
@ -701,7 +701,7 @@ const std::vector<std::string> & CSpellHandler::getTypeNames() const
std::vector<int> CSpellHandler::spellRangeInHexes(std::string input) const std::vector<int> CSpellHandler::spellRangeInHexes(std::string input) const
{ {
std::set<BattleHex> ret; BattleHexArray ret;
std::string rng = input + ','; //copy + artificial comma for easier handling std::string rng = input + ','; //copy + artificial comma for easier handling
if(rng.size() >= 2 && std::tolower(rng[0]) != 'x') //there is at least one hex in range (+artificial comma) if(rng.size() >= 2 && std::tolower(rng[0]) != 'x') //there is at least one hex in range (+artificial comma)
@ -754,7 +754,14 @@ std::vector<int> CSpellHandler::spellRangeInHexes(std::string input) const
} }
} }
return std::vector<int>(ret.begin(), ret.end()); std::vector<int> result;
result.reserve(ret.size());
std::transform(ret.begin(), ret.end(), std::back_inserter(result),
[](BattleHex hex) { return hex.toInt(); }
);
return result;
} }
std::shared_ptr<CSpell> CSpellHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) std::shared_ptr<CSpell> CSpellHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index)

View File

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

View File

@ -185,7 +185,7 @@ public:
virtual bool adaptProblem(ESpellCastProblem source, Problem & target) const = 0; virtual bool adaptProblem(ESpellCastProblem source, Problem & target) const = 0;
virtual bool adaptGenericProblem(Problem & target) const = 0; virtual bool adaptGenericProblem(Problem & target) const = 0;
virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex) const = 0; virtual BattleHexArray rangeInHexes(BattleHex centralHex) const = 0;
virtual std::vector<const CStack *> getAffectedStacks(const Target & target) const = 0; virtual std::vector<const CStack *> getAffectedStacks(const Target & target) const = 0;
virtual bool canBeCast(Problem & problem) const = 0; virtual bool canBeCast(Problem & problem) const = 0;

View File

@ -95,7 +95,7 @@ void Catapult::applyMassive(ServerCallback * server, const Mechanics * m) const
CatapultAttack::AttackInfo newInfo; CatapultAttack::AttackInfo newInfo;
newInfo.damageDealt = getRandomDamage(server); newInfo.damageDealt = getRandomDamage(server);
newInfo.attackedPart = target; newInfo.attackedPart = target;
newInfo.destinationTile = m->battle()->wallPartToBattleHex(target); newInfo.destinationTile = m->battle()->wallPartToBattleHex(target).toInt();
ca.attackedParts.push_back(newInfo); ca.attackedParts.push_back(newInfo);
attackInfo = ca.attackedParts.end() - 1; attackInfo = ca.attackedParts.end() - 1;
} }
@ -137,7 +137,7 @@ void Catapult::applyTargeted(ServerCallback * server, const Mechanics * m, const
CatapultAttack::AttackInfo attack; CatapultAttack::AttackInfo attack;
attack.attackedPart = actualTarget; attack.attackedPart = actualTarget;
attack.destinationTile = m->battle()->wallPartToBattleHex(actualTarget); attack.destinationTile = m->battle()->wallPartToBattleHex(actualTarget).toInt();
attack.damageDealt = getRandomDamage(server); attack.damageDealt = getRandomDamage(server);
CatapultAttack ca; //package for clients CatapultAttack ca; //package for clients

View File

@ -43,7 +43,7 @@ void Clone::apply(ServerCallback * server, const Mechanics * m, const EffectTarg
if(clonedStack->getCount() < 1) if(clonedStack->getCount() < 1)
continue; continue;
auto hex = m->battle()->getAvailableHex(clonedStack->creatureId(), m->casterSide, clonedStack->getPosition()); auto hex = m->battle()->getAvailableHex(clonedStack->creatureId(), m->casterSide, clonedStack->getPosition().toInt());
if(!hex.isValid()) if(!hex.isValid())
{ {

View File

@ -65,7 +65,7 @@ void DemonSummon::apply(ServerCallback * server, const Mechanics * m, const Effe
continue; continue;
} }
auto hex = m->battle()->getAvailableHex(targetStack->creatureId(), m->casterSide, targetStack->getPosition()); auto hex = m->battle()->getAvailableHex(targetStack->creatureId(), m->casterSide, targetStack->getPosition().toInt());
if(!hex.isValid()) if(!hex.isValid())
{ {

View File

@ -14,7 +14,8 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex; class BattleHex;
class BattleHexArray;
class CBattleInfoCallback; class CBattleInfoCallback;
class JsonSerializeFormat; class JsonSerializeFormat;
class ServerCallback; class ServerCallback;
@ -51,7 +52,7 @@ public:
virtual void adjustTargetTypes(std::vector<TargetType> & types) const = 0; virtual void adjustTargetTypes(std::vector<TargetType> & types) const = 0;
/// Generates list of hexes affected by spell, if spell were to cast at specified target /// Generates list of hexes affected by spell, if spell were to cast at specified target
virtual void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const = 0; virtual void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const = 0;
/// Returns whether effect has any valid targets on the battlefield /// Returns whether effect has any valid targets on the battlefield
virtual bool applicable(Problem & problem, const Mechanics * m) const; virtual bool applicable(Problem & problem, const Mechanics * m) const;

View File

@ -11,6 +11,7 @@
#include "LocationEffect.h" #include "LocationEffect.h"
#include "../ISpellMechanics.h" #include "../ISpellMechanics.h"
#include "battle/BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -24,7 +25,7 @@ void LocationEffect::adjustTargetTypes(std::vector<TargetType> & types) const
} }
void LocationEffect::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const void LocationEffect::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{ {
for(const auto & destnation : spellTarget) for(const auto & destnation : spellTarget)
hexes.insert(destnation.hexValue); hexes.insert(destnation.hexValue);

View File

@ -25,7 +25,7 @@ class LocationEffect : public Effect
public: public:
void adjustTargetTypes(std::vector<TargetType> & types) const override; void adjustTargetTypes(std::vector<TargetType> & types) const override;
void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const override; void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override;
EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override;

View File

@ -32,7 +32,7 @@ namespace spells
namespace effects namespace effects
{ {
static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string & fieldName, std::vector<std::vector<BattleHex>> & moatHexes) static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string & fieldName, std::vector<BattleHexArray> & moatHexes)
{ {
{ {
JsonArraySerializer outer = handler.enterArray(fieldName); JsonArraySerializer outer = handler.enterArray(fieldName);
@ -44,7 +44,11 @@ static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string
inner.syncSize(moatHexes.at(outerIndex), JsonNode::JsonType::DATA_INTEGER); inner.syncSize(moatHexes.at(outerIndex), JsonNode::JsonType::DATA_INTEGER);
for(size_t innerIndex = 0; innerIndex < inner.size(); innerIndex++) for(size_t innerIndex = 0; innerIndex < inner.size(); innerIndex++)
inner.serializeInt(innerIndex, moatHexes.at(outerIndex).at(innerIndex)); {
si16 hex = moatHexes.at(outerIndex).at(innerIndex).toInt();
inner.serializeInt(innerIndex, hex);
moatHexes.at(outerIndex).set(innerIndex, hex);
}
} }
} }
} }
@ -96,10 +100,10 @@ void Moat::convertBonus(const Mechanics * m, std::vector<Bonus> & converted) con
nb.sid = BonusSourceID(m->getSpellId()); //for all nb.sid = BonusSourceID(m->getSpellId()); //for all
nb.source = BonusSource::SPELL_EFFECT;//for all nb.source = BonusSource::SPELL_EFFECT;//for all
} }
std::set<BattleHex> flatMoatHexes; BattleHexArray flatMoatHexes;
for(const auto & moatPatch : moatHexes) for(const auto & moatPatch : moatHexes)
flatMoatHexes.insert(moatPatch.begin(), moatPatch.end()); flatMoatHexes.insert(moatPatch);
nb.limiter = std::make_shared<UnitOnHexLimiter>(std::move(flatMoatHexes)); nb.limiter = std::make_shared<UnitOnHexLimiter>(std::move(flatMoatHexes));
converted.push_back(nb); converted.push_back(nb);
@ -164,7 +168,7 @@ void Moat::placeObstacles(ServerCallback * server, const Mechanics * m, const Ef
obstacle.appearSound = sideOptions.appearSound; //For dispellable moats obstacle.appearSound = sideOptions.appearSound; //For dispellable moats
obstacle.appearAnimation = sideOptions.appearAnimation; //For dispellable moats obstacle.appearAnimation = sideOptions.appearAnimation; //For dispellable moats
obstacle.animation = sideOptions.animation; obstacle.animation = sideOptions.animation;
obstacle.customSize.insert(obstacle.customSize.end(),destination.cbegin(), destination.cend()); obstacle.customSize.insert(destination);
obstacle.animationYOffset = sideOptions.offsetY; obstacle.animationYOffset = sideOptions.offsetY;
pack.changes.emplace_back(); pack.changes.emplace_back();
obstacle.toInfo(pack.changes.back()); obstacle.toInfo(pack.changes.back());

View File

@ -25,7 +25,7 @@ class Moat : public Obstacle
{ {
private: private:
ObstacleSideOptions sideOptions; //Defender only ObstacleSideOptions sideOptions; //Defender only
std::vector<std::vector<BattleHex>> moatHexes; //Determine number of moat patches and hexes std::vector<BattleHexArray> moatHexes; //Determine number of moat patches and hexes
std::vector<std::shared_ptr<Bonus>> bonus; //For battle-wide bonuses std::vector<std::shared_ptr<Bonus>> bonus; //For battle-wide bonuses
bool dispellable; //For Tower landmines bool dispellable; //For Tower landmines
int moatDamage; // Minimal moat damage int moatDamage; // Minimal moat damage

View File

@ -95,7 +95,7 @@ void ObstacleSideOptions::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("offsetY", offsetY); handler.serializeInt("offsetY", offsetY);
} }
void Obstacle::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const void Obstacle::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{ {
EffectTarget effectTarget = transformTarget(m, spellTarget, spellTarget); EffectTarget effectTarget = transformTarget(m, spellTarget, spellTarget);
@ -180,11 +180,11 @@ void Obstacle::apply(ServerCallback * server, const Mechanics * m, const EffectT
{ {
if(patchCount > 0) if(patchCount > 0)
{ {
std::vector<BattleHex> availableTiles; BattleHexArray availableTiles;
auto insertAvailable = [&m](const BattleHex & hex, std::vector<BattleHex> & availableTiles) auto insertAvailable = [&m](const BattleHex & hex, BattleHexArray & availableTiles)
{ {
if(isHexAvailable(m->battle(), hex, true)) if(isHexAvailable(m->battle(), hex, true))
availableTiles.push_back(hex); availableTiles.insert(hex);
}; };
if(m->isMassive()) if(m->isMassive())
@ -309,7 +309,6 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
obstacle.animationYOffset = options.offsetY; obstacle.animationYOffset = options.offsetY;
obstacle.customSize.clear(); obstacle.customSize.clear();
obstacle.customSize.reserve(options.shape.size());
for(const auto & shape : options.shape) for(const auto & shape : options.shape)
{ {
@ -318,7 +317,7 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
for(auto direction : shape) for(auto direction : shape)
hex.moveInDirection(direction, false); hex.moveInDirection(direction, false);
obstacle.customSize.emplace_back(hex); obstacle.customSize.insert(hex);
} }
pack.changes.emplace_back(); pack.changes.emplace_back();

View File

@ -12,7 +12,7 @@
#include "LocationEffect.h" #include "LocationEffect.h"
#include "../../GameConstants.h" #include "../../GameConstants.h"
#include "../../battle/BattleHex.h" #include "../../battle/BattleHexArray.h"
#include "../../battle/CObstacleInstance.h" #include "../../battle/CObstacleInstance.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -42,7 +42,7 @@ public:
class Obstacle : public LocationEffect class Obstacle : public LocationEffect
{ {
public: public:
void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const override; void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override;
bool applicable(Problem & problem, const Mechanics * m) const override; bool applicable(Problem & problem, const Mechanics * m) const override;
bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override;

View File

@ -28,7 +28,7 @@ namespace spells
namespace effects namespace effects
{ {
void Summon::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const void Summon::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{ {
//no hexes affected //no hexes affected
} }
@ -207,4 +207,4 @@ EffectTarget Summon::transformTarget(const Mechanics * m, const Target & aimPoin
} }
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -23,7 +23,7 @@ namespace effects
class Summon : public Effect class Summon : public Effect
{ {
public: public:
void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const override; void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override;
void adjustTargetTypes(std::vector<TargetType> & types) const override; void adjustTargetTypes(std::vector<TargetType> & types) const override;
bool applicable(Problem & problem, const Mechanics * m) const override; bool applicable(Problem & problem, const Mechanics * m) const override;

View File

@ -81,8 +81,8 @@ void Teleport::apply(ServerCallback * server, const Mechanics * m, const EffectT
pack.battleID = m->battle()->getBattle()->getBattleID(); pack.battleID = m->battle()->getBattle()->getBattleID();
pack.distance = 0; pack.distance = 0;
pack.stack = targetUnit->unitId(); pack.stack = targetUnit->unitId();
std::vector<BattleHex> tiles; BattleHexArray tiles;
tiles.push_back(destination); tiles.insert(destination);
pack.tilesToMove = tiles; pack.tilesToMove = tiles;
pack.teleporting = true; pack.teleporting = true;
server->apply(pack); server->apply(pack);

View File

@ -30,7 +30,7 @@ void UnitEffect::adjustTargetTypes(std::vector<TargetType> & types) const
} }
void UnitEffect::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const void UnitEffect::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{ {
for(const auto & destnation : spellTarget) for(const auto & destnation : spellTarget)
hexes.insert(destnation.hexValue); hexes.insert(destnation.hexValue);
@ -193,7 +193,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe
return EffectTarget(); return EffectTarget();
} }
std::set<BattleHex> possibleHexes; BattleHexArray possibleHexes;
auto possibleTargets = m->battle()->battleGetUnitsIf([&](const battle::Unit * unit) -> bool auto possibleTargets = m->battle()->battleGetUnitsIf([&](const battle::Unit * unit) -> bool
{ {
@ -223,7 +223,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe
effectTarget.emplace_back(); effectTarget.emplace_back();
for(auto hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide())) for(auto hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide()))
possibleHexes.erase(hex); possibleHexes.erase(hex.toInt());
if(possibleHexes.empty()) if(possibleHexes.empty())
break; break;
@ -278,4 +278,4 @@ void UnitEffect::serializeJsonEffect(JsonSerializeFormat & handler)
} }
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -24,7 +24,7 @@ class UnitEffect : public Effect
public: public:
void adjustTargetTypes(std::vector<TargetType> & types) const override; void adjustTargetTypes(std::vector<TargetType> & types) const override;
void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const override; void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override;
bool applicable(Problem & problem, const Mechanics * m) const override; bool applicable(Problem & problem, const Mechanics * m) const override;
bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override;

View File

@ -58,7 +58,7 @@ void LuaSpellEffect::adjustTargetTypes(std::vector<TargetType> & types) const
} }
void LuaSpellEffect::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const void LuaSpellEffect::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{ {
} }
@ -98,7 +98,7 @@ bool LuaSpellEffect::applicable(Problem & problem, const Mechanics * m, const Ef
for(const auto & dest : target) for(const auto & dest : target)
{ {
JsonNode targetData; JsonNode targetData;
targetData.Vector().emplace_back(dest.hexValue.hex); targetData.Vector().emplace_back(dest.hexValue.toInt());
if(dest.unitValue) if(dest.unitValue)
targetData.Vector().emplace_back(dest.unitValue->unitId()); targetData.Vector().emplace_back(dest.unitValue->unitId());
@ -141,7 +141,7 @@ void LuaSpellEffect::apply(ServerCallback * server, const Mechanics * m, const E
for(const auto & dest : target) for(const auto & dest : target)
{ {
JsonNode targetData; JsonNode targetData;
targetData.Vector().emplace_back(dest.hexValue.hex); targetData.Vector().emplace_back(dest.hexValue.toInt());
if(dest.unitValue) if(dest.unitValue)
targetData.Vector().emplace_back(dest.unitValue->unitId()); targetData.Vector().emplace_back(dest.unitValue->unitId());

View File

@ -49,7 +49,7 @@ public:
void adjustTargetTypes(std::vector<TargetType> & types) const override; void adjustTargetTypes(std::vector<TargetType> & types) const override;
void adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const override; void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override;
bool applicable(Problem & problem, const Mechanics * m) const override; bool applicable(Problem & problem, const Mechanics * m) const override;
bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override;

View File

@ -99,11 +99,13 @@ int BattleCbProxy::getUnitByPos(lua_State * L)
if(!S.tryGet(1, object)) if(!S.tryGet(1, object))
return S.retVoid(); return S.retVoid();
BattleHex hex; si16 hexVal;
if(!S.tryGet(2, hex.hex)) if(!S.tryGet(2, hexVal))
return S.retNil(); return S.retNil();
BattleHex hex(hexVal);
bool onlyAlive; bool onlyAlive;
if(!S.tryGet(3, onlyAlive)) if(!S.tryGet(3, onlyAlive))

View File

@ -47,7 +47,7 @@ int BattleStackMovedProxy::addTileToMove(lua_State * L)
lua_Integer hex = 0; lua_Integer hex = 0;
if(!S.tryGetInteger(2, hex)) if(!S.tryGetInteger(2, hex))
return S.retVoid(); return S.retVoid();
object->tilesToMove.emplace_back(hex); object->tilesToMove.insert(hex);
return S.retVoid(); return S.retVoid();
} }

View File

@ -635,7 +635,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
//initing necessary tables //initing necessary tables
auto accessibility = battle.getAccessibility(curStack); auto accessibility = battle.getAccessibility(curStack);
std::set<BattleHex> passed; BattleHexArray passed;
//Ignore obstacles on starting position //Ignore obstacles on starting position
passed.insert(curStack->getPosition()); passed.insert(curStack->getPosition());
if(curStack->doubleWide()) if(curStack->doubleWide())
@ -665,7 +665,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
canUseGate = true; canUseGate = true;
} }
std::pair< std::vector<BattleHex>, int > path = battle.getPath(start, dest, curStack); std::pair< BattleHexArray, int > path = battle.getPath(start, dest, curStack);
ret = path.second; ret = path.second;
@ -723,8 +723,8 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
BattleStackMoved sm; BattleStackMoved sm;
sm.battleID = battle.getBattle()->getBattleID(); sm.battleID = battle.getBattle()->getBattleID();
sm.stack = curStack->unitId(); sm.stack = curStack->unitId();
std::vector<BattleHex> tiles; BattleHexArray tiles;
tiles.push_back(path.first[0]); tiles.insert(path.first[0]);
sm.tilesToMove = tiles; sm.tilesToMove = tiles;
sm.distance = path.second; sm.distance = path.second;
sm.teleporting = false; sm.teleporting = false;
@ -733,10 +733,10 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
} }
else //for non-flying creatures else //for non-flying creatures
{ {
std::vector<BattleHex> tiles; BattleHexArray tiles;
const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0); const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0);
int v = (int)path.first.size()-1; int v = (int)path.first.size()-1;
path.first.push_back(start); path.first.insert(start);
// check if gate need to be open or closed at some point // check if gate need to be open or closed at some point
BattleHex openGateAtHex, gateMayCloseAtHex; BattleHex openGateAtHex, gateMayCloseAtHex;
@ -822,7 +822,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
for (bool obstacleHit = false; (!obstacleHit) && (!gateStateChanging) && (v >= tilesToMove); --v) for (bool obstacleHit = false; (!obstacleHit) && (!gateStateChanging) && (v >= tilesToMove); --v)
{ {
BattleHex hex = path.first[v]; BattleHex hex = path.first[v];
tiles.push_back(hex); tiles.insert(hex);
if ((openGateAtHex.isValid() && openGateAtHex == hex) || if ((openGateAtHex.isValid() && openGateAtHex == hex) ||
(gateMayCloseAtHex.isValid() && gateMayCloseAtHex == hex)) (gateMayCloseAtHex.isValid() && gateMayCloseAtHex == hex))

View File

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

View File

@ -36,7 +36,7 @@ BattleFlowProcessor::BattleFlowProcessor(BattleProcessor * owner, CGameHandler *
{ {
} }
void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, BattleHexArray & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
{ {
int x = targetPosition.getX(); int x = targetPosition.getX();
int y = targetPosition.getY(); int y = targetPosition.getY();
@ -44,31 +44,31 @@ void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & batt
const bool targetIsAttacker = side == BattleSide::ATTACKER; const bool targetIsAttacker = side == BattleSide::ATTACKER;
if (targetIsAttacker) //handle front guardians, TODO: should we handle situation when units start battle near opposite side of the battlefield? Cannot happen in normal H3... if (targetIsAttacker) //handle front guardians, TODO: should we handle situation when units start battle near opposite side of the battlefield? Cannot happen in normal H3...
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false));
else else
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false));
//guardian spawn locations for four default position cases for attacker and defender, non-default starting location for att and def is handled in first two if's //guardian spawn locations for four default position cases for attacker and defender, non-default starting location for att and def is handled in first two if's
if (targetIsAttacker && ((y % 2 == 0) || (x > 1))) if (targetIsAttacker && ((y % 2 == 0) || (x > 1)))
{ {
if (targetIsTwoHex && (y % 2 == 1) && (x == 2)) //handle exceptional case if (targetIsTwoHex && (y % 2 == 1) && (x == 2)) //handle exceptional case
{ {
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
} }
else else
{ //add back-side guardians for two-hex target, side guardians for one-hex { //add back-side guardians for two-hex target, side guardians for one-hex
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_LEFT : BattleHex::EDir::TOP_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_LEFT : BattleHex::EDir::TOP_RIGHT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_LEFT : BattleHex::EDir::BOTTOM_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_LEFT : BattleHex::EDir::BOTTOM_RIGHT, false));
if (!targetIsTwoHex && x > 2) //back guard for one-hex if (!targetIsTwoHex && x > 2) //back guard for one-hex
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false));
else if (targetIsTwoHex)//front-side guardians for two-hex target else if (targetIsTwoHex)//front-side guardians for two-hex target
{ {
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
if (x > 3) //back guard for two-hex if (x > 3) //back guard for two-hex
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false));
} }
} }
@ -78,36 +78,36 @@ void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & batt
{ {
if (targetIsTwoHex && (y % 2 == 0) && (x == GameConstants::BFIELD_WIDTH - 3)) //handle exceptional case... equivalent for above for defender side if (targetIsTwoHex && (y % 2 == 0) && (x == GameConstants::BFIELD_WIDTH - 3)) //handle exceptional case... equivalent for above for defender side
{ {
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
} }
else else
{ {
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_RIGHT : BattleHex::EDir::TOP_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_RIGHT : BattleHex::EDir::TOP_LEFT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_RIGHT : BattleHex::EDir::BOTTOM_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_RIGHT : BattleHex::EDir::BOTTOM_LEFT, false));
if (!targetIsTwoHex && x < GameConstants::BFIELD_WIDTH - 3) if (!targetIsTwoHex && x < GameConstants::BFIELD_WIDTH - 3)
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false));
else if (targetIsTwoHex) else if (targetIsTwoHex)
{ {
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
if (x < GameConstants::BFIELD_WIDTH - 4) if (x < GameConstants::BFIELD_WIDTH - 4)
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false));
} }
} }
} }
else if (!targetIsAttacker && y % 2 == 0) else if (!targetIsAttacker && y % 2 == 0)
{ {
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
} }
else if (targetIsAttacker && y % 2 == 1) else if (targetIsAttacker && y % 2 == 1)
{ {
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false));
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false), output); output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
} }
} }
@ -150,7 +150,7 @@ void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle,
std::shared_ptr<const Bonus> summonInfo = stack->getBonus(Selector::type()(BonusType::SUMMON_GUARDIANS)); std::shared_ptr<const Bonus> summonInfo = stack->getBonus(Selector::type()(BonusType::SUMMON_GUARDIANS));
auto accessibility = battle.getAccessibility(); auto accessibility = battle.getAccessibility();
CreatureID creatureData = summonInfo->subtype.as<CreatureID>(); CreatureID creatureData = summonInfo->subtype.as<CreatureID>();
std::vector<BattleHex> targetHexes; BattleHexArray targetHexes;
const bool targetIsBig = stack->unitType()->isDoubleWide(); //target = creature to guard const bool targetIsBig = stack->unitType()->isDoubleWide(); //target = creature to guard
const bool guardianIsBig = creatureData.toCreature()->isDoubleWide(); const bool guardianIsBig = creatureData.toCreature()->isDoubleWide();

View File

@ -13,7 +13,8 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CStack; class CStack;
struct BattleHex; class BattleHex;
class BattleHexArray;
class BattleAction; class BattleAction;
class CBattleInfoCallback; class CBattleInfoCallback;
struct CObstacleInstance; struct CObstacleInstance;
@ -37,7 +38,7 @@ class BattleFlowProcessor : boost::noncopyable
bool rollGoodMorale(const CBattleInfoCallback & battle, const CStack * stack); bool rollGoodMorale(const CBattleInfoCallback & battle, const CStack * stack);
bool tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack); bool tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack);
void summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex); void summonGuardiansHelper(const CBattleInfoCallback & battle, BattleHexArray & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex);
void trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack); void trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack);
void tryPlaceMoats(const CBattleInfoCallback & battle); void tryPlaceMoats(const CBattleInfoCallback & battle);
void castOpeningSpells(const CBattleInfoCallback & battle); void castOpeningSpells(const CBattleInfoCallback & battle);

View File

@ -9,29 +9,29 @@
*/ */
#include "StdInc.h" #include "StdInc.h"
#include "../lib/battle/BattleHex.h" #include "../lib/battle/BattleHexArray.h"
TEST(BattleHexTest, getNeighbouringTiles) TEST(BattleHexTest, getNeighbouringTiles)
{ {
BattleHex mainHex; BattleHex mainHex;
std::vector<BattleHex> neighbouringTiles; BattleHexArray neighbouringTiles;
mainHex.setXY(16,0); mainHex.setXY(16,0);
neighbouringTiles = mainHex.neighbouringTiles(); neighbouringTiles = mainHex.getNeighbouringTiles();
EXPECT_EQ(neighbouringTiles.size(), 1); EXPECT_EQ(neighbouringTiles.size(), 1);
mainHex.setXY(0,0); mainHex.setXY(0,0);
neighbouringTiles = mainHex.neighbouringTiles(); neighbouringTiles = mainHex.getNeighbouringTiles();
EXPECT_EQ(neighbouringTiles.size(), 2); EXPECT_EQ(neighbouringTiles.size(), 2);
mainHex.setXY(15,2); mainHex.setXY(15,2);
neighbouringTiles = mainHex.neighbouringTiles(); neighbouringTiles = mainHex.getNeighbouringTiles();
EXPECT_EQ(neighbouringTiles.size(), 3); EXPECT_EQ(neighbouringTiles.size(), 3);
mainHex.setXY(2,0); mainHex.setXY(2,0);
neighbouringTiles = mainHex.neighbouringTiles(); neighbouringTiles = mainHex.getNeighbouringTiles();
EXPECT_EQ(neighbouringTiles.size(), 4); EXPECT_EQ(neighbouringTiles.size(), 4);
mainHex.setXY(1,2); mainHex.setXY(1,2);
neighbouringTiles = mainHex.neighbouringTiles(); neighbouringTiles = mainHex.getNeighbouringTiles();
EXPECT_EQ(neighbouringTiles.size(), 5); EXPECT_EQ(neighbouringTiles.size(), 5);
mainHex.setXY(8,5); mainHex.setXY(8,5);
neighbouringTiles = mainHex.neighbouringTiles(); neighbouringTiles = mainHex.getNeighbouringTiles();
EXPECT_EQ(neighbouringTiles.size(), 6); EXPECT_EQ(neighbouringTiles.size(), 6);
ASSERT_TRUE(neighbouringTiles.size()==6 && mainHex==93); ASSERT_TRUE(neighbouringTiles.size()==6 && mainHex==93);
@ -85,7 +85,7 @@ TEST(BattleHexTest, mutualPositions)
TEST(BattleHexTest, getClosestTile) TEST(BattleHexTest, getClosestTile)
{ {
BattleHex mainHex(0); BattleHex mainHex(0);
std::set<BattleHex> possibilities; BattleHexArray possibilities;
possibilities.insert(3); possibilities.insert(3);
possibilities.insert(170); possibilities.insert(170);
possibilities.insert(100); possibilities.insert(100);

View File

@ -17,7 +17,7 @@ TEST(battle_Unit_getSurroundingHexes, oneWide)
auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER); auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER);
EXPECT_EQ(actual, position.neighbouringTiles()); EXPECT_EQ(actual, position.getNeighbouringTiles());
} }
TEST(battle_Unit_getSurroundingHexes, oneWideLeftCorner) TEST(battle_Unit_getSurroundingHexes, oneWideLeftCorner)
@ -26,7 +26,7 @@ TEST(battle_Unit_getSurroundingHexes, oneWideLeftCorner)
auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER); auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER);
EXPECT_EQ(actual, position.neighbouringTiles()); EXPECT_EQ(actual, position.getNeighbouringTiles());
} }
TEST(battle_Unit_getSurroundingHexes, oneWideRightCorner) TEST(battle_Unit_getSurroundingHexes, oneWideRightCorner)
@ -35,7 +35,7 @@ TEST(battle_Unit_getSurroundingHexes, oneWideRightCorner)
auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER); auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER);
EXPECT_EQ(actual, position.neighbouringTiles()); EXPECT_EQ(actual, position.getNeighbouringTiles());
} }
TEST(battle_Unit_getSurroundingHexes, doubleWideAttacker) TEST(battle_Unit_getSurroundingHexes, doubleWideAttacker)
@ -44,7 +44,7 @@ TEST(battle_Unit_getSurroundingHexes, doubleWideAttacker)
auto actual = battle::Unit::getSurroundingHexes(position, true, BattleSide::ATTACKER); auto actual = battle::Unit::getSurroundingHexes(position, true, BattleSide::ATTACKER);
static const std::vector<BattleHex> expected = static const BattleHexArray expected =
{ {
60, 60,
61, 61,
@ -65,7 +65,7 @@ TEST(battle_Unit_getSurroundingHexes, doubleWideLeftCorner)
auto actualAtt = battle::Unit::getSurroundingHexes(position, true, BattleSide::ATTACKER); auto actualAtt = battle::Unit::getSurroundingHexes(position, true, BattleSide::ATTACKER);
static const std::vector<BattleHex> expectedAtt = static const BattleHexArray expectedAtt =
{ {
35, 35,
53, 53,
@ -76,7 +76,7 @@ TEST(battle_Unit_getSurroundingHexes, doubleWideLeftCorner)
auto actualDef = battle::Unit::getSurroundingHexes(position, true, BattleSide::DEFENDER); auto actualDef = battle::Unit::getSurroundingHexes(position, true, BattleSide::DEFENDER);
static const std::vector<BattleHex> expectedDef = static const BattleHexArray expectedDef =
{ {
35, 35,
36, 36,
@ -94,7 +94,7 @@ TEST(battle_Unit_getSurroundingHexes, doubleWideRightCorner)
auto actualAtt = battle::Unit::getSurroundingHexes(position, true, BattleSide::ATTACKER); auto actualAtt = battle::Unit::getSurroundingHexes(position, true, BattleSide::ATTACKER);
static const std::vector<BattleHex> expectedAtt = static const BattleHexArray expectedAtt =
{ {
116, 116,
117, 117,
@ -109,7 +109,7 @@ TEST(battle_Unit_getSurroundingHexes, doubleWideRightCorner)
auto actualDef = battle::Unit::getSurroundingHexes(position, true, BattleSide::DEFENDER); auto actualDef = battle::Unit::getSurroundingHexes(position, true, BattleSide::DEFENDER);
static const std::vector<BattleHex> expectedDef = static const BattleHexArray expectedDef =
{ {
116, 116,
117, 117,
@ -128,7 +128,7 @@ TEST(battle_Unit_getSurroundingHexes, doubleWideDefender)
auto actual = battle::Unit::getSurroundingHexes(position, true, BattleSide::DEFENDER); auto actual = battle::Unit::getSurroundingHexes(position, true, BattleSide::DEFENDER);
static const std::vector<BattleHex> expected = static const BattleHexArray expected =
{ {
60, 60,
61, 61,

View File

@ -40,7 +40,7 @@ public:
MOCK_CONST_METHOD0(getPlayerID, std::optional<PlayerColor>()); MOCK_CONST_METHOD0(getPlayerID, std::optional<PlayerColor>());
MOCK_CONST_METHOD2(battleGetAllObstaclesOnPos, std::vector<std::shared_ptr<const CObstacleInstance>>(BattleHex, bool)); MOCK_CONST_METHOD2(battleGetAllObstaclesOnPos, std::vector<std::shared_ptr<const CObstacleInstance>>(BattleHex, bool));
MOCK_CONST_METHOD2(getAllAffectedObstaclesByStack, std::vector<std::shared_ptr<const CObstacleInstance>>(const battle::Unit *, const std::set<BattleHex> &)); MOCK_CONST_METHOD2(getAllAffectedObstaclesByStack, std::vector<std::shared_ptr<const CObstacleInstance>>(const battle::Unit *, const BattleHexArray &));
}; };

View File

@ -12,6 +12,7 @@
#include "../../lib/spells/ISpellMechanics.h" #include "../../lib/spells/ISpellMechanics.h"
#include "../../lib/CGameInfoCallback.h" #include "../../lib/CGameInfoCallback.h"
#include "../../lib/battle/BattleHexArray.h"
namespace spells namespace spells
{ {
@ -22,7 +23,7 @@ public:
MOCK_CONST_METHOD2(adaptProblem, bool(ESpellCastProblem, Problem &)); MOCK_CONST_METHOD2(adaptProblem, bool(ESpellCastProblem, Problem &));
MOCK_CONST_METHOD1(adaptGenericProblem, bool(Problem &)); MOCK_CONST_METHOD1(adaptGenericProblem, bool(Problem &));
MOCK_CONST_METHOD1(rangeInHexes, std::vector<BattleHex>(BattleHex)); MOCK_CONST_METHOD1(rangeInHexes, BattleHexArray(BattleHex));
MOCK_CONST_METHOD1(getAffectedStacks, std::vector<const CStack *>(const Target &)); MOCK_CONST_METHOD1(getAffectedStacks, std::vector<const CStack *>(const Target &));
MOCK_CONST_METHOD1(canBeCast, bool(Problem &)); MOCK_CONST_METHOD1(canBeCast, bool(Problem &));

View File

@ -84,7 +84,7 @@ TEST_F(LuaSpellEffectAPITest, DISABLED_ApplicableOnLeftSideOfField)
BattleHex hex(2,2); BattleHex hex(2,2);
JsonNode first; JsonNode first;
first.Vector().emplace_back(hex.hex); first.Vector().emplace_back(hex.toInt());
first.Vector().emplace_back(); first.Vector().emplace_back();
JsonNode targets; JsonNode targets;
@ -113,7 +113,7 @@ TEST_F(LuaSpellEffectAPITest, DISABLED_NotApplicableOnRightSideOfField)
BattleHex hex(11,2); BattleHex hex(11,2);
JsonNode first; JsonNode first;
first.Vector().emplace_back(hex.hex); first.Vector().emplace_back(hex.toInt());
first.Vector().emplace_back(-1); first.Vector().emplace_back(-1);
JsonNode targets; JsonNode targets;
@ -138,13 +138,13 @@ TEST_F(LuaSpellEffectAPITest, DISABLED_ApplyMoveUnit)
BattleHex hex1(11,2); BattleHex hex1(11,2);
JsonNode unit; JsonNode unit;
unit.Vector().emplace_back(hex1.hex); unit.Vector().emplace_back(hex1.toInt());
unit.Vector().emplace_back(42); unit.Vector().emplace_back(42);
BattleHex hex2(5,4); BattleHex hex2(5,4);
JsonNode destination; JsonNode destination;
destination.Vector().emplace_back(hex2.hex); destination.Vector().emplace_back(hex2.toInt());
destination.Vector().emplace_back(-1); destination.Vector().emplace_back(-1);
JsonNode targets; JsonNode targets;
@ -163,7 +163,7 @@ TEST_F(LuaSpellEffectAPITest, DISABLED_ApplyMoveUnit)
EXPECT_EQ(pack.teleporting, true); EXPECT_EQ(pack.teleporting, true);
EXPECT_EQ(pack.distance, 0); EXPECT_EQ(pack.distance, 0);
std::vector<BattleHex> toMove(1, hex2); BattleHexArray toMove = { hex2 };
EXPECT_EQ(pack.tilesToMove, toMove); EXPECT_EQ(pack.tilesToMove, toMove);
}; };

View File

@ -154,11 +154,11 @@ TEST_F(LuaSpellEffectTest, ApplicableTargetRedirected)
JsonNode first; JsonNode first;
first.Vector().emplace_back(hex1.hex); first.Vector().emplace_back(hex1.toInt());
first.Vector().emplace_back(id1); first.Vector().emplace_back(id1);
JsonNode second; JsonNode second;
second.Vector().emplace_back(hex2.hex); second.Vector().emplace_back(hex2.toInt());
second.Vector().emplace_back(-1); second.Vector().emplace_back(-1);
JsonNode targets; JsonNode targets;
@ -193,7 +193,7 @@ TEST_F(LuaSpellEffectTest, ApplyRedirected)
subject->apply(&serverMock, &mechanicsMock, target); subject->apply(&serverMock, &mechanicsMock, target);
JsonNode first; JsonNode first;
first.Vector().emplace_back(hex1.hex); first.Vector().emplace_back(hex1.toInt());
first.Vector().emplace_back(id1); first.Vector().emplace_back(id1);
JsonNode targets; JsonNode targets;