1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-19 21:10:12 +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
commit a44bbf4527
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
91 changed files with 1271 additions and 836 deletions

View File

@ -280,7 +280,7 @@ int64_t AttackPossibility::evaluateBlockedShootersDmg(
std::set<uint32_t> checkedUnits;
auto attacker = attackInfo.attacker;
auto hexes = attacker->getSurroundingHexes(hex);
const auto & hexes = attacker->getSurroundingHexes(hex);
for(BattleHex tile : hexes)
{
auto st = state->battleGetUnitByPos(tile, true);
@ -326,9 +326,9 @@ AttackPossibility AttackPossibility::evaluate(
AttackPossibility bestAp(hex, BattleHex::INVALID, attackInfo);
std::vector<BattleHex> defenderHex;
BattleHexArray defenderHex;
if(attackInfo.shooting)
defenderHex.push_back(defender->getPosition());
defenderHex.insert(defender->getPosition());
else
defenderHex = CStack::meleeAttackHexes(attacker, defender, hex);
@ -384,7 +384,7 @@ AttackPossibility AttackPossibility::evaluate(
affectedUnits = defenderUnits;
vstd::concatenate(affectedUnits, retaliatedUnits);
logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex.hex, defHex.hex);
logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex, defHex);
std::map<uint32_t, std::shared_ptr<battle::CUnitState>> defenderStates;

View File

@ -88,7 +88,7 @@ public:
//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 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 battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;

View File

@ -214,8 +214,8 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
bestAttack.attackerState->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->getCount(),
(int)bestAttack.from,
(int)bestAttack.attack.attacker->getPosition().hex,
bestAttack.from.toInt(),
bestAttack.attack.attacker->getPosition().toInt(),
bestAttack.attack.chargeDistance,
bestAttack.attack.attacker->getMovementRange(0),
bestAttack.defenderDamageReduce,
@ -252,7 +252,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
if(siegeDefense)
{
logAi->trace("Evaluating exchange at %d self-defense", stack->getPosition().hex);
logAi->trace("Evaluating exchange at %d self-defense", stack->getPosition());
BattleAttackInfo bai(stack, stack, 0, false);
AttackPossibility apDefend(stack->getPosition(), stack->getPosition(), bai);
@ -278,7 +278,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
score = moveTarget.score;
cachedAttack.ap = moveTarget.cachedAttack;
cachedAttack.score = score;
cachedAttack.turn = moveTarget.turnsToRich;
cachedAttack.turn = moveTarget.turnsToReach;
if(stack->waited())
{
@ -286,7 +286,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
"Moving %s towards hex %s[%d], score: %2f",
stack->getDescription(),
moveTarget.cachedAttack->attack.defender->getDescription(),
moveTarget.cachedAttack->attack.defender->getPosition().hex,
moveTarget.cachedAttack->attack.defender->getPosition(),
moveTarget.score);
return goTowardsNearest(stack, moveTarget.positions, *targets);
@ -320,9 +320,9 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * 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();
}
@ -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 avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
@ -381,20 +381,20 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
return BattleAction::makeDefend(stack);
}
std::vector<BattleHex> targetHexes = hexes;
BattleHexArray targetHexes = hexes;
vstd::erase_if(targetHexes, [](const BattleHex & hex) { return !hex.isValid(); });
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);
}
@ -419,24 +419,19 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
if(stack->hasBonusOfType(BonusType::FLYING))
{
std::set<BattleHex> obstacleHexes;
auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> & obstacleHexes) {
auto affectedHexes = spellObst.getAffectedTiles();
obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend());
};
BattleHexArray obstacleHexes;
const auto & obstacles = hb->battleGetAllObstacles();
for (const auto & obst: obstacles) {
for (const auto & obst : obstacles)
{
if(obst->triggersEffects())
{
auto triggerAbility = VLC->spells()->getById(obst->getTrigger());
auto triggerIsNegative = triggerAbility->isNegative() || triggerAbility->isDamage();
if(triggerIsNegative)
insertAffected(*obst, obstacleHexes);
obstacleHexes.insert(obst->getAffectedTiles());
}
}
// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
@ -446,7 +441,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
const int NEGATIVE_OBSTACLE_PENALTY = 100; // avoid landing on negative obstacle (moat, fire wall, etc)
const int BLOCKED_STACK_PENALTY = 100; // avoid landing on moat
auto distance = BattleHex::getDistance(bestNeighbor, hex);
auto distance = BattleHex::getDistance(bestNeighbour, hex);
if(vstd::contains(obstacleHexes, hex))
distance += NEGATIVE_OBSTACLE_PENALTY;
@ -458,7 +453,8 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
}
else
{
BattleHex currentDest = bestNeighbor;
BattleHex currentDest = bestNeighbour;
while(true)
{
if(!currentDest.isValid())
@ -472,7 +468,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
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 canCastSpell();
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;
bool hasWorkingTowers() const;
void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only

View File

@ -21,7 +21,7 @@ AttackerValue::AttackerValue()
MoveTarget::MoveTarget()
: positions(), cachedAttack(), score(EvaluationResult::INEFFECTIVE_SCORE)
{
turnsToRich = 1;
turnsToReach = 1;
}
float BattleExchangeVariant::trackAttack(
@ -310,7 +310,7 @@ ReachabilityInfo getReachabilityWithEnemyBypass(
for(auto & hex : unit->getHexes())
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;
@ -361,7 +361,8 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
float penaltyMultiplier = 1.0f; // Default multiplier, no penalty
float closestAllyDistance = std::numeric_limits<float>::max();
for (const battle::Unit* ally : hb->battleAliveUnits()) {
for (const battle::Unit* ally : hb->battleAliveUnits())
{
if (ally == activeStack)
continue;
if (ally->unitSide() != activeStack->unitSide())
@ -375,12 +376,13 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
}
// If an ally is closer to the enemy, compute the penaltyMultiplier
if (closestAllyDistance < distance) {
if (closestAllyDistance < distance)
{
penaltyMultiplier = closestAllyDistance / distance; // Ratio of distances
}
auto turnsToRich = (distance - 1) / speed + 1;
auto hexes = enemy->getSurroundingHexes();
auto turnsToReach = (distance - 1) / speed + 1;
const BattleHexArray & hexes = enemy->getSurroundingHexes();
auto enemySpeed = enemy->getMovementRange();
auto speedRatio = speed / static_cast<float>(enemySpeed);
auto multiplier = (speedRatio > 1 ? 1 : speedRatio) * penaltyMultiplier;
@ -393,16 +395,16 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
attack.shootersBlockedDmg = 0; // we do not want to count on it, it is not for sure
auto score = calculateExchange(attack, turnsToRich, targets, damageCache, hb);
auto score = calculateExchange(attack, turnsToReach, targets, damageCache, hb);
score.enemyDamageReduce *= multiplier;
#if BATTLE_TRACE_LEVEL >= 1
logAi->trace("Multiplier: %f, turns: %d, current score %f, new score %f", multiplier, turnsToRich, result.score, scoreValue(score));
logAi->trace("Multiplier: %f, turns: %d, current score %f, new score %f", multiplier, turnsToReach, result.score, scoreValue(score));
#endif
if(result.score < scoreValue(score)
|| (result.turnsToRich > turnsToRich && vstd::isAlmostEqual(result.score, scoreValue(score))))
|| (result.turnsToReach > turnsToReach && vstd::isAlmostEqual(result.score, scoreValue(score))))
{
result.score = scoreValue(score);
result.positions.clear();
@ -411,15 +413,13 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
logAi->trace("New high score");
#endif
for(const BattleHex & initialEnemyHex : enemy->getAttackableHexes(activeStack))
for(BattleHex enemyHex : enemy->getAttackableHexes(activeStack))
{
BattleHex enemyHex = initialEnemyHex;
while(!flying && dists.distances[enemyHex] > speed && dists.predecessors.at(enemyHex).isValid())
while(!flying && dists.distances[enemyHex.toInt()] > speed && dists.predecessors.at(enemyHex.toInt()).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);
@ -429,7 +429,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
logAi->trace("Found target to bypass at %d", enemyHex.hex);
#endif
auto attackHex = dists.predecessors[enemyHex];
auto attackHex = dists.predecessors[enemyHex.toInt()];
auto baiBypass = BattleAttackInfo(activeStack, defenderToBypass, 0, cb->battleCanShoot(activeStack));
auto attackBypass = AttackPossibility::evaluate(baiBypass, attackHex, damageCache, hb);
@ -440,7 +440,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
auto bypassScore = calculateExchange(
attackBypass,
dists.distances[attackHex],
dists.distances[attackHex.toInt()],
targets,
damageCache,
hb,
@ -458,11 +458,11 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
}
}
result.positions.push_back(enemyHex);
result.positions.insert(enemyHex);
}
result.cachedAttack = attack;
result.turnsToRich = turnsToRich;
result.turnsToReach = turnsToReach;
}
}
}
@ -484,15 +484,15 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getAdjacentUnits(cons
queue.pop();
checkedStacks.push_back(stack);
auto hexes = stack->getSurroundingHexes();
auto const & hexes = stack->getSurroundingHexes();
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);
checkedStacks.push_back(neighbor);
queue.push(neighbour);
checkedStacks.push_back(neighbour);
}
}
}
@ -511,7 +511,8 @@ ReachabilityData BattleExchangeEvaluator::getExchangeUnits(
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;
@ -915,8 +916,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(std::shared_ptr<HypotheticBa
}
}
}
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); ++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);
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);
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;
}
@ -1008,22 +1008,21 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
auto unitReachability = turnBattle.getReachability(unit);
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 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);
if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
{
enemyUnit = true;
for(BattleHex neighbor : hex.neighbouringTiles())
for(BattleHex neighbour : hex.getNeighbouringTiles())
{
reachable = unitReachability.distances.at(neighbor) <= unitSpeed;
reachable = unitReachability.distances.at(neighbour.toInt()) <= unitSpeed;
if(reachable) break;
}

View File

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

View File

@ -50,7 +50,7 @@ PotentialTargets::PotentialTargets(
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);
return AttackPossibility::evaluate(bai, hex, damageCache, state);

View File

@ -69,7 +69,7 @@ public:
const CStack * s;
int adi;
int adr;
std::vector<BattleHex> attackFrom; //for melee fight
BattleHexArray attackFrom; //for melee fight
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
{}
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 (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(s->isShooter())
shooters[i]++;
@ -157,7 +158,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
}
else
{
std::vector<BattleHex> avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false);
BattleHexArray avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false);
for (BattleHex hex : avHexes)
{
@ -170,7 +171,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
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");
}
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");
}
@ -278,7 +279,7 @@ void CStupidAI::print(const std::string &text) const
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 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
{
return reachability.distances[h1] < reachability.distances[h2];
return reachability.distances[h1.toInt()] < reachability.distances[h2.toInt()];
});
for(auto hex : hexes)
{
if(vstd::contains(avHexes, hex))
if(avHexes.contains(hex))
{
if(stack->position == hex)
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);
}
@ -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.
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
{
return BattleHex::getDistance(bestNeighbor, hex);
return BattleHex::getDistance(bestneighbour, hex);
});
return BattleAction::makeMove(stack, *nearestAvailableHex);
}
else
{
BattleHex currentDest = bestNeighbor;
BattleHex currentDest = bestneighbour;
while(1)
{
if(!currentDest.isValid())
@ -339,14 +340,14 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
return BattleAction::makeDefend(stack);
}
if(vstd::contains(avHexes, currentDest))
if(avHexes.contains(currentDest))
{
if(stack->position == currentDest)
return BattleAction::makeDefend(stack);
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 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 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 battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//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
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);
}
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;
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 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 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 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

