diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index c55de09b2..bc14014c9 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -280,7 +280,7 @@ int64_t AttackPossibility::evaluateBlockedShootersDmg( std::set checkedUnits; auto attacker = attackInfo.attacker; - auto hexes = attacker->getSurroundingHexes(hex); + const 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 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> defenderStates; diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index 18df749f0..4889afe42 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -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 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; diff --git a/AI/BattleAI/BattleEvaluator.cpp b/AI/BattleAI/BattleEvaluator.cpp index 33f31eb9c..75f39baaa 100644 --- a/AI/BattleAI/BattleEvaluator.cpp +++ b/AI/BattleAI/BattleEvaluator.cpp @@ -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 start) +uint64_t timeElapsed(std::chrono::time_point start) { - auto end = std::chrono::high_resolution_clock::now(); + auto end = std::chrono::steady_clock::now(); return std::chrono::duration_cast(end - start).count(); } @@ -355,7 +355,7 @@ BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, BattleHex hex, } } -BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector 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 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 obstacleHexes; - - auto insertAffected = [](const CObstacleInstance & spellObst, std::set & 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()]; } } diff --git a/AI/BattleAI/BattleEvaluator.h b/AI/BattleAI/BattleEvaluator.h index 4c4bb1e5b..01be3d9f7 100644 --- a/AI/BattleAI/BattleEvaluator.h +++ b/AI/BattleAI/BattleEvaluator.h @@ -51,7 +51,7 @@ public: bool attemptCastingSpell(const CStack * stack); bool canCastSpell(); std::optional findBestCreatureSpell(const CStack * stack); - BattleAction goTowardsNearest(const CStack * stack, std::vector hexes, const PotentialTargets & targets); + BattleAction goTowardsNearest(const CStack * stack, const BattleHexArray & hexes, const PotentialTargets & targets); std::vector getBrokenWallMoatHexes() const; bool hasWorkingTowers() const; void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index 60b6b4b02..49be0c470 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -21,7 +21,7 @@ AttackerValue::AttackerValue() MoveTarget::MoveTarget() : positions(), cachedAttack(), score(EvaluationResult::INEFFECTIVE_SCORE) { - turnsToRich = 1; + turnsToReach = 1; } float BattleExchangeVariant::trackAttack( @@ -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::max(); - for (const battle::Unit* ally : hb->battleAliveUnits()) { + for (const battle::Unit* ally : hb->battleAliveUnits()) + { if (ally == activeStack) continue; if (ally->unitSide() != activeStack->unitSide()) @@ -375,12 +376,13 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable( } // If an ally is closer to the enemy, compute the penaltyMultiplier - if (closestAllyDistance < distance) { + if (closestAllyDistance < distance) + { penaltyMultiplier = closestAllyDistance / distance; // Ratio of distances } - auto turnsToRich = (distance - 1) / speed + 1; - auto hexes = enemy->getSurroundingHexes(); + auto turnsToReach = (distance - 1) / speed + 1; + const BattleHexArray & hexes = enemy->getSurroundingHexes(); auto enemySpeed = enemy->getMovementRange(); auto speedRatio = speed / static_cast(enemySpeed); auto multiplier = (speedRatio > 1 ? 1 : speedRatio) * penaltyMultiplier; @@ -393,16 +395,16 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable( attack.shootersBlockedDmg = 0; // we do not want to count on it, it is not for sure - auto score = calculateExchange(attack, turnsToRich, targets, damageCache, hb); + auto score = calculateExchange(attack, turnsToReach, targets, damageCache, hb); score.enemyDamageReduce *= multiplier; #if BATTLE_TRACE_LEVEL >= 1 - logAi->trace("Multiplier: %f, turns: %d, current score %f, new score %f", multiplier, turnsToRich, result.score, scoreValue(score)); + logAi->trace("Multiplier: %f, turns: %d, current score %f, new score %f", multiplier, turnsToReach, result.score, scoreValue(score)); #endif if(result.score < scoreValue(score) - || (result.turnsToRich > turnsToRich && vstd::isAlmostEqual(result.score, scoreValue(score)))) + || (result.turnsToReach > turnsToReach && vstd::isAlmostEqual(result.score, scoreValue(score)))) { result.score = scoreValue(score); result.positions.clear(); @@ -411,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 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 allReachableUnits = additionalUnits; @@ -915,8 +916,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(std::shared_ptr 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; } diff --git a/AI/BattleAI/BattleExchangeVariant.h b/AI/BattleAI/BattleExchangeVariant.h index dbbbaab3d..0aba34749 100644 --- a/AI/BattleAI/BattleExchangeVariant.h +++ b/AI/BattleAI/BattleExchangeVariant.h @@ -54,9 +54,9 @@ struct AttackerValue struct MoveTarget { float score; - std::vector positions; + BattleHexArray positions; std::optional cachedAttack; - uint8_t turnsToRich; + uint8_t turnsToReach; MoveTarget(); }; diff --git a/AI/BattleAI/PotentialTargets.cpp b/AI/BattleAI/PotentialTargets.cpp index f38415ef7..6b7fbc874 100644 --- a/AI/BattleAI/PotentialTargets.cpp +++ b/AI/BattleAI/PotentialTargets.cpp @@ -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); diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 53919bb70..36c131059 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -69,7 +69,7 @@ public: const CStack * s; int adi; int adr; - std::vector attackFrom; //for melee fight + BattleHexArray attackFrom; //for melee fight EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0) {} void calcDmg(std::shared_ptr cb, const BattleID & battleID, const CStack * ourStack) @@ -107,7 +107,8 @@ static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr 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 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 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 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()]; } } } diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index 302bd5590..0aa45e601 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -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 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 hexes) const; + BattleAction goTowards(const BattleID & battleID, const CStack * stack, BattleHexArray hexes) const; }; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 1462e7977..5bbb30c91 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -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 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; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index ab86dd194..3aefb35d9 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -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 & lines) override; - void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector 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 diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index 6c9784b80..54f8dbcd2 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -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 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 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; @@ -1126,4 +1126,4 @@ void BattleActionsController::pushFrontPossibleAction(PossiblePlayerBattleAction void BattleActionsController::resetCurrentStackPossibleActions() { possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack()); -} +} \ No newline at end of file diff --git a/client/battle/BattleAnimationClasses.cpp b/client/battle/BattleAnimationClasses.cpp index 7d34e8978..196066d1f 100644 --- a/client/battle/BattleAnimationClasses.cpp +++ b/client/battle/BattleAnimationClasses.cpp @@ -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 _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 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 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); } diff --git a/client/battle/BattleAnimationClasses.h b/client/battle/BattleAnimationClasses.h index 83233819b..1314c2350 100644 --- a/client/battle/BattleAnimationClasses.h +++ b/client/battle/BattleAnimationClasses.h @@ -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 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 _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 animation; std::vector positions; - std::vector 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 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 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 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; diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 27c62535e..6e6ad3731 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -267,7 +267,7 @@ void BattleFieldController::showBackgroundImageWithHexes(Canvas & canvas) void BattleFieldController::redrawBackgroundWithHexes() { const CStack *activeStack = owner.stacksController->getActiveStack(); - std::vector 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 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 BattleFieldController::getHighlightedHexesForActiveStack() +BattleHexArray BattleFieldController::getHighlightedHexesForActiveStack() { - std::set result; + BattleHexArray result; if(!owner.stacksController->getActiveStack()) return result; @@ -324,16 +324,16 @@ std::set BattleFieldController::getHighlightedHexesForActiveStack() auto hoveredHex = getHoveredHex(); - std::set 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 BattleFieldController::getMovementRangeForHoveredStack() +BattleHexArray BattleFieldController::getMovementRangeForHoveredStack() { - std::set result; + BattleHexArray result; if (!owner.stacksController->getActiveStack()) return result; @@ -344,16 +344,16 @@ std::set BattleFieldController::getMovementRangeForHoveredStack() auto hoveredStack = getHoveredStack(); if(hoveredStack) { - std::vector 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 BattleFieldController::getHighlightedHexesForSpellRange() +BattleHexArray BattleFieldController::getHighlightedHexesForSpellRange() { - std::set result; + BattleHexArray result; auto hoveredHex = getHoveredHex(); const spells::Caster *caster = nullptr; @@ -378,7 +378,7 @@ std::set BattleFieldController::getHighlightedHexesForSpellRange() return result; } -std::set BattleFieldController::getHighlightedHexesForMovementTarget() +BattleHexArray BattleFieldController::getHighlightedHexesForMovementTarget() { const CStack * stack = owner.stacksController->getActiveStack(); auto hoveredHex = getHoveredHex(); @@ -386,7 +386,7 @@ std::set BattleFieldController::getHighlightedHexesForMovementTarget( if(!stack) return {}; - std::vector 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 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 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 BattleFieldController::getHighlightedHexesForMovementTarget( // Range limit highlight helpers -std::vector BattleFieldController::getRangeHexes(BattleHex sourceHex, uint8_t distance) +BattleHexArray BattleFieldController::getRangeHexes(BattleHex sourceHex, uint8_t distance) { - std::vector rangeHexes; + BattleHexArray rangeHexes; if (!settings["battle"]["rangeLimitHighlightOnHover"].Bool() && !GH.isKeyboardShiftDown()) return rangeHexes; @@ -436,27 +436,27 @@ std::vector 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 BattleFieldController::getRangeLimitHexes(BattleHex hoveredHex, std::vector rangeHexes, uint8_t distanceToLimit) +BattleHexArray BattleFieldController::getRangeLimitHexes(BattleHex hoveredHex, const BattleHexArray & rangeHexes, uint8_t distanceToLimit) { - std::vector 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 & 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> BattleFieldController::getOutsideNeighbourDirectionsForLimitHexes(std::vector wholeRangeHexes, std::vector rangeLimitHexes) +std::vector> BattleFieldController::getOutsideNeighbourDirectionsForLimitHexes( + const BattleHexArray & wholeRangeHexes, const BattleHexArray & rangeLimitHexes) { std::vector> 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 outsideNeighbourDirections; @@ -491,9 +492,7 @@ std::vector> 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> BattleFieldController::calculateRangeLimitH return output; } -void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr rangeLimitImages, std::vector & rangeLimitHexes, std::vector> & rangeLimitHexesHighlights) +void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr rangeLimitImages, BattleHexArray & rangeLimitHexes, std::vector> & rangeLimitHexesHighlights) { - std::vector rangeHexes = getRangeHexes(hoveredHex, distance); + BattleHexArray rangeHexes = getRangeHexes(hoveredHex, distance); rangeLimitHexes = getRangeLimitHexes(hoveredHex, rangeHexes, distance); std::vector> 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 rangedFullDamageLimitHexes; - std::vector shootingRangeLimitHexes; + BattleHexArray rangedFullDamageLimitHexes; + BattleHexArray shootingRangeLimitHexes; std::vector> rangedFullDamageLimitHexesHighlights; std::vector> shootingRangeLimitHexesHighlights; - std::set hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack(); - std::set hoveredSpellHexes = getHighlightedHexesForSpellRange(); - std::set hoveredMoveHexes = getHighlightedHexesForMovementTarget(); + BattleHexArray hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack(); + BattleHexArray hoveredSpellHexes = getHighlightedHexesForSpellRange(); + BattleHexArray hoveredMoveHexes = getHighlightedHexesForMovementTarget(); BattleHex hoveredHex = getHoveredHex(); - std::set hoveredMouseHex = hoveredHex.isValid() ? std::set({ hoveredHex }) : std::set(); + 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) diff --git a/client/battle/BattleFieldController.h b/client/battle/BattleFieldController.h index 4500e95ab..8f21492df 100644 --- a/client/battle/BattleFieldController.h +++ b/client/battle/BattleFieldController.h @@ -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 occupiableHexes; + BattleHexArray occupiableHexes; /// hexes that when in front of a unit cause it's amount box to move back std::array stackCountOutsideHexes; void showHighlightedHex(Canvas & to, std::shared_ptr highlight, BattleHex hex, bool darkBorder); - std::set getHighlightedHexesForActiveStack(); - std::set getMovementRangeForHoveredStack(); - std::set getHighlightedHexesForSpellRange(); - std::set 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 getRangeHexes(BattleHex sourceHex, uint8_t distance); + BattleHexArray getRangeHexes(BattleHex sourceHex, uint8_t distance); /// get only hexes at the limit of a range - std::vector getRangeLimitHexes(BattleHex hoveredHex, std::vector 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 & 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> getOutsideNeighbourDirectionsForLimitHexes(std::vector rangeHexes, std::vector rangeLimitHexes); + std::vector> 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> calculateRangeLimitHighlightImages(std::vector> hexesNeighbourDirections, std::shared_ptr 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 rangeLimitImages, std::vector & rangeLimitHexes, std::vector> & rangeLimitHexesHighlights); + void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr rangeLimitImages, BattleHexArray & rangeLimitHexes, std::vector> & rangeLimitHexesHighlights); void showBackground(Canvas & canvas); void showBackgroundImage(Canvas & canvas); diff --git a/client/battle/BattleInterface.cpp b/client/battle/BattleInterface.cpp index db02f9a2c..da8c39396 100644 --- a/client/battle/BattleInterface.cpp +++ b/client/battle/BattleInterface.cpp @@ -216,7 +216,7 @@ void BattleInterface::stackActivated(const CStack *stack) stacksController->stackActivated(stack); } -void BattleInterface::stackMoved(const CStack *stack, std::vector 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); diff --git a/client/battle/BattleInterface.h b/client/battle/BattleInterface.h index a7c578ecf..a5a2fe538 100644 --- a/client/battle/BattleInterface.h +++ b/client/battle/BattleInterface.h @@ -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 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 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(); diff --git a/client/battle/BattleObstacleController.h b/client/battle/BattleObstacleController.h index 39119cf32..48a7ebfcd 100644 --- a/client/battle/BattleObstacleController.h +++ b/client/battle/BattleObstacleController.h @@ -13,7 +13,7 @@ VCMI_LIB_NAMESPACE_BEGIN -struct BattleHex; +class BattleHex; struct CObstacleInstance; class JsonNode; class ObstacleChanges; diff --git a/client/battle/BattleSiegeController.cpp b/client/battle/BattleSiegeController.cpp index 2f3c4df5e..e698b1aca 100644 --- a/client/battle/BattleSiegeController.cpp +++ b/client/battle/BattleSiegeController.cpp @@ -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; diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 173925244..10f2eaabf 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -491,7 +491,7 @@ void BattleStacksController::stacksAreAttacked(std::vector at owner.waitForAnimations(); } -void BattleStacksController::stackTeleported(const CStack *stack, std::vector 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 destHex, int distance) +void BattleStacksController::stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance) { assert(destHex.size() > 0); owner.checkForAnimations(); diff --git a/client/battle/BattleStacksController.h b/client/battle/BattleStacksController.h index 010602306..6e79bdd4e 100644 --- a/client/battle/BattleStacksController.h +++ b/client/battle/BattleStacksController.h @@ -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 destHex, int distance); //stack with id number moved to destHex - void stackTeleported(const CStack *stack, std::vector 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 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 diff --git a/include/vstd/RNG.h b/include/vstd/RNG.h index 9a4e54a06..9762e730d 100644 --- a/include/vstd/RNG.h +++ b/include/vstd/RNG.h @@ -88,14 +88,15 @@ namespace RandomGeneratorUtil return container.size() - 1; } - template - void randomShuffle(std::vector & container, vstd::RNG & rand) + template + 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)); } } } diff --git a/lib/BattleFieldHandler.cpp b/lib/BattleFieldHandler.cpp index ff265d2e5..583299e8c 100644 --- a/lib/BattleFieldHandler.cpp +++ b/lib/BattleFieldHandler.cpp @@ -38,7 +38,7 @@ std::shared_ptr 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"]); diff --git a/lib/BattleFieldHandler.h b/lib/BattleFieldHandler.h index f95e01c89..9e19213a7 100644 --- a/lib/BattleFieldHandler.h +++ b/lib/BattleFieldHandler.h @@ -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 impassableHexes; + BattleHexArray impassableHexes; AudioPath openingSoundFilename; AudioPath musicFilename; diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index 8581a2f6d..e19a7b012 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -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 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); } diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index a0896014a..9719801d7 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -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 & obstacles) override; - void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector 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; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bd7d3ea1d..6be9b534f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -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 diff --git a/lib/CStack.cpp b/lib/CStack.cpp index 1819e57df..df2a38fa2 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -242,25 +242,25 @@ void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, const bsa.newState.operation = UnitChanges::EOperation::RESET_STATE; } -std::vector 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 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 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 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 CStack::meleeAttackHexes(const battle::Unit * attacker, c if((mask & 2) == 0) { mask |= 2; - res.push_back(otherDefenderPos); + res.insert(otherDefenderPos); } } diff --git a/lib/CStack.h b/lib/CStack.h index d339eba40..23f22cf4f 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -60,7 +60,7 @@ public: std::vector 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 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; @@ -146,4 +146,4 @@ private: const BattleInfo * battle; //do not serialize }; -VCMI_LIB_NAMESPACE_END +VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index ca7d5e86f..3f58ecc88 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -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 & lines){}; - virtual void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector 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 diff --git a/lib/ObstacleHandler.cpp b/lib/ObstacleHandler.cpp index b78f048c9..e8472ca9e 100644 --- a/lib/ObstacleHandler.cpp +++ b/lib/ObstacleHandler.cpp @@ -55,26 +55,25 @@ Obstacle ObstacleInfo::getId() const return obstacle; } -std::vector ObstacleInfo::getBlocked(BattleHex hex) const +BattleHexArray ObstacleInfo::getBlocked(BattleHex hex) const { - std::vector 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; diff --git a/lib/ObstacleHandler.h b/lib/ObstacleHandler.h index fe4dbdac0..e80eb8dd6 100644 --- a/lib/ObstacleHandler.h +++ b/lib/ObstacleHandler.h @@ -13,7 +13,7 @@ #include #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 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; }; diff --git a/lib/battle/AccessibilityInfo.cpp b/lib/battle/AccessibilityInfo.cpp index 22fa5196e..21211392a 100644 --- a/lib/battle/AccessibilityInfo.cpp +++ b/lib/battle/AccessibilityInfo.cpp @@ -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) diff --git a/lib/battle/AccessibilityInfo.h b/lib/battle/AccessibilityInfo.h index 1352c4da2..8377d163b 100644 --- a/lib/battle/AccessibilityInfo.h +++ b/lib/battle/AccessibilityInfo.h @@ -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; +using TBattlefieldTurnsArray = std::array; struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray { - std::map destructibleEnemyTurns; + std::shared_ptr 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 diff --git a/lib/battle/BattleHex.cpp b/lib/battle/BattleHex.cpp index 581cf538f..be7c8b51a 100644 --- a/lib/battle/BattleHex.cpp +++ b/lib/battle/BattleHex.cpp @@ -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 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 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 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::neighbouringTiles() const -{ - std::vector ret; - ret.reserve(6); - for(auto dir : hexagonalDirections()) - checkAndPush(cloneInDirection(dir, false), ret); - return ret; -} - -std::vector BattleHex::allNeighbouringTiles() const -{ - std::vector 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(hex1.getX() + y1 / 2); - int x2 = static_cast(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 & ret) -{ - if(tile.isAvailable()) - ret.push_back(tile); -} - -BattleHex BattleHex::getClosestTile(BattleSide side, BattleHex initialPos, std::set & possibilities) -{ - std::vector 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::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 diff --git a/lib/battle/BattleHex.h b/lib/battle/BattleHex.h index 0f1dc37e4..8ba2b7d3c 100644 --- a/lib/battle/BattleHex.h +++ b/lib/battle/BattleHex.h @@ -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 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 xy); - si16 getX() const; - si16 getY() const; - std::pair 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 xy) + { + setXY(xy); + } - /// returns all valid neighbouring tiles - std::vector neighbouringTiles() const; + [[nodiscard]] bool isValid() const noexcept + { + return hex >= 0 && hex < GameConstants::BFIELD_SIZE; + } + + [[nodiscard]] bool isAvailable() const noexcept //valid position not in first or last column + { + return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH - 1; + } - /// returns all tiles, unavailable tiles will be set as invalid - /// order of returned tiles matches EDir enim - std::vector allNeighbouringTiles() const; + void setX(si16 x) + { + setXY(x, getY()); + } - static EDir mutualPosition(BattleHex hex1, BattleHex hex2); - static uint8_t getDistance(BattleHex hex1, BattleHex hex2); - static void checkAndPush(BattleHex tile, std::vector & ret); - static BattleHex getClosestTile(BattleSide side, BattleHex initialPos, std::set & possibilities); //TODO: vector or set? copying one to another is bad + 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 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 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{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 - void serialize(Handler &h) + void serialize(Handler & h) { h & hex; } - using NeighbouringTiles = std::array; - using NeighbouringTilesCache = std::vector; - - static const NeighbouringTilesCache neighbouringTilesCache; private: - //Constexpr defined array with all directions used in battle - static constexpr auto hexagonalDirections() { - return std::array{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); diff --git a/lib/battle/BattleHexArray.cpp b/lib/battle/BattleHexArray.cpp new file mode 100644 index 000000000..632329bf9 --- /dev/null +++ b/lib/battle/BattleHexArray.cpp @@ -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 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(0); dir <= static_cast(4); dir = static_cast(dir + 1)) + hexes.checkAndPush(hex.cloneInDirection(dir, false)); + + hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)); + hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false)); + hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)); + } + else if(side == BattleSide::DEFENDER) + { + const BattleHex otherHex = h + 1; + + hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)); + + for(auto dir = static_cast(0); dir <= static_cast(4); dir = static_cast(dir + 1)) + hexes.checkAndPush(otherHex.cloneInDirection(dir, false)); + + hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)); + hexes.checkAndPush(hex.cloneInDirection(BattleHex::EDir::LEFT, false)); + } + ret[h] = std::move(hexes); + } + + return ret; +} + +const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::neighbouringTiles = precalculateNeighbouringTiles(); +const BattleHexArray::ArrayOfBattleHexArrays BattleHexArray::allNeighbouringTiles = precalculateAllNeighbouringTiles(); +const std::map BattleHexArray::neighbouringTilesDoubleWide = + { + { BattleSide::ATTACKER, precalculateNeighbouringTilesDoubleWide(BattleSide::ATTACKER) }, + { BattleSide::DEFENDER, precalculateNeighbouringTilesDoubleWide(BattleSide::DEFENDER) } + }; + +VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/battle/BattleHexArray.h b/lib/battle/BattleHexArray.h new file mode 100644 index 000000000..1cc19f601 --- /dev/null +++ b/lib/battle/BattleHexArray.h @@ -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 + +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; + using ArrayOfBattleHexArrays = std::array; + + 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 >> + 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 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 >> + 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 toVector() const noexcept + { + return std::vector(internalStorage.begin(), internalStorage.end()); + } + + template + iterator findIf(Predicate predicate) noexcept + { + return std::find_if(begin(), end(), predicate); + } + + template + const_iterator findIf(Predicate predicate) const noexcept + { + return std::find_if(begin(), end(), predicate); + } + + template + 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 + 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 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 neighbouringTilesDoubleWide; + + static ArrayOfBattleHexArrays precalculateNeighbouringTiles(); + static ArrayOfBattleHexArrays precalculateAllNeighbouringTiles(); + static ArrayOfBattleHexArrays precalculateNeighbouringTilesDoubleWide(BattleSide side); +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 905d8a3a0..95670694a 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -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 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(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 } } diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index a0bf78d14..32a3ec0e5 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -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 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, 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(), 0); + return std::make_pair(BattleHexArray(), 0); } //making the Path - std::vector 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 + auto getShortestPath = [](BattleHex from, BattleHex dest) -> BattleHexArray { //Out early if(from == dest) return {}; - std::vector 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 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 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 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 & turns, battleGetTurnOrder(turns, maxUnits, maxTurns, actualTurn + 1, sideThatLastMoved); } -std::vector 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()); + RETURN_IF_NOT_BATTLE(BattleHexArray()); if(!unit->getPosition().isValid()) //turrets - return std::vector(); + return BattleHexArray(); auto reachability = getReachability(unit); return battleGetAvailableHexes(reachability, unit, obtainMovementRange); } -std::vector 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 ret; + BattleHexArray ret; RETURN_IF_NOT_BATTLE(ret); if(!unit->getPosition().isValid()) //turrets @@ -612,28 +607,27 @@ std::vector CBattleInfoCallback::battleGetAvailableHexes(const Reacha continue; } - ret.emplace_back(i); + ret.insert(i); } return ret; } -std::vector CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, std::vector * attackable) const +BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, BattleHexArray * attackable) const { - std::vector ret = battleGetAvailableHexes(unit, obtainMovementRange); + BattleHexArray ret = battleGetAvailableHexes(unit, obtainMovementRange); if(ret.empty()) return ret; if(addOccupiable && unit->doubleWide()) { - std::vector 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 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 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> 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> CBattleInfoCallback::battl return obstacles; } -std::vector> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set & passed) const +std::vector> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const { auto affectedObstacles = std::vector>(); 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> CBattleInfoCallback::getAl return affectedObstacles; } -bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set & 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 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 & 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 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 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 & 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 CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective) const +BattleHexArray CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective) const { - std::set ret; + BattleHexArray ret; RETURN_IF_NOT_BATTLE(ret); for(auto &oi : battleGetAllObstacles(whichSidePerspective)) @@ -1155,7 +1140,7 @@ std::set 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 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 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( + & 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 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 hexes = destinationTile.neighbouringTiles(); - for(int i = 0; ihasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile)) + if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !attackerPos.getNeighbouringTiles().contains(destinationTile)) { - std::vector 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 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 CBattleInfoCallback::getAttackableBattleHexes() const +BattleHexArray CBattleInfoCallback::getAttackableBattleHexes() const { - std::vector 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; diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index aedba1996..e7c11d5da 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -38,8 +38,8 @@ namespace spells struct DLL_LINKAGE AttackableTiles { - std::set hostileCreaturePositions; - std::set friendlyCreaturePositions; //for Dragon Breath + BattleHexArray hostileCreaturePositions; + BattleHexArray friendlyCreaturePositions; //for Dragon Breath template void serialize(Handler &h) { h & hostileCreaturePositions; @@ -59,9 +59,9 @@ public: std::optional battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw std::vector> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override; - std::vector> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set & passed) const override; + std::vector> 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 & 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 & 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 battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, std::vector * 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 battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const; + BattleHexArray battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const; - std::vector 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 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, 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 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 & accessibleHexes) const; //given hexes will be marked as accessible + AccessibilityInfo getAccessibility(const BattleHexArray & accessibleHexes) const; //given hexes will be marked as accessible std::pair getNearestStack(const battle::Unit * closest) const; BattleHex getAvailableHex(const CreatureID & creID, BattleSide side, int initialPos = -1) const; //find place for adding new stack 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 & obstacles, const ReachabilityInfo::Parameters & params) const; - std::set 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 +VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/battle/CObstacleInstance.cpp b/lib/battle/CObstacleInstance.cpp index 6bb5c65f3..5be15e744 100644 --- a/lib/battle/CObstacleInstance.cpp +++ b/lib/battle/CObstacleInstance.cpp @@ -24,21 +24,21 @@ const ObstacleInfo & CObstacleInstance::getInfo() const return *Obstacle(ID).getInfo(); } -std::vector CObstacleInstance::getBlockedTiles() const +BattleHexArray CObstacleInstance::getBlockedTiles() const { if(blocksTiles()) return getAffectedTiles(); - return std::vector(); + return BattleHexArray(); } -std::vector CObstacleInstance::getStoppingTile() const +BattleHexArray CObstacleInstance::getStoppingTile() const { if(stopsMovement()) return getAffectedTiles(); - return std::vector(); + return BattleHexArray(); } -std::vector CObstacleInstance::getAffectedTiles() const +BattleHexArray CObstacleInstance::getAffectedTiles() const { switch(obstacleType) { @@ -47,7 +47,7 @@ std::vector CObstacleInstance::getAffectedTiles() const return getInfo().getBlocked(pos); default: assert(0); - return std::vector(); + 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 SpellCreatedObstacle::getAffectedTiles() const +BattleHexArray SpellCreatedObstacle::getAffectedTiles() const { return customSize; } @@ -258,4 +266,4 @@ int SpellCreatedObstacle::getAnimationYOffset(int imageHeight) const return offset; } -VCMI_LIB_NAMESPACE_END +VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/battle/CObstacleInstance.h b/lib/battle/CObstacleInstance.h index 3ce98a202..dd9a924cf 100644 --- a/lib/battle/CObstacleInstance.h +++ b/lib/battle/CObstacleInstance.h @@ -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 getBlockedTiles() const; - std::vector 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 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 customSize; + BattleHexArray customSize; SpellCreatedObstacle(); - std::vector getAffectedTiles() const override; + BattleHexArray getAffectedTiles() const override; bool visibleForSide(BattleSide side, bool hasNativeStack) const override; bool blocksTiles() const override; diff --git a/lib/battle/CUnitState.cpp b/lib/battle/CUnitState.cpp index aa1508472..dfeec02b9 100644 --- a/lib/battle/CUnitState.cpp +++ b/lib/battle/CUnitState.cpp @@ -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_) diff --git a/lib/battle/DamageCalculator.cpp b/lib/battle/DamageCalculator.cpp index b6030e87e..419eafb26 100644 --- a/lib/battle/DamageCalculator.cpp +++ b/lib/battle/DamageCalculator.cpp @@ -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(); diff --git a/lib/battle/IBattleInfoCallback.h b/lib/battle/IBattleInfoCallback.h index ca43cb767..546eb9a7e 100644 --- a/lib/battle/IBattleInfoCallback.h +++ b/lib/battle/IBattleInfoCallback.h @@ -11,7 +11,7 @@ #pragma once #include "GameConstants.h" -#include "BattleHex.h" +#include "BattleHexArray.h" #include @@ -81,7 +81,7 @@ public: //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) virtual std::vector> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0; - virtual std::vector> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set & passed) const = 0; + virtual std::vector> getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const = 0; }; diff --git a/lib/battle/ReachabilityInfo.cpp b/lib/battle/ReachabilityInfo.cpp index c0165b7af..7a7bae3ea 100644 --- a/lib/battle/ReachabilityInfo.cpp +++ b/lib/battle/ReachabilityInfo.cpp @@ -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 & 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(); diff --git a/lib/battle/ReachabilityInfo.h b/lib/battle/ReachabilityInfo.h index f0c5ed948..b21c99246 100644 --- a/lib/battle/ReachabilityInfo.h +++ b/lib/battle/ReachabilityInfo.h @@ -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 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 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 & targetHexes, + const BattleHexArray & targetHexes, BattleHex * chosenHex = nullptr) const; uint32_t distToNearestNeighbour( diff --git a/lib/battle/Unit.cpp b/lib/battle/Unit.cpp index 51bf21d77..57a36eb6a 100644 --- a/lib/battle/Unit.cpp +++ b/lib/battle/Unit.cpp @@ -51,55 +51,29 @@ const IBonusBearer* Unit::getBonusBearer() const return this; } -std::vector 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 Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side) +const BattleHexArray & Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side) { - std::vector hexes; - if(twoHex) - { - const BattleHex otherHex = occupiedHex(position, twoHex, side); + if(!twoHex) + return position.getNeighbouringTiles(); - if(side == BattleSide::ATTACKER) - { - for(auto dir = static_cast(0); dir <= static_cast(4); dir = static_cast(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(0); dir <= static_cast(4); dir = static_cast(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 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 targetableHexes; + BattleHexArray targetableHexes; for(auto defenderHex : defenderHexes) { @@ -112,11 +86,9 @@ std::vector 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 Unit::getHexes() const +const BattleHexArray & Unit::getHexes() const { return getHexes(getPosition(), doubleWide(), unitSide()); } -std::vector Unit::getHexes(BattleHex assumedPos) const +const BattleHexArray & Unit::getHexes(BattleHex assumedPos) const { return getHexes(assumedPos, doubleWide(), unitSide()); } -std::vector Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side) +const BattleHexArray & Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side) { - std::vector 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); } diff --git a/lib/battle/Unit.h b/lib/battle/Unit.h index 574a111be..9708b124f 100644 --- a/lib/battle/Unit.h +++ b/lib/battle/Unit.h @@ -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 getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size - std::vector getAttackableHexes(const Unit * attacker) const; - static std::vector 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 getHexes() const; //up to two occupied hexes, starting from front - std::vector getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front - static std::vector 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 diff --git a/lib/bonuses/BonusEnum.h b/lib/bonuses/BonusEnum.h index 1ef515e1f..f2daccac0 100644 --- a/lib/bonuses/BonusEnum.h +++ b/lib/bonuses/BonusEnum.h @@ -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*/ \ diff --git a/lib/bonuses/Limiters.cpp b/lib/bonuses/Limiters.cpp index e5aa2fae3..0334f0127 100644 --- a/lib/bonuses/Limiters.cpp +++ b/lib/bonuses/Limiters.cpp @@ -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 & 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; } diff --git a/lib/bonuses/Limiters.h b/lib/bonuses/Limiters.h index 9ad0e56d2..bafa4f7b0 100644 --- a/lib/bonuses/Limiters.h +++ b/lib/bonuses/Limiters.h @@ -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 applicableHexes; + BattleHexArray applicableHexes; - UnitOnHexLimiter(const std::set & applicableHexes = {}); + UnitOnHexLimiter(const BattleHexArray & applicableHexes = {}); EDecision limit(const BonusLimitationContext &context) const override; JsonNode toJsonNode() const override; diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 8d389cf38..ba7dbf72f 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -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) }; diff --git a/lib/networkPacks/PacksForClientBattle.h b/lib/networkPacks/PacksForClientBattle.h index fcaf85457..916bcd857 100644 --- a/lib/networkPacks/PacksForClientBattle.h +++ b/lib/networkPacks/PacksForClientBattle.h @@ -11,6 +11,7 @@ #include "NetPacksBase.h" #include "BattleChanges.h" +#include "../battle/BattleHexArray.h" #include "../battle/BattleAction.h" #include "../texts/MetaString.h" @@ -170,10 +171,10 @@ struct DLL_LINKAGE BattleStackMoved : public CPackForClient { BattleID battleID = BattleID::NONE; ui32 stack = 0; - std::vector tilesToMove; + BattleHexArray tilesToMove; int distance = 0; bool teleporting = false; - + void applyGs(CGameState * gs) override; void applyBattle(IBattleState * battleState); diff --git a/lib/rmg/RmgPath.cpp b/lib/rmg/RmgPath.cpp index b36628f98..e9ffdcc48 100644 --- a/lib/rmg/RmgPath.cpp +++ b/lib/rmg/RmgPath.cpp @@ -117,6 +117,7 @@ Path Path::search(const Tileset & dst, bool straight, std::function::max(); auto it = distances.find(pos); diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 81a7f1a1b..0b02d5002 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -508,16 +508,16 @@ bool BattleSpellMechanics::counteringSelector(const Bonus * bonus) const return false; } -std::set BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex) const +BattleHexArray BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex) const { using namespace SRSLPraserHelpers; - std::set ret; + BattleHexArray ret; std::vector rng = owner->getLevelInfo(getRangeLevel()).range; for(auto & elem : rng) { - std::set curLayer = getInRange(centralHex, elem, elem); + std::set 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 BattleSpellMechanics::getPossibleDestinations(size_t in if(fast) { auto stacks = battle()->battleGetAllStacks(); - std::set 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 BattleSpellMechanics::rangeInHexes(BattleHex centralHex) const +BattleHexArray BattleSpellMechanics::rangeInHexes(BattleHex centralHex) const { if(isMassive() || !centralHex.isValid()) - return std::vector(1, BattleHex::INVALID); + return BattleHexArray(); Target aimPoint; aimPoint.push_back(Destination(centralHex)); Target spellTarget = transformSpellTarget(aimPoint); - std::set effectRange; + BattleHexArray effectRange; effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * effect, bool & stop) { @@ -681,12 +679,7 @@ std::vector BattleSpellMechanics::rangeInHexes(BattleHex centralHex) } }); - std::vector ret; - ret.reserve(effectRange.size()); - - std::copy(effectRange.begin(), effectRange.end(), std::back_inserter(ret)); - - return ret; + return effectRange; } const Spell * BattleSpellMechanics::getSpell() const diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index a910154d4..860a1487c 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -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 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 & targets, const CSelector & selector); - std::set spellRangeInHexes(BattleHex centralHex) const; + BattleHexArray spellRangeInHexes(BattleHex centralHex) const; Target transformSpellTarget(const Target & aimPoint) const; }; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 79619ac51..f357f2c9b 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -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 & CSpellHandler::getTypeNames() const std::vector CSpellHandler::spellRangeInHexes(std::string input) const { - std::set 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 CSpellHandler::spellRangeInHexes(std::string input) const } } - return std::vector(ret.begin(), ret.end()); + std::vector 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 CSpellHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 77e69ff47..8b82a2eb3 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -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" diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 35fedbf72..84db51277 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.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 rangeInHexes(BattleHex centralHex) const = 0; + virtual BattleHexArray rangeInHexes(BattleHex centralHex) const = 0; virtual std::vector getAffectedStacks(const Target & target) const = 0; virtual bool canBeCast(Problem & problem) const = 0; diff --git a/lib/spells/effects/Catapult.cpp b/lib/spells/effects/Catapult.cpp index 28ec7998e..763ad3045 100644 --- a/lib/spells/effects/Catapult.cpp +++ b/lib/spells/effects/Catapult.cpp @@ -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 diff --git a/lib/spells/effects/Clone.cpp b/lib/spells/effects/Clone.cpp index 03f35ff9d..389b5b65f 100644 --- a/lib/spells/effects/Clone.cpp +++ b/lib/spells/effects/Clone.cpp @@ -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()) { diff --git a/lib/spells/effects/DemonSummon.cpp b/lib/spells/effects/DemonSummon.cpp index b86a87d1a..853a80381 100644 --- a/lib/spells/effects/DemonSummon.cpp +++ b/lib/spells/effects/DemonSummon.cpp @@ -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()) { diff --git a/lib/spells/effects/Effect.h b/lib/spells/effects/Effect.h index d70b6ec7a..721b8ada0 100644 --- a/lib/spells/effects/Effect.h +++ b/lib/spells/effects/Effect.h @@ -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 & types) const = 0; /// Generates list of hexes affected by spell, if spell were to cast at specified target - virtual void adjustAffectedHexes(std::set & 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; diff --git a/lib/spells/effects/LocationEffect.cpp b/lib/spells/effects/LocationEffect.cpp index e1cefc3c0..94b391b0b 100644 --- a/lib/spells/effects/LocationEffect.cpp +++ b/lib/spells/effects/LocationEffect.cpp @@ -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 & types) const } -void LocationEffect::adjustAffectedHexes(std::set & 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); diff --git a/lib/spells/effects/LocationEffect.h b/lib/spells/effects/LocationEffect.h index 1ce98dbff..8a99d163b 100644 --- a/lib/spells/effects/LocationEffect.h +++ b/lib/spells/effects/LocationEffect.h @@ -25,7 +25,7 @@ class LocationEffect : public Effect public: void adjustTargetTypes(std::vector & types) const override; - void adjustAffectedHexes(std::set & 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; diff --git a/lib/spells/effects/Moat.cpp b/lib/spells/effects/Moat.cpp index 889644b88..7cd1c3ebe 100644 --- a/lib/spells/effects/Moat.cpp +++ b/lib/spells/effects/Moat.cpp @@ -32,7 +32,7 @@ namespace spells namespace effects { -static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string & fieldName, std::vector> & moatHexes) +static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string & fieldName, std::vector & 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 & converted) con nb.sid = BonusSourceID(m->getSpellId()); //for all nb.source = BonusSource::SPELL_EFFECT;//for all } - std::set flatMoatHexes; + BattleHexArray flatMoatHexes; for(const auto & moatPatch : moatHexes) - flatMoatHexes.insert(moatPatch.begin(), moatPatch.end()); + flatMoatHexes.insert(moatPatch); nb.limiter = std::make_shared(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()); diff --git a/lib/spells/effects/Moat.h b/lib/spells/effects/Moat.h index c30130743..ac940fc7d 100644 --- a/lib/spells/effects/Moat.h +++ b/lib/spells/effects/Moat.h @@ -25,7 +25,7 @@ class Moat : public Obstacle { private: ObstacleSideOptions sideOptions; //Defender only - std::vector> moatHexes; //Determine number of moat patches and hexes + std::vector moatHexes; //Determine number of moat patches and hexes std::vector> bonus; //For battle-wide bonuses bool dispellable; //For Tower landmines int moatDamage; // Minimal moat damage diff --git a/lib/spells/effects/Obstacle.cpp b/lib/spells/effects/Obstacle.cpp index 40c22a088..027d7041c 100644 --- a/lib/spells/effects/Obstacle.cpp +++ b/lib/spells/effects/Obstacle.cpp @@ -95,7 +95,7 @@ void ObstacleSideOptions::serializeJson(JsonSerializeFormat & handler) handler.serializeInt("offsetY", offsetY); } -void Obstacle::adjustAffectedHexes(std::set & 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 availableTiles; - auto insertAvailable = [&m](const BattleHex & hex, std::vector & 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(); diff --git a/lib/spells/effects/Obstacle.h b/lib/spells/effects/Obstacle.h index b6410aed4..2c3624669 100644 --- a/lib/spells/effects/Obstacle.h +++ b/lib/spells/effects/Obstacle.h @@ -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 & 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; diff --git a/lib/spells/effects/Summon.cpp b/lib/spells/effects/Summon.cpp index e4d51bd5b..160f1020d 100644 --- a/lib/spells/effects/Summon.cpp +++ b/lib/spells/effects/Summon.cpp @@ -28,7 +28,7 @@ namespace spells namespace effects { -void Summon::adjustAffectedHexes(std::set & hexes, const Mechanics * m, const Target & spellTarget) const +void Summon::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const { //no hexes affected } @@ -207,4 +207,4 @@ EffectTarget Summon::transformTarget(const Mechanics * m, const Target & aimPoin } } -VCMI_LIB_NAMESPACE_END +VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/spells/effects/Summon.h b/lib/spells/effects/Summon.h index 017f7fe2e..dce5e1c4a 100644 --- a/lib/spells/effects/Summon.h +++ b/lib/spells/effects/Summon.h @@ -23,7 +23,7 @@ namespace effects class Summon : public Effect { public: - void adjustAffectedHexes(std::set & 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 & types) const override; bool applicable(Problem & problem, const Mechanics * m) const override; diff --git a/lib/spells/effects/Teleport.cpp b/lib/spells/effects/Teleport.cpp index cffe3c2f1..f7139fff2 100644 --- a/lib/spells/effects/Teleport.cpp +++ b/lib/spells/effects/Teleport.cpp @@ -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 tiles; - tiles.push_back(destination); + BattleHexArray tiles; + tiles.insert(destination); pack.tilesToMove = tiles; pack.teleporting = true; server->apply(pack); diff --git a/lib/spells/effects/UnitEffect.cpp b/lib/spells/effects/UnitEffect.cpp index 3598fbbb8..8a3a543d1 100644 --- a/lib/spells/effects/UnitEffect.cpp +++ b/lib/spells/effects/UnitEffect.cpp @@ -30,7 +30,7 @@ void UnitEffect::adjustTargetTypes(std::vector & types) const } -void UnitEffect::adjustAffectedHexes(std::set & 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 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; @@ -278,4 +278,4 @@ void UnitEffect::serializeJsonEffect(JsonSerializeFormat & handler) } } -VCMI_LIB_NAMESPACE_END +VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/spells/effects/UnitEffect.h b/lib/spells/effects/UnitEffect.h index e17d2f7e9..de2b481eb 100644 --- a/lib/spells/effects/UnitEffect.h +++ b/lib/spells/effects/UnitEffect.h @@ -24,7 +24,7 @@ class UnitEffect : public Effect public: void adjustTargetTypes(std::vector & types) const override; - void adjustAffectedHexes(std::set & 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; diff --git a/scripting/lua/LuaSpellEffect.cpp b/scripting/lua/LuaSpellEffect.cpp index 91db47955..e6717f104 100644 --- a/scripting/lua/LuaSpellEffect.cpp +++ b/scripting/lua/LuaSpellEffect.cpp @@ -58,7 +58,7 @@ void LuaSpellEffect::adjustTargetTypes(std::vector & types) const } -void LuaSpellEffect::adjustAffectedHexes(std::set & 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()); diff --git a/scripting/lua/LuaSpellEffect.h b/scripting/lua/LuaSpellEffect.h index 54b11d4c2..5f793c8bf 100644 --- a/scripting/lua/LuaSpellEffect.h +++ b/scripting/lua/LuaSpellEffect.h @@ -49,7 +49,7 @@ public: void adjustTargetTypes(std::vector & types) const override; - void adjustAffectedHexes(std::set & 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; diff --git a/scripting/lua/api/BattleCb.cpp b/scripting/lua/api/BattleCb.cpp index de4e87a83..73942e018 100644 --- a/scripting/lua/api/BattleCb.cpp +++ b/scripting/lua/api/BattleCb.cpp @@ -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)) diff --git a/scripting/lua/api/netpacks/BattleStackMoved.cpp b/scripting/lua/api/netpacks/BattleStackMoved.cpp index 888b1422e..ee7c6ce9c 100644 --- a/scripting/lua/api/netpacks/BattleStackMoved.cpp +++ b/scripting/lua/api/netpacks/BattleStackMoved.cpp @@ -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(); } diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index cdfed2975..1ba3a3cdc 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -635,7 +635,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta //initing necessary tables auto accessibility = battle.getAccessibility(curStack); - std::set 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, 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 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 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)) diff --git a/server/battles/BattleActionProcessor.h b/server/battles/BattleActionProcessor.h index 72744dc96..538f93da4 100644 --- a/server/battles/BattleActionProcessor.h +++ b/server/battles/BattleActionProcessor.h @@ -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; diff --git a/server/battles/BattleFlowProcessor.cpp b/server/battles/BattleFlowProcessor.cpp index 058f84f20..18712bebd 100644 --- a/server/battles/BattleFlowProcessor.cpp +++ b/server/battles/BattleFlowProcessor.cpp @@ -36,7 +36,7 @@ BattleFlowProcessor::BattleFlowProcessor(BattleProcessor * owner, CGameHandler * { } -void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector & 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 summonInfo = stack->getBonus(Selector::type()(BonusType::SUMMON_GUARDIANS)); auto accessibility = battle.getAccessibility(); CreatureID creatureData = summonInfo->subtype.as(); - std::vector targetHexes; + BattleHexArray targetHexes; const bool targetIsBig = stack->unitType()->isDoubleWide(); //target = creature to guard const bool guardianIsBig = creatureData.toCreature()->isDoubleWide(); diff --git a/server/battles/BattleFlowProcessor.h b/server/battles/BattleFlowProcessor.h index 70ef29249..6c50af985 100644 --- a/server/battles/BattleFlowProcessor.h +++ b/server/battles/BattleFlowProcessor.h @@ -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 & 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); diff --git a/test/battle/BattleHexTest.cpp b/test/battle/BattleHexTest.cpp index 075952bba..555f2bd0a 100644 --- a/test/battle/BattleHexTest.cpp +++ b/test/battle/BattleHexTest.cpp @@ -9,29 +9,29 @@ */ #include "StdInc.h" -#include "../lib/battle/BattleHex.h" +#include "../lib/battle/BattleHexArray.h" TEST(BattleHexTest, getNeighbouringTiles) { BattleHex mainHex; - std::vector 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 possibilities; + BattleHexArray possibilities; possibilities.insert(3); possibilities.insert(170); possibilities.insert(100); diff --git a/test/battle/battle_UnitTest.cpp b/test/battle/battle_UnitTest.cpp index b6ead9ab2..556a5ab99 100644 --- a/test/battle/battle_UnitTest.cpp +++ b/test/battle/battle_UnitTest.cpp @@ -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 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 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 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 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 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 expected = + static const BattleHexArray expected = { 60, 61, diff --git a/test/mock/mock_IBattleInfoCallback.h b/test/mock/mock_IBattleInfoCallback.h index f2eb2b35b..63bfa2a9e 100644 --- a/test/mock/mock_IBattleInfoCallback.h +++ b/test/mock/mock_IBattleInfoCallback.h @@ -40,7 +40,7 @@ public: MOCK_CONST_METHOD0(getPlayerID, std::optional()); MOCK_CONST_METHOD2(battleGetAllObstaclesOnPos, std::vector>(BattleHex, bool)); - MOCK_CONST_METHOD2(getAllAffectedObstaclesByStack, std::vector>(const battle::Unit *, const std::set &)); + MOCK_CONST_METHOD2(getAllAffectedObstaclesByStack, std::vector>(const battle::Unit *, const BattleHexArray &)); }; diff --git a/test/mock/mock_spells_Mechanics.h b/test/mock/mock_spells_Mechanics.h index 288c06fa4..7660cfe72 100644 --- a/test/mock/mock_spells_Mechanics.h +++ b/test/mock/mock_spells_Mechanics.h @@ -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)); + MOCK_CONST_METHOD1(rangeInHexes, BattleHexArray(BattleHex)); MOCK_CONST_METHOD1(getAffectedStacks, std::vector(const Target &)); MOCK_CONST_METHOD1(canBeCast, bool(Problem &)); diff --git a/test/scripting/LuaSpellEffectAPITest.cpp b/test/scripting/LuaSpellEffectAPITest.cpp index f9c4a1b7d..f577cb3b8 100644 --- a/test/scripting/LuaSpellEffectAPITest.cpp +++ b/test/scripting/LuaSpellEffectAPITest.cpp @@ -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 toMove(1, hex2); + BattleHexArray toMove = { hex2 }; EXPECT_EQ(pack.tilesToMove, toMove); }; diff --git a/test/scripting/LuaSpellEffectTest.cpp b/test/scripting/LuaSpellEffectTest.cpp index a550f57cc..16fcc2053 100644 --- a/test/scripting/LuaSpellEffectTest.cpp +++ b/test/scripting/LuaSpellEffectTest.cpp @@ -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;