diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 01947d4d5..0f7068402 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -507,7 +507,12 @@ void CBattleAI::attemptCastingSpell() { spells::BattleCast temp(cb.get(), hero, spells::Mode::HERO, spell); - for(auto & target : temp.findPotentialTargets()) + if(!spell->isDamage() && spell->getTargetType() == spells::AimType::LOCATION) + continue; + + const bool FAST = true; + + for(auto & target : temp.findPotentialTargets(FAST)) { PossibleSpellcast ps; ps.dest = target; diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index a1a4ddc24..d796134cd 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -600,6 +600,8 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb) if(unit->isTurret()) continue; + auto unitSpeed = unit->speed(turn); + if(turnBattle.battleCanShoot(unit)) { for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1) @@ -614,7 +616,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb) for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1) { - bool reachable = unitReachability.distances[hex] <= unit->speed(turn); + bool reachable = unitReachability.distances[hex] <= unitSpeed; if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK) { @@ -624,7 +626,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb) { for(BattleHex neighbor : hex.neighbouringTiles()) { - reachable = unitReachability.distances[neighbor] <= unit->speed(turn); + reachable = unitReachability.distances[neighbor] <= unitSpeed; if(reachable) break; } diff --git a/AI/BattleAI/PotentialTargets.cpp b/AI/BattleAI/PotentialTargets.cpp index d55e1dfa8..268350b5d 100644 --- a/AI/BattleAI/PotentialTargets.cpp +++ b/AI/BattleAI/PotentialTargets.cpp @@ -84,17 +84,6 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet { return lhs.damageDiff() > rhs.damageDiff(); }); - - if (!possibleAttacks.empty()) - { - auto & bestAp = possibleAttacks[0]; - - logGlobal->debug("Battle AI best: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld", - bestAp.attack.attacker->unitType()->getJsonKey(), - state.battleGetUnitByPos(bestAp.dest)->unitType()->getJsonKey(), - (int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(), - bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg); - } } int64_t PotentialTargets::bestActionValue() const diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index a09cf1965..82a16a276 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -590,7 +590,7 @@ std::vector BattleSpellMechanics::getTargetTypes() const return ret; } -std::vector BattleSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current) const +std::vector BattleSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const { //TODO: BattleSpellMechanics::getPossibleDestinations @@ -602,19 +602,66 @@ std::vector BattleSpellMechanics::getPossibleDestinations(size_t in switch(aimType) { case AimType::CREATURE: - case AimType::LOCATION: - for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) + { + auto stacks = battle()->battleGetAllStacks(); + + for(auto stack : stacks) { - BattleHex dest(i); - if(dest.isAvailable()) + Target tmp = current; + tmp.emplace_back(stack->getPosition()); + + detail::ProblemImpl ignored; + + if(canBeCastAt(tmp, ignored)) + ret.emplace_back(stack->getPosition()); + } + + break; + } + + case AimType::LOCATION: + if(fast) + { + auto stacks = battle()->battleGetAllStacks(); + std::set hexesToCheck; + + for(auto stack : stacks) { - Target tmp = current; - tmp.emplace_back(dest); + hexesToCheck.insert(stack->getPosition()); - detail::ProblemImpl ignored; + for(auto adjacent : stack->getPosition().neighbouringTiles()) + hexesToCheck.insert(adjacent); + } - if(canBeCastAt(tmp, ignored)) - ret.emplace_back(dest); + for(auto hex : hexesToCheck) + { + if(hex.isAvailable()) + { + Target tmp = current; + tmp.emplace_back(hex); + + detail::ProblemImpl ignored; + + if(canBeCastAt(tmp, ignored)) + ret.emplace_back(hex); + } + } + } + else + { + for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) + { + BattleHex dest(i); + if(dest.isAvailable()) + { + Target tmp = current; + tmp.emplace_back(dest); + + detail::ProblemImpl ignored; + + if(canBeCastAt(tmp, ignored)) + ret.emplace_back(dest); + } } } break; diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 7909da805..b89f1f444 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -50,7 +50,7 @@ public: /// Returns vector of all possible destinations for specified aim type /// index - ??? /// current - ??? - std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current) const override final; + std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const override final; /// Returns true if spell can be cast on unit bool isReceptive(const battle::Unit * target) const override; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 4f9d111be..f8709b882 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -326,7 +326,7 @@ bool BattleCast::castIfPossible(ServerCallback * server, Target target) return false; } -std::vector BattleCast::findPotentialTargets() const +std::vector BattleCast::findPotentialTargets(bool fast) const { //TODO: for more than 2 destinations per target much more efficient algorithm is required @@ -354,7 +354,7 @@ std::vector BattleCast::findPotentialTargets() const if(previous.empty()) { Target empty; - destinations = m->getPossibleDestinations(index, targetTypes.at(index), empty); + destinations = m->getPossibleDestinations(index, targetTypes.at(index), empty, fast); for(auto & destination : destinations) { @@ -367,7 +367,7 @@ std::vector BattleCast::findPotentialTargets() const { for(const Target & current : previous) { - destinations = m->getPossibleDestinations(index, targetTypes.at(index), current); + destinations = m->getPossibleDestinations(index, targetTypes.at(index), current, fast); for(auto & destination : destinations) { diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 2abce5b7b..e540bf20d 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -139,7 +139,7 @@ public: ///cast with silent check for permitted cast bool castIfPossible(ServerCallback * server, Target target); - std::vector findPotentialTargets() const; + std::vector findPotentialTargets(bool fast = false) const; private: ///spell school level @@ -199,7 +199,7 @@ public: virtual std::vector getTargetTypes() const = 0; - virtual std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current) const = 0; + virtual std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast = false) const = 0; virtual const Spell * getSpell() const = 0; diff --git a/test/mock/mock_spells_Mechanics.h b/test/mock/mock_spells_Mechanics.h index 05bc11118..e3efe9950 100644 --- a/test/mock/mock_spells_Mechanics.h +++ b/test/mock/mock_spells_Mechanics.h @@ -34,7 +34,7 @@ public: MOCK_CONST_METHOD1(isReceptive, bool(const battle::Unit * )); MOCK_CONST_METHOD0(getTargetTypes, std::vector()); - MOCK_CONST_METHOD3(getPossibleDestinations, std::vector(size_t, AimType, const Target &)); + MOCK_CONST_METHOD4(getPossibleDestinations, std::vector(size_t, AimType, const Target &, bool)); MOCK_CONST_METHOD0(getSpell, const Spell *());