View File

@ -522,7 +522,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
{
const auto * attacker = owner.stacksController->getActiveStack();
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;
BattleAttackInfo attackInfo(attacker, targetStack, distance, false );
DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation);
@ -723,11 +723,11 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
{
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);
if(vstd::contains(acc, targetHex))
if(acc.contains(targetHex))
owner.giveCommand(EActionType::WALK, targetHex);
else if(vstd::contains(acc, shiftedDest))
else if(acc.contains(shiftedDest))
owner.giveCommand(EActionType::WALK, shiftedDest);
}
else
@ -1008,12 +1008,12 @@ bool BattleActionsController::isCastingPossibleHere(const CSpell * currentSpell,
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);
if (vstd::contains(acc, myNumber))
if (acc.contains(myNumber))
return true;
else if (stackToMove->doubleWide() && vstd::contains(acc, shiftedDest))
else if (stackToMove->doubleWide() && acc.contains(shiftedDest))
return true;
else
return false;

View File

@ -174,7 +174,7 @@ AttackAnimation::AttackAnimation(BattleInterface & owner, const CStack *attacker
attackingStack(attacker)
{
assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n");
attackingStackPosBeforeReturn = attackingStack->getPosition();
attackingStackPosBeforeReturn = attackingStack->getPosition().toInt();
}
HittedAnimation::HittedAnimation(BattleInterface & owner, const CStack * stack)
@ -422,7 +422,7 @@ MovementAnimation::~MovementAnimation()
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()),
destTiles(_destTiles),
currentMoveIndex(0),
@ -892,17 +892,17 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
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)
{
battlehexes = hex;
battlehexes = hexes;
}
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex, int effects, float transparencyFactor, bool reversed):
EffectAnimation(owner, animationName, effects, transparencyFactor, reversed)
{
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):
@ -921,7 +921,7 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
EffectAnimation(owner, animationName, effects, 1.0f, reversed)
{
assert(hex.isValid());
battlehexes.push_back(hex);
battlehexes.insert(hex);
positions.push_back(pos);
}

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "../../lib/battle/BattleHex.h"
#include "../../lib/battle/BattleHexArray.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "BattleConstants.h"
@ -143,7 +143,7 @@ class MovementAnimation : public StackMoveAnimation
private:
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
double begX, begY; // starting position
@ -159,7 +159,7 @@ public:
bool init() 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();
};
@ -316,7 +316,7 @@ class EffectAnimation : public BattleAnimation
std::shared_ptr<CAnimation> animation;
std::vector<Point> positions;
std::vector<BattleHex> battlehexes;
BattleHexArray battlehexes;
bool alignToBottom() 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);
/// 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, std::vector<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);
/// 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, std::vector<BattleHex> hex, int effects = 0, 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, 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();
bool init() override;

