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:
commit
a44bbf4527
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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()];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct BattleHex;
|
||||
class BattleHex;
|
||||
struct CObstacleInstance;
|
||||
class JsonNode;
|
||||
class ObstacleChanges;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"]);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
132
lib/battle/BattleHexArray.cpp
Normal file
132
lib/battle/BattleHexArray.cpp
Normal 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
317
lib/battle/BattleHexArray.h
Normal 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
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 ¶ms) 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 ¶ms) 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;
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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_)
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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*/ \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) };
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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 &));
|
||||
|
||||
};
|
||||
|
||||
|
@ -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 &));
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user