View File

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

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "../../lib/battle/BattleHex.h"
#include "../../lib/battle/BattleHexArray.h"
#include "../../lib/Point.h"
#include "../gui/CIntObject.h"
@ -50,39 +50,39 @@ class BattleFieldController : public CIntObject
BattleHex hoveredHex;
/// 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
std::array<bool, GameConstants::BFIELD_SIZE> stackCountOutsideHexes;
void showHighlightedHex(Canvas & to, std::shared_ptr<IImage> highlight, BattleHex hex, bool darkBorder);
std::set<BattleHex> getHighlightedHexesForActiveStack();
std::set<BattleHex> getMovementRangeForHoveredStack();
std::set<BattleHex> getHighlightedHexesForSpellRange();
std::set<BattleHex> getHighlightedHexesForMovementTarget();
BattleHexArray getHighlightedHexesForActiveStack();
BattleHexArray getMovementRangeForHoveredStack();
BattleHexArray getHighlightedHexesForSpellRange();
BattleHexArray getHighlightedHexesForMovementTarget();
// Range limit highlight helpers
/// 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
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
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
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
/// 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);
/// 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 showBackgroundImage(Canvas & canvas);

View File

@ -216,7 +216,7 @@ void BattleInterface::stackActivated(const CStack *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)
stacksController->stackTeleported(stack, destHex, distance);

View File

@ -10,6 +10,7 @@
#pragma once
#include "BattleConstants.h"
#include "../lib/battle/BattleHex.h"
#include "../gui/CIntObject.h"
#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
#include "../ConditionalWait.h"
@ -27,7 +28,6 @@ class BattleAction;
class CGTownInstance;
struct CatapultAttack;
struct BattleTriggerEffect;
struct BattleHex;
struct InfoAboutHero;
class ObstacleChanges;
class CPlayerBattleCallback;
@ -202,7 +202,7 @@ public:
void stackAdded(const CStack * stack); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
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 stackAttacking(const StackAttackInfo & attackInfo); //called when stack with id ID is attacking something on hex dest
void newRoundFirst();

View File

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

View File

@ -185,7 +185,7 @@ BattleSiegeController::BattleSiegeController(BattleInterface & owner, const CGTo
const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) const
{
switch (position)
switch (position.toInt())
{
case BattleHex::CASTLE_CENTRAL_TOWER:
return town->fortificationsLevel().citadelShooter.toCreature();
@ -195,14 +195,14 @@ const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) co
return town->fortificationsLevel().lowerTowerShooter.toCreature();
}
throw std::runtime_error("Unable to select shooter for tower at " + std::to_string(position.hex));
throw std::runtime_error("Unable to select shooter for tower at " + std::to_string(position.toInt()));
}
Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const
{
// Turret positions are read out of the config/wall_pos.txt
int posID = 0;
switch (position)
switch (position.toInt())
{
case BattleHex::CASTLE_CENTRAL_TOWER: // keep creature
posID = EWallVisual::CREATURE_KEEP;

View File

@ -491,7 +491,7 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
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);
//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
}
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);
owner.checkForAnimations();

View File

@ -13,7 +13,8 @@
VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
class BattleHex;
class BattleHexArray;
class BattleAction;
class CStack;
class CSpell;
@ -109,8 +110,8 @@ public:
void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
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 stackTeleported(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, 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 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;
}
template<typename T>
void randomShuffle(std::vector<T> & container, vstd::RNG & rand)
template<typename Container>
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();
for(auto node : json["impassableHexes"].Vector())
info->impassableHexes.emplace_back(node.Integer());
info->impassableHexes.insert(node.Integer());
info->openingSoundFilename = AudioPath::fromJson(json["openingSound"]);
info->musicFilename = AudioPath::fromJson(json["music"]);

View File

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

View File

@ -204,7 +204,7 @@ void CAdventureAI::battleObstaclesChanged(const BattleID & battleID, const std::
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);
}

View File

@ -151,7 +151,7 @@ public:
void actionFinished(const BattleID & battleID, const BattleAction &action) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) 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 battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) 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/BattleAttackInfo.cpp
battle/BattleHex.cpp
battle/BattleHexArray.cpp
battle/BattleInfo.cpp
battle/BattleLayout.cpp
battle/BattleProxy.cpp
@ -413,6 +414,7 @@ set(lib_MAIN_HEADERS
battle/BattleAction.h
battle/BattleAttackInfo.h
battle/BattleHex.h
battle/BattleHexArray.h
battle/BattleInfo.h
battle/BattleLayout.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;
}
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;
std::vector<BattleHex> res;
BattleHexArray res;
if (!attackerPos.isValid())
attackerPos = attacker->getPosition();
if (!defenderPos.isValid())
defenderPos = defender->getPosition();
BattleHex otherAttackerPos = attackerPos + (attacker->unitSide() == BattleSide::ATTACKER ? -1 : 1);
BattleHex otherDefenderPos = defenderPos + (defender->unitSide() == BattleSide::ATTACKER ? -1 : 1);
BattleHex otherAttackerPos = attackerPos.toInt() + (attacker->unitSide() == BattleSide::ATTACKER ? -1 : 1);
BattleHex otherDefenderPos = defenderPos.toInt() + (defender->unitSide() == BattleSide::ATTACKER ? -1 : 1);
if(BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front
{
if((mask & 1) == 0)
{
mask |= 1;
res.push_back(defenderPos);
res.insert(defenderPos);
}
}
if (attacker->doubleWide() //back <=> front
@ -269,7 +269,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 1) == 0)
{
mask |= 1;
res.push_back(defenderPos);
res.insert(defenderPos);
}
}
if (defender->doubleWide()//front <=> back
@ -278,7 +278,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 2) == 0)
{
mask |= 2;
res.push_back(otherDefenderPos);
res.insert(otherDefenderPos);
}
}
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)
{
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
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);
BattleHex::EDir destShiftDir() const;

View File

@ -10,7 +10,7 @@
#pragma once
#include "networkPacks/EInfoWindowMode.h"
#include "battle/BattleHex.h"
#include "battle/BattleHexArray.h"
#include "GameConstants.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 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 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 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

View File

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

View File

@ -13,7 +13,7 @@
#include <vcmi/Entity.h>
#include "GameConstants.h"
#include "IHandlerBase.h"
#include "battle/BattleHex.h"
#include "battle/BattleHexArray.h"
#include "filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -54,7 +54,7 @@ public:
void registerIcons(const IconRegistar & cb) 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;
};

View File

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

View File

@ -27,15 +27,16 @@ enum class EAccessibility
DESTRUCTIBLE_WALL,
GATE, //sieges -> gate opens only for defender stacks
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 TBattlefieldTurnsArray = std::array<int8_t, GameConstants::BFIELD_SIZE>;
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:
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 "BattleHex.h"
#include "BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN
BattleHex::BattleHex() : hex(INVALID) {}
BattleHex::BattleHex(si16 _hex) : hex(_hex) {}
BattleHex::BattleHex(si16 x, si16 y)
BattleHex BattleHex::getClosestTile(BattleSide side, BattleHex initialPos, const BattleHexArray & hexes)
{
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);
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);
};
boost::sort (sortedTiles, compareDistance); //closest tiles at front
int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
{
return closestDistance < here.getDistance (initialPos, here);
};
vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool
int distance = initialHex.getDistance(initialHex, hex);
if(distance < closestDistance)
{
closestDistance = distance;
closestTiles.clear();
closestTiles.insert(hex);
}
else if(distance == closestDistance)
closestTiles.insert(hex);
}
auto compareHorizontal = [side, initialPos](const BattleHex & left, const BattleHex & right)
{
if(left.getX() != right.getX())
{
if(side == BattleSide::ATTACKER)
return left.getX() > right.getX(); //find furthest right
else
return left.getX() < right.getX(); //find furthest left
}
else
{
//Prefer tiles in the same row.
return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
return (side == BattleSide::ATTACKER) ? (left.getX() > right.getX()) : (left.getX() < right.getX());
}
return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
};
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)
{
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

View File

@ -22,9 +22,14 @@ namespace GameConstants
const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
}
// for battle stacks' positions
struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design
class BattleHexArray;
// 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
static constexpr si16 CASTLE_CENTRAL_TOWER = -2;
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_INNER = 96;
si16 hex;
static constexpr si16 INVALID = -1;
enum EDir
{
NONE = -1,
@ -64,52 +69,209 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
BOTTOM
};
BattleHex();
BattleHex(si16 _hex);
BattleHex(si16 x, si16 y);
BattleHex(std::pair<si16, si16> xy);
operator si16() const;
bool isValid() const;
bool isAvailable() const; //valid position not in first or last column
void setX(si16 x);
void setY(si16 y);
void setXY(si16 x, si16 y, bool hasToBeValid = true);
void setXY(std::pair<si16, si16> xy);
si16 getX() const;
si16 getY() const;
std::pair<si16, si16> getXY() const;
BattleHex& moveInDirection(EDir dir, bool hasToBeValid = true);
BattleHex& operator+=(EDir dir);
BattleHex cloneInDirection(EDir dir, bool hasToBeValid = true) const;
BattleHex operator+(EDir dir) const;
BattleHex() noexcept
: hex(INVALID)
{}
BattleHex(si16 _hex) noexcept
: hex(_hex)
{}
BattleHex(si16 x, si16 y)
{
setXY(x, y);
}
BattleHex(std::pair<si16, si16> xy)
{
setXY(xy);
}
/// returns all valid neighbouring tiles
std::vector<BattleHex> neighbouringTiles() const;
[[nodiscard]] bool isValid() const noexcept
{
return hex >= 0 && hex < GameConstants::BFIELD_SIZE;
}
/// returns all tiles, unavailable tiles will be set as invalid
/// order of returned tiles matches EDir enim
std::vector<BattleHex> allNeighbouringTiles() const;
[[nodiscard]] bool isAvailable() const noexcept //valid position not in first or last column
{
return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH - 1;
}
static EDir mutualPosition(BattleHex hex1, BattleHex hex2);
static uint8_t getDistance(BattleHex hex1, BattleHex hex2);
static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret);
static BattleHex getClosestTile(BattleSide side, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
void setX(si16 x)
{
setXY(x, getY());
}
void setY(si16 y)
{
setXY(getX(), y);
}
void setXY(si16 x, si16 y, bool hasToBeValid = true)
{
if(hasToBeValid)
{
if(x < 0 || x >= GameConstants::BFIELD_WIDTH || y < 0 || y >= GameConstants::BFIELD_HEIGHT)
throw std::runtime_error("Valid hex required");
}
hex = x + y * GameConstants::BFIELD_WIDTH;
}
void setXY(std::pair<si16, si16> xy)
{
setXY(xy.first, xy.second);
}
[[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>
void serialize(Handler &h)
void serialize(Handler & h)
{
h & hex;
}
using NeighbouringTiles = std::array<BattleHex, 6>;
using NeighbouringTilesCache = std::vector<NeighbouringTiles>;
static const NeighbouringTilesCache neighbouringTilesCache;
private:
//Constexpr defined array with all directions used in battle
static constexpr auto hexagonalDirections() {
return std::array<EDir,6>{BattleHex::TOP_LEFT, BattleHex::TOP_RIGHT, BattleHex::RIGHT, BattleHex::BOTTOM_RIGHT, BattleHex::BOTTOM_LEFT, BattleHex::LEFT};
}
si16 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));
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);
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
int tilesToBlock = r.rand(5,12);
std::vector<BattleHex> blockedTiles;
BattleHexArray blockedTiles;
auto appropriateAbsoluteObstacle = [&](int id)
{
@ -232,7 +232,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked);
blockedTiles.insert(blocked);
tilesToBlock -= Obstacle(obstPtr->ID).getInfo()->blockedTiles.size() / 2;
}
catch(RangeGenerator::ExhaustedPossibilities &)
@ -259,14 +259,14 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
return false;
if(pos.getX() + obi.width > 15)
return false;
if(vstd::contains(blockedTiles, pos))
if(blockedTiles.contains(pos))
return false;
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;
if(vstd::contains(blockedTiles, blocked))
if(blockedTiles.contains(blocked))
return false;
int x = blocked.getX();
if(x <= 2 || x >= 14)
@ -285,7 +285,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked);
blockedTiles.insert(blocked);
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))
{
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
}
}

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)
{
const int wallInStackLine = lineToWallHex(pos1.getY());
const int wallInDestLine = lineToWallHex(pos2.getY());
const bool stackLeft = pos1 < wallInStackLine;
const bool destLeft = pos2 < wallInDestLine;
const bool stackLeft = pos1 < lineToWallHex(pos1.getY());
const bool destLeft = pos2 < lineToWallHex(pos2.getY());
return stackLeft == destLeft;
}
static bool isInsideWalls(BattleHex pos)
{
const int wallInStackLine = lineToWallHex(pos.getY());
return wallInStackLine < pos;
return lineToWallHex(pos.getY()) < pos;
}
// parts of wall
@ -79,9 +75,10 @@ static const std::pair<int, EWallPart> wallParts[] =
static EWallPart hexToWallPart(BattleHex hex)
{
si16 hexValue = hex.toInt();
for(const auto & elem : wallParts)
{
if(elem.first == hex)
if(elem.first == hexValue)
return elem.second;
}
@ -147,25 +144,25 @@ ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const spells::Caster *
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);
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
std::vector<BattleHex> path;
BattleHexArray path;
BattleHex curElem = dest;
while(curElem != start)
{
path.push_back(curElem);
curElem = reachability.predecessors[curElem];
path.insert(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
@ -176,7 +173,7 @@ bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const
bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
{
if (!from.isAvailable() || !dest.isAvailable())
throw std::runtime_error("Invalid hex (" + std::to_string(from.hex) + " and " + std::to_string(dest.hex) + ") received in battleHasPenaltyOnLine!" );
throw std::runtime_error("Invalid hex (" + std::to_string(from.toInt()) + " and " + std::to_string(dest.toInt()) + ") received in battleHasPenaltyOnLine!" );
auto isTileBlocked = [&](BattleHex tile)
{
@ -191,23 +188,21 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
return isWallPartAttackable(wallPart);
};
// 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
if(from == dest)
return {};
std::vector<BattleHex> ret;
BattleHexArray ret;
auto next = from;
//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;
while (next != dest)
{
auto tiles = next.neighbouringTiles();
std::set<BattleHex> possibilities = {tiles.begin(), tiles.end()};
next = BattleHex::getClosestTile(direction, dest, possibilities);
ret.push_back(next);
next = BattleHex::getClosestTile(direction, dest, next.getNeighbouringTiles());
ret.insert(next);
}
assert(!ret.empty());
ret.pop_back(); //Remove destination hex
@ -227,7 +222,7 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
auto obstacles = battleGetAllObstaclesOnPos(hex, false);
if(hex != BattleHex::GATE_BRIDGE || (battleIsGatePassable()))
if(hex.toInt() != BattleHex::GATE_BRIDGE || (battleIsGatePassable()))
for(const auto & obst : obstacles)
if(obst->obstacleType == CObstacleInstance::MOAT)
pathHasMoat |= true;
@ -318,9 +313,9 @@ PossiblePlayerBattleAction CBattleInfoCallback::getCasterAction(const CSpell * s
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);
AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos);
@ -347,7 +342,7 @@ const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyA
{
RETURN_IF_NOT_BATTLE(nullptr);
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 nullptr;
@ -569,21 +564,21 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector<battle::Units> & turns,
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
return std::vector<BattleHex>();
return BattleHexArray();
auto reachability = getReachability(unit);
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);
if(!unit->getPosition().isValid()) //turrets
@ -612,28 +607,27 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const Reacha
continue;
}
ret.emplace_back(i);
ret.insert(i);
}
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())
return ret;
if(addOccupiable && unit->doubleWide())
{
std::vector<BattleHex> occupiable;
BattleHexArray occupiable;
occupiable.reserve(ret.size());
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.
// 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 availableNeighbor != ret.end();
return availableNeighbour != ret.end();
};
for(const auto * otherSt : battleAliveUnits(otherSide(unit->unitSide())))
{
if(!otherSt->isValidTarget(false))
continue;
std::vector<BattleHex> occupied = otherSt->getHexes();
const BattleHexArray & occupied = otherSt->getHexes();
if(battleCanShoot(unit, otherSt->getPosition()))
{
attackable->insert(attackable->end(), occupied.begin(), occupied.end());
attackable->insert(occupied);
continue;
}
for(BattleHex he : occupied)
{
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;
}
@ -792,7 +783,7 @@ DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit *
{
RETURN_IF_NOT_BATTLE({});
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);
}
@ -857,8 +848,8 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
RETURN_IF_NOT_BATTLE(obstacles);
for(auto & obs : battleGetAllObstacles())
{
if(vstd::contains(obs->getBlockedTiles(), tile)
|| (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile)))
if(obs->getBlockedTiles().contains(tile)
|| (!onlyBlocking && obs->getAffectedTiles().contains(tile)))
{
obstacles.push_back(obs);
}
@ -866,18 +857,18 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
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>>();
RETURN_IF_NOT_BATTLE(affectedObstacles);
if(unit->alive())
{
if(!passed.count(unit->getPosition()))
if(!passed.contains(unit->getPosition()))
affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false);
if(unit->doubleWide())
{
BattleHex otherHex = unit->occupiedHex();
if(otherHex.isValid() && !passed.count(otherHex))
if(otherHex.isValid() && !passed.contains(otherHex))
for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
if(!vstd::contains(affectedObstacles, i))
affectedObstacles.push_back(i);
@ -891,7 +882,7 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAl
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())
return false;
@ -961,8 +952,8 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
//removing accessibility for side columns of hexes
for(int y = 0; y < GameConstants::BFIELD_HEIGHT; y++)
{
ret[BattleHex(GameConstants::BFIELD_WIDTH - 1, y)] = EAccessibility::SIDE_COLUMN;
ret[BattleHex(0, y)] = EAccessibility::SIDE_COLUMN;
ret[BattleHex(GameConstants::BFIELD_WIDTH - 1, y).toInt()] = EAccessibility::SIDE_COLUMN;
ret[BattleHex(0, y).toInt()] = EAccessibility::SIDE_COLUMN;
}
//special battlefields with logically unavailable tiles
@ -970,10 +961,8 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
if(bFieldType != BattleField::NONE)
{
std::vector<BattleHex> impassableHexes = bFieldType.getInfo()->impassableHexes;
for(auto hex : impassableHexes)
ret[hex] = EAccessibility::UNAVAILABLE;
for(auto hex : bFieldType.getInfo()->impassableHexes)
ret[hex.toInt()] = EAccessibility::UNAVAILABLE;
}
//gate -> should be before stacks
@ -998,14 +987,14 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
{
for(auto hex : unit->getHexes())
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
for(const auto &obst : battleGetAllObstacles())
{
for(auto hex : obst->getBlockedTiles())
ret[hex] = EAccessibility::OBSTACLE;
ret[hex.toInt()] = EAccessibility::OBSTACLE;
}
//walls
@ -1028,7 +1017,7 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
for(const auto & elem : lockedIfNotDestroyed)
{
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()));
}
AccessibilityInfo CBattleInfoCallback::getAccessibility(const std::vector<BattleHex> & accessibleHexes) const
AccessibilityInfo CBattleInfoCallback::getAccessibility(const BattleHexArray & accessibleHexes) const
{
auto ret = getAccessibility();
for(auto hex : accessibleHexes)
if(hex.isValid())
ret[hex] = EAccessibility::ACCESSIBLE;
ret[hex.toInt()] = EAccessibility::ACCESSIBLE;
return ret;
}
ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibility, const ReachabilityInfo::Parameters & params) const
ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const
{
ReachabilityInfo ret;
ret.accessibility = accessibility;
@ -1062,7 +1051,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
if(!params.startPosition.isValid()) //if got call for arrow turrets
return ret;
const std::set<BattleHex> obstacles = getStoppers(params.perspective);
const BattleHexArray obstacles = getStoppers(params.perspective);
auto checkParams = params;
checkParams.ignoreKnownAccessible = true; //Ignore starting hexes obstacles
@ -1070,7 +1059,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
//first element
hexq.push(params.startPosition);
ret.distances[params.startPosition] = 0;
ret.distances[params.startPosition.toInt()] = 0;
std::array<bool, GameConstants::BFIELD_SIZE> accessibleCache{};
for(int hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
@ -1085,32 +1074,29 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
if(isInObstacle(curHex, obstacles, checkParams))
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);
if(enemyToBypass != params.destructibleEnemyTurns.end())
{
additionalCost = enemyToBypass->second;
}
additionalCost = enemyToBypass;
}
}
const int costFoundSoFar = ret.distances[neighbour.hex];
const int costFoundSoFar = ret.distances[neighbour.toInt()];
if(accessibleCache[neighbour.hex] && costToNeighbour + additionalCost < costFoundSoFar)
{
hexq.push(neighbour);
ret.distances[neighbour.hex] = costToNeighbour + additionalCost;
ret.predecessors[neighbour.hex] = curHex;
}
if(accessibleCache[neighbour.toInt()] && costToNeighbour + additionalCost < costFoundSoFar)
{
hexq.push(neighbour);
ret.distances[neighbour.toInt()] = costToNeighbour + additionalCost;
ret.predecessors[neighbour.toInt()] = curHex;
}
}
}
@ -1120,17 +1106,16 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
bool CBattleInfoCallback::isInObstacle(
BattleHex hex,
const std::set<BattleHex> & obstacles,
const BattleHexArray & obstacleHexes,
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;
if(vstd::contains(obstacles, occupiedHex))
if(obstacleHexes.contains(occupiedHex))
{
if(occupiedHex == BattleHex::GATE_BRIDGE)
{
@ -1145,9 +1130,9 @@ bool CBattleInfoCallback::isInObstacle(
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);
for(auto &oi : battleGetAllObstacles(whichSidePerspective))
@ -1155,7 +1140,7 @@ std::set<BattleHex> CBattleInfoCallback::getStoppers(BattleSide whichSidePerspec
if(!battleIsObstacleVisibleForSide(*oi, whichSidePerspective))
continue;
for(const auto & hex : oi->getStoppingTile())
for(auto hex : oi->getStoppingTile())
{
if(hex == BattleHex::GATE_BRIDGE && oi->obstacleType == CObstacleInstance::MOAT)
{
@ -1193,7 +1178,7 @@ std::pair<const battle::Unit *, BattleHex> CBattleInfoCallback::getNearestStack(
for(BattleHex hex : avHexes)
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);
}
}
@ -1225,7 +1210,7 @@ BattleHex CBattleInfoCallback::getAvailableHex(const CreatureID & creID, BattleS
auto accessibility = getAccessibility();
std::set<BattleHex> occupyable;
BattleHexArray occupyable;
for(int i = 0; i < accessibility.size(); i++)
if(accessibility.accessible(i, twoHex, side))
occupyable.insert(i);
@ -1278,24 +1263,27 @@ ReachabilityInfo CBattleInfoCallback::getReachability(const battle::Unit * unit)
return getReachability(params);
}
ReachabilityInfo CBattleInfoCallback::getReachability(const ReachabilityInfo::Parameters &params) const
ReachabilityInfo CBattleInfoCallback::getReachability(const ReachabilityInfo::Parameters & params) const
{
if(params.flying)
return getFlyingReachability(params);
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);
}
}
ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityInfo::Parameters &params) const
ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityInfo::Parameters & params) const
{
ReachabilityInfo ret;
ret.accessibility = getAccessibility(params.knownAccessible);
ret.accessibility = getAccessibility(* params.knownAccessible);
for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
{
@ -1338,9 +1326,9 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
AttackableTiles 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);
if(reverse && attacker->doubleWide())
@ -1349,11 +1337,11 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
}
if(attacker->hasBonusOfType(BonusType::ATTACKS_ALL_ADJACENT))
{
boost::copy(attacker->getSurroundingHexes(attackerPos), vstd::set_inserter(at.hostileCreaturePositions));
at.hostileCreaturePositions.insert(attacker->getSurroundingHexes(attackerPos));
}
if(attacker->hasBonusOfType(BonusType::THREE_HEADED_ATTACK))
{
std::vector<BattleHex> hexes = attacker->getSurroundingHexes(attackerPos);
const BattleHexArray & hexes = attacker->getSurroundingHexes(attackerPos);
for(BattleHex tile : hexes)
{
if((BattleHex::mutualPosition(tile, destinationTile) > -1 && BattleHex::mutualPosition(tile, attackOriginHex) > -1)) //adjacent both to attacker's head and attacked tile
@ -1366,12 +1354,12 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
}
if(attacker->hasBonusOfType(BonusType::WIDE_BREATH))
{
std::vector<BattleHex> hexes = destinationTile.neighbouringTiles();
for(int i = 0; i<hexes.size(); i++)
BattleHexArray hexes = destinationTile.getNeighbouringTiles();
for(int i = 0; i < hexes.size(); i++)
{
if(hexes.at(i) == attackOriginHex)
{
hexes.erase(hexes.begin() + i);
hexes.erase(i);
i = 0;
}
}
@ -1439,11 +1427,10 @@ AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const battle::
AttackableTiles 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();
targetHexes.push_back(destinationTile);
boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions));
at.hostileCreaturePositions.insert(destinationTile.getNeighbouringTiles());
at.hostileCreaturePositions.insert(destinationTile);
}
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()))
{
if (vstd::contains(at.hostileCreaturePositions, hex))
if (at.hostileCreaturePositions.contains(hex))
return true;
if (vstd::contains(at.friendlyCreaturePositions, hex))
if (at.friendlyCreaturePositions.contains(hex))
return true;
}
return false;
@ -1671,15 +1658,15 @@ bool CBattleInfoCallback::isWallPartAttackable(EWallPart wallPart) const
return false;
}
std::vector<BattleHex> CBattleInfoCallback::getAttackableBattleHexes() const
BattleHexArray CBattleInfoCallback::getAttackableBattleHexes() const
{
std::vector<BattleHex> attackableBattleHexes;
BattleHexArray attackableBattleHexes;
RETURN_IF_NOT_BATTLE(attackableBattleHexes);
for(const auto & wallPartPair : wallParts)
{
if(isWallPartAttackable(wallPartPair.second))
attackableBattleHexes.emplace_back(wallPartPair.first);
attackableBattleHexes.insert(wallPartPair.first);
}
return attackableBattleHexes;

View File

@ -38,8 +38,8 @@ namespace spells
struct DLL_LINKAGE AttackableTiles
{
std::set<BattleHex> hostileCreaturePositions;
std::set<BattleHex> friendlyCreaturePositions; //for Dragon Breath
BattleHexArray hostileCreaturePositions;
BattleHexArray friendlyCreaturePositions; //for Dragon Breath
template <typename Handler> void serialize(Handler &h)
{
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::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
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;
@ -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;
///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)
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
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 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 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
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
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 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;
AccessibilityInfo getAccessibility() const;
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;
BattleHex getAvailableHex(const CreatureID & creID, BattleSide side, int initialPos = -1) const; //find place for adding new stack
protected:
ReachabilityInfo getFlyingReachability(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;
std::set<BattleHex> getStoppers(BattleSide whichSidePerspective) const; //get hexes with stopping obstacles (quicksands)
bool isInObstacle(BattleHex hex, const BattleHexArray & obstacles, const ReachabilityInfo::Parameters & params) const;
BattleHexArray getStoppers(BattleSide whichSidePerspective) const; //get hexes with stopping obstacles (quicksands)
};
VCMI_LIB_NAMESPACE_END

View File

@ -24,21 +24,21 @@ const ObstacleInfo & CObstacleInstance::getInfo() const
return *Obstacle(ID).getInfo();
}
std::vector<BattleHex> CObstacleInstance::getBlockedTiles() const
BattleHexArray CObstacleInstance::getBlockedTiles() const
{
if(blocksTiles())
return getAffectedTiles();
return std::vector<BattleHex>();
return BattleHexArray();
}
std::vector<BattleHex> CObstacleInstance::getStoppingTile() const
BattleHexArray CObstacleInstance::getStoppingTile() const
{
if(stopsMovement())
return getAffectedTiles();
return std::vector<BattleHex>();
return BattleHexArray();
}
std::vector<BattleHex> CObstacleInstance::getAffectedTiles() const
BattleHexArray CObstacleInstance::getAffectedTiles() const
{
switch(obstacleType)
{
@ -47,7 +47,7 @@ std::vector<BattleHex> CObstacleInstance::getAffectedTiles() const
return getInfo().getBlocked(pos);
default:
assert(0);
return std::vector<BattleHex>();
return BattleHexArray();
}
}
@ -113,7 +113,9 @@ void CObstacleInstance::serializeJson(JsonSerializeFormat & handler)
animationYOffset -= 42;
//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.serializeBool("hidden", hidden);
handler.serializeBool("needAnimationOffsetFix", needAnimationOffsetFix);
@ -188,7 +190,9 @@ void SpellCreatedObstacle::fromInfo(const ObstacleChanges & info)
void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler)
{
handler.serializeInt("spell", ID);
handler.serializeInt("position", pos);
si16 posValue = pos.toInt();
handler.serializeInt("position", posValue);
pos = posValue;
handler.serializeInt("turnsRemaining", turnsRemaining);
handler.serializeInt("casterSpellPower", casterSpellPower);
@ -216,11 +220,15 @@ void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler)
customSizeJson.syncSize(customSize, JsonNode::JsonType::DATA_INTEGER);
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;
}

View File

@ -8,7 +8,7 @@
*
*/
#pragma once
#include "BattleHex.h"
#include "BattleHexArray.h"
#include "../constants/EntityIdentifiers.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)
std::vector<BattleHex> getBlockedTiles() const;
std::vector<BattleHex> getStoppingTile() const; //hexes that will stop stack move
BattleHexArray getBlockedTiles() const;
BattleHexArray getStoppingTile() const; //hexes that will stop stack move
//The two functions below describe how the obstacle affects affected tiles
//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 SpellID getTrigger() const;
virtual std::vector<BattleHex> getAffectedTiles() const;
virtual BattleHexArray getAffectedTiles() const;
virtual bool visibleForSide(BattleSide side, bool hasNativeStack) const; //0 attacker
virtual void battleTurnPassed(){};
@ -97,11 +97,11 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
int animationYOffset;
std::vector<BattleHex> customSize;
BattleHexArray customSize;
SpellCreatedObstacle();
std::vector<BattleHex> getAffectedTiles() const override;
BattleHexArray getAffectedTiles() const override;
bool visibleForSide(BattleSide side, bool hasNativeStack) const override;
bool blocksTiles() const override;

View File

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

View File

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

View File

@ -11,7 +11,7 @@
#pragma once
#include "GameConstants.h"
#include "BattleHex.h"
#include "BattleHexArray.h"
#include <vcmi/Entity.h>
@ -81,7 +81,7 @@ public:
//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>> 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()),
flying(Stack->hasBonusOfType(BonusType::FLYING))
{
knownAccessible = battle::Unit::getHexes(startPosition, doubleWide, side);
knownAccessible = & battle::Unit::getHexes(startPosition, doubleWide, side);
destructibleEnemyTurns.fill(-1);
}
ReachabilityInfo::ReachabilityInfo()
@ -32,22 +33,22 @@ ReachabilityInfo::ReachabilityInfo()
bool ReachabilityInfo::isReachable(BattleHex hex) const
{
return distances[hex] < INFINITE_DIST;
return distances[hex.toInt()] < INFINITE_DIST;
}
uint32_t ReachabilityInfo::distToNearestNeighbour(
const std::vector<BattleHex> & targetHexes,
const BattleHexArray & targetHexes,
BattleHex * chosenHex) const
{
uint32_t ret = 1000000;
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)
*chosenHex = n;
}
@ -70,16 +71,14 @@ uint32_t ReachabilityInfo::distToNearestNeighbour(
{
// 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
vstd::concatenate(attackableHexes, battle::Unit::getHexes(defender->occupiedHex(), true, defender->unitSide()));
attackableHexes.insert(battle::Unit::getHexes(defender->occupiedHex(), true, defender->unitSide()));
}
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
{
return h.getY() != defender->getPosition().getY() || !h.isAvailable();

View File

@ -8,12 +8,14 @@
*
*/
#pragma once
#include "BattleHex.h"
#include "BattleHexArray.h"
#include "CBattleInfoEssentials.h"
#include "AccessibilityInfo.h"
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),
// startPosition and perspective.
struct DLL_LINKAGE ReachabilityInfo
@ -30,13 +32,16 @@ struct DLL_LINKAGE ReachabilityInfo
bool flying = false;
bool ignoreKnownAccessible = false; //Ignore obstacles if it is in accessible hexes
bool bypassEnemyStacks = false; // in case of true will count amount of turns needed to kill enemy and thus move forward
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)
std::map<BattleHex, ui8> destructibleEnemyTurns; // hom many turns it is needed to kill enemy on specific hex
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)
TBattlefieldTurnsArray destructibleEnemyTurns; // how many turns it is needed to kill enemy on specific hex (index <=> hex)
BattleHex startPosition; //assumed position of stack
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);
};
@ -50,7 +55,7 @@ struct DLL_LINKAGE ReachabilityInfo
bool isReachable(BattleHex hex) const;
uint32_t distToNearestNeighbour(
const std::vector<BattleHex> & targetHexes,
const BattleHexArray & targetHexes,
BattleHex * chosenHex = nullptr) const;
uint32_t distToNearestNeighbour(

View File

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

View File

@ -17,7 +17,7 @@
#include "../bonuses/IBonusBearer.h"
#include "IUnitInfo.h"
#include "BattleHex.h"
#include "BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -128,15 +128,15 @@ public:
virtual std::string getDescription() const;
std::vector<BattleHex> getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
std::vector<BattleHex> getAttackableHexes(const Unit * attacker) const;
static std::vector<BattleHex> getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side);
const BattleHexArray & getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
BattleHexArray getAttackableHexes(const Unit * attacker) const;
static const BattleHexArray & getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side);
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
std::vector<BattleHex> getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
static std::vector<BattleHex> getHexes(BattleHex assumedPos, bool twoHex, BattleSide side);
const BattleHexArray & getHexes() const; //up to two occupied hexes, starting from front
const BattleHexArray & getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
static const BattleHexArray & getHexes(BattleHex assumedPos, bool twoHex, BattleSide side);
BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1

View File

@ -142,7 +142,7 @@ class JsonNode;
BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\
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(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*/ \

View File

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

View File

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

View File

@ -278,7 +278,7 @@ CGHeroInstance * CMap::getHero(HeroTypeID heroID)
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),
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 "BattleChanges.h"
#include "../battle/BattleHexArray.h"
#include "../battle/BattleAction.h"
#include "../texts/MetaString.h"
@ -170,7 +171,7 @@ struct DLL_LINKAGE BattleStackMoved : public CPackForClient
{
BattleID battleID = BattleID::NONE;
ui32 stack = 0;
std::vector<BattleHex> tilesToMove;
BattleHexArray tilesToMove;
int distance = 0;
bool teleporting = false;

View File

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

View File

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

View File

@ -56,7 +56,7 @@ public:
bool isReceptive(const battle::Unit * target) const override;
/// 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;
@ -75,7 +75,7 @@ private:
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;
};

View File

@ -357,7 +357,7 @@ int32_t CSpell::getLevelPower(const int32_t skillLevel) const
si32 CSpell::getProbability(const FactionID & factionId) const
{
if(!vstd::contains(probabilities,factionId))
if(!vstd::contains(probabilities, factionId))
{
return defaultProbability;
}
@ -701,7 +701,7 @@ const std::vector<std::string> & CSpellHandler::getTypeNames() 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
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)

View File

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

View File

@ -185,7 +185,7 @@ public:
virtual bool adaptProblem(ESpellCastProblem source, 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 bool canBeCast(Problem & problem) const = 0;

View File

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

View File

@ -65,7 +65,7 @@ void DemonSummon::apply(ServerCallback * server, const Mechanics * m, const Effe
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())
{

View File

@ -14,7 +14,8 @@
VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
class BattleHex;
class BattleHexArray;
class CBattleInfoCallback;
class JsonSerializeFormat;
class ServerCallback;
@ -51,7 +52,7 @@ public:
virtual void adjustTargetTypes(std::vector<TargetType> & types) const = 0;
/// 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
virtual bool applicable(Problem & problem, const Mechanics * m) const;

View File

@ -11,6 +11,7 @@
#include "LocationEffect.h"
#include "../ISpellMechanics.h"
#include "battle/BattleHexArray.h"
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)
hexes.insert(destnation.hexValue);

View File

@ -25,7 +25,7 @@ class LocationEffect : public Effect
public:
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;

View File

@ -32,7 +32,7 @@ namespace spells
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);
@ -44,7 +44,11 @@ static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string
inner.syncSize(moatHexes.at(outerIndex), JsonNode::JsonType::DATA_INTEGER);
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.source = BonusSource::SPELL_EFFECT;//for all
}
std::set<BattleHex> flatMoatHexes;
BattleHexArray flatMoatHexes;
for(const auto & moatPatch : moatHexes)
flatMoatHexes.insert(moatPatch.begin(), moatPatch.end());
flatMoatHexes.insert(moatPatch);
nb.limiter = std::make_shared<UnitOnHexLimiter>(std::move(flatMoatHexes));
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.appearAnimation = sideOptions.appearAnimation; //For dispellable moats
obstacle.animation = sideOptions.animation;
obstacle.customSize.insert(obstacle.customSize.end(),destination.cbegin(), destination.cend());
obstacle.customSize.insert(destination);
obstacle.animationYOffset = sideOptions.offsetY;
pack.changes.emplace_back();
obstacle.toInfo(pack.changes.back());

View File

@ -25,7 +25,7 @@ class Moat : public Obstacle
{
private:
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
bool dispellable; //For Tower landmines
int moatDamage; // Minimal moat damage

View File

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

View File

@ -12,7 +12,7 @@
#include "LocationEffect.h"
#include "../../GameConstants.h"
#include "../../battle/BattleHex.h"
#include "../../battle/BattleHexArray.h"
#include "../../battle/CObstacleInstance.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -42,7 +42,7 @@ public:
class Obstacle : public LocationEffect
{
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 EffectTarget & target) const override;

View File

@ -28,7 +28,7 @@ namespace spells
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
}

View File

@ -23,7 +23,7 @@ namespace effects
class Summon : public Effect
{
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;
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.distance = 0;
pack.stack = targetUnit->unitId();
std::vector<BattleHex> tiles;
tiles.push_back(destination);
BattleHexArray tiles;
tiles.insert(destination);
pack.tilesToMove = tiles;
pack.teleporting = true;
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)
hexes.insert(destnation.hexValue);
@ -193,7 +193,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe
return EffectTarget();
}
std::set<BattleHex> possibleHexes;
BattleHexArray possibleHexes;
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();
for(auto hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide()))
possibleHexes.erase(hex);
possibleHexes.erase(hex.toInt());
if(possibleHexes.empty())
break;

View File

@ -24,7 +24,7 @@ class UnitEffect : public Effect
public:
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 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)
{
JsonNode targetData;
targetData.Vector().emplace_back(dest.hexValue.hex);
targetData.Vector().emplace_back(dest.hexValue.toInt());
if(dest.unitValue)
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)
{
JsonNode targetData;
targetData.Vector().emplace_back(dest.hexValue.hex);
targetData.Vector().emplace_back(dest.hexValue.toInt());
if(dest.unitValue)
targetData.Vector().emplace_back(dest.unitValue->unitId());

View File

@ -49,7 +49,7 @@ public:
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 EffectTarget & target) const override;

View File

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

View File

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

View File

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

View File

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

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 y = targetPosition.getY();
@ -44,31 +44,31 @@ void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & batt
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...
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
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
if (targetIsAttacker && ((y % 2 == 0) || (x > 1)))
{
if (targetIsTwoHex && (y % 2 == 1) && (x == 2)) //handle exceptional case
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
}
else
{ //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);
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_LEFT : BattleHex::EDir::BOTTOM_RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_LEFT : BattleHex::EDir::TOP_RIGHT, false));
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_LEFT : BattleHex::EDir::BOTTOM_RIGHT, false));
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
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output);
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::TOP_RIGHT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
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
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
}
else
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_RIGHT : BattleHex::EDir::TOP_LEFT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_RIGHT : BattleHex::EDir::BOTTOM_LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_RIGHT : BattleHex::EDir::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_RIGHT : BattleHex::EDir::BOTTOM_LEFT, false));
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)
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output);
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::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
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)
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output);
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::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
}
else if (targetIsAttacker && y % 2 == 1)
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output);
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::TOP_RIGHT, false));
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));
auto accessibility = battle.getAccessibility();
CreatureID creatureData = summonInfo->subtype.as<CreatureID>();
std::vector<BattleHex> targetHexes;
BattleHexArray targetHexes;
const bool targetIsBig = stack->unitType()->isDoubleWide(); //target = creature to guard
const bool guardianIsBig = creatureData.toCreature()->isDoubleWide();

View File

@ -13,7 +13,8 @@
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
struct BattleHex;
class BattleHex;
class BattleHexArray;
class BattleAction;
class CBattleInfoCallback;
struct CObstacleInstance;
@ -37,7 +38,7 @@ class BattleFlowProcessor : boost::noncopyable
bool rollGoodMorale(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 tryPlaceMoats(const CBattleInfoCallback & battle);
void castOpeningSpells(const CBattleInfoCallback & battle);

View File

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

View File

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

View File

@ -40,7 +40,7 @@ public:
MOCK_CONST_METHOD0(getPlayerID, std::optional<PlayerColor>());
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/CGameInfoCallback.h"
#include "../../lib/battle/BattleHexArray.h"
namespace spells
{
@ -22,7 +23,7 @@ public:
MOCK_CONST_METHOD2(adaptProblem, bool(ESpellCastProblem, 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(canBeCast, bool(Problem &));

View File

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

View File

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