mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Nullkiller AI: further stabilisation, implement staged hero chain (first with limit 0 turns then 1 turn)
This commit is contained in:
committed by
Andrii Danylchenko
parent
6bebb766a6
commit
eea5cb7f0b
@@ -99,7 +99,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
auto hero = path.targetHero;
|
auto hero = path.targetHero;
|
||||||
auto danger = path.getTotalDanger();
|
auto danger = path.getTotalDanger();
|
||||||
|
|
||||||
if(danger == 0 && path.exchangeCount > 1)
|
if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||||
@@ -131,13 +131,13 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
|
|
||||||
for(auto way : waysToVisitObj)
|
for(auto way : waysToVisitObj)
|
||||||
{
|
{
|
||||||
|
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
|
||||||
|
continue;
|
||||||
|
|
||||||
way->evaluationContext.closestWayRatio
|
way->evaluationContext.closestWayRatio
|
||||||
= way->evaluationContext.movementCost / closestWay->evaluationContext.movementCost;
|
= way->evaluationContext.movementCost / closestWay->evaluationContext.movementCost;
|
||||||
|
|
||||||
if(way->hero && ai->nullkiller->canMove(way->hero.h))
|
tasks.push_back(sptr(*way));
|
||||||
{
|
|
||||||
tasks.push_back(sptr(*way));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -227,13 +227,25 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
priority);
|
priority);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, town->visitingHero.get()).setpriority(priority)));
|
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, town->visitingHero.get(), HeroLockedReason::DEFENCE).setpriority(priority)));
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)
|
if(path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)
|
||||||
{
|
{
|
||||||
|
if(ai->nullkiller->arePathHeroesLocked(path))
|
||||||
|
{
|
||||||
|
#if AI_TRACE_LEVEL >= 1
|
||||||
|
logAi->trace("Can not move %s to defend town %s with priority %f. Path is locked.",
|
||||||
|
path.targetHero->name,
|
||||||
|
town->name,
|
||||||
|
priority);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
#if AI_TRACE_LEVEL >= 1
|
#if AI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Move %s to defend town %s with priority %f",
|
logAi->trace("Move %s to defend town %s with priority %f",
|
||||||
path.targetHero->name,
|
path.targetHero->name,
|
||||||
@@ -242,8 +254,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, town).setpriority(priority)));
|
tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, town).setpriority(priority)));
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
#include "lib/mapping/CMap.h" //for victory conditions
|
#include "lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "lib/mapObjects/MapObjects.h" //for victory conditions
|
#include "lib/mapObjects/MapObjects.h" //for victory conditions
|
||||||
#include "lib/CPathfinder.h"
|
#include "lib/CPathfinder.h"
|
||||||
|
#include "../Engine/Nullkiller.h"
|
||||||
|
|
||||||
extern boost::thread_specific_ptr<CCallback> cb;
|
extern boost::thread_specific_ptr<CCallback> cb;
|
||||||
extern boost::thread_specific_ptr<VCAI> ai;
|
extern boost::thread_specific_ptr<VCAI> ai;
|
||||||
@@ -147,17 +148,17 @@ Goals::TGoalVec StartupBehavior::getTasks()
|
|||||||
{
|
{
|
||||||
if(canRecruitHero || ai->ah->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
|
if(canRecruitHero || ai->ah->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero, HeroLockedReason::STARTUP).setpriority(100)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(ai->ah->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
|
else if(ai->ah->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero).setpriority(100)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero, HeroLockedReason::STARTUP).setpriority(100)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(canRecruitHero)
|
else if(canRecruitHero)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero, HeroLockedReason::STARTUP).setpriority(100)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,7 +172,7 @@ Goals::TGoalVec StartupBehavior::getTasks()
|
|||||||
{
|
{
|
||||||
for(const CGTownInstance * town : towns)
|
for(const CGTownInstance * town : towns)
|
||||||
{
|
{
|
||||||
if(town->garrisonHero && town->garrisonHero->movement)
|
if(town->garrisonHero && town->garrisonHero->movement && ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(0.0001f)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(0.0001f)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -79,13 +79,26 @@ void Nullkiller::updateAiState()
|
|||||||
auto activeHeroes = ai->getMyHeroes();
|
auto activeHeroes = ai->getMyHeroes();
|
||||||
|
|
||||||
vstd::erase_if(activeHeroes, [this](const HeroPtr & hero) -> bool{
|
vstd::erase_if(activeHeroes, [this](const HeroPtr & hero) -> bool{
|
||||||
return isHeroLocked(hero.h);
|
auto lockedReason = getHeroLockedReason(hero.h);
|
||||||
|
|
||||||
|
return lockedReason == HeroLockedReason::DEFENCE || lockedReason == HeroLockedReason::STARTUP;
|
||||||
});
|
});
|
||||||
|
|
||||||
ai->ah->updatePaths(activeHeroes, true);
|
ai->ah->updatePaths(activeHeroes, true);
|
||||||
ai->ah->updateHeroRoles();
|
ai->ah->updateHeroRoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Nullkiller::arePathHeroesLocked(const AIPath & path) const
|
||||||
|
{
|
||||||
|
for(auto & node : path.nodes)
|
||||||
|
{
|
||||||
|
if(isHeroLocked(node.targetHero))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Nullkiller::makeTurn()
|
void Nullkiller::makeTurn()
|
||||||
{
|
{
|
||||||
resetAiState();
|
resetAiState();
|
||||||
@@ -97,17 +110,14 @@ void Nullkiller::makeTurn()
|
|||||||
Goals::TGoalVec bestTasks = {
|
Goals::TGoalVec bestTasks = {
|
||||||
choseBestTask(std::make_shared<BuyArmyBehavior>()),
|
choseBestTask(std::make_shared<BuyArmyBehavior>()),
|
||||||
choseBestTask(std::make_shared<CaptureObjectsBehavior>()),
|
choseBestTask(std::make_shared<CaptureObjectsBehavior>()),
|
||||||
choseBestTask(std::make_shared<RecruitHeroBehavior>())
|
choseBestTask(std::make_shared<RecruitHeroBehavior>()),
|
||||||
|
choseBestTask(std::make_shared<DefenceBehavior>())
|
||||||
};
|
};
|
||||||
|
|
||||||
if(cb->getDate(Date::DAY) == 1)
|
if(cb->getDate(Date::DAY) == 1)
|
||||||
{
|
{
|
||||||
bestTasks.push_back(choseBestTask(std::make_shared<StartupBehavior>()));
|
bestTasks.push_back(choseBestTask(std::make_shared<StartupBehavior>()));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
bestTasks.push_back(choseBestTask(std::make_shared<DefenceBehavior>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Goals::TSubgoal bestTask = choseBestTask(bestTasks);
|
Goals::TSubgoal bestTask = choseBestTask(bestTasks);
|
||||||
|
|
||||||
|
@@ -14,12 +14,23 @@
|
|||||||
#include "../Goals/AbstractGoal.h"
|
#include "../Goals/AbstractGoal.h"
|
||||||
#include "../Behaviors/Behavior.h"
|
#include "../Behaviors/Behavior.h"
|
||||||
|
|
||||||
|
enum class HeroLockedReason
|
||||||
|
{
|
||||||
|
NOT_LOCKED = 0,
|
||||||
|
|
||||||
|
STARTUP = 1,
|
||||||
|
|
||||||
|
DEFENCE = 2,
|
||||||
|
|
||||||
|
HERO_CHAIN = 3
|
||||||
|
};
|
||||||
|
|
||||||
class Nullkiller
|
class Nullkiller
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<PriorityEvaluator> priorityEvaluator;
|
std::unique_ptr<PriorityEvaluator> priorityEvaluator;
|
||||||
const CGHeroInstance * activeHero;
|
const CGHeroInstance * activeHero;
|
||||||
std::set<const CGHeroInstance *> lockedHeroes;
|
std::map<const CGHeroInstance *, HeroLockedReason> lockedHeroes;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
|
std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
|
||||||
@@ -28,10 +39,11 @@ public:
|
|||||||
void makeTurn();
|
void makeTurn();
|
||||||
bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; }
|
bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; }
|
||||||
bool isHeroLocked(const CGHeroInstance * hero) const { return vstd::contains(lockedHeroes, hero); }
|
bool isHeroLocked(const CGHeroInstance * hero) const { return vstd::contains(lockedHeroes, hero); }
|
||||||
|
HeroLockedReason getHeroLockedReason(const CGHeroInstance * hero) const { return isHeroLocked(hero) ? lockedHeroes.at(hero) : HeroLockedReason::NOT_LOCKED; }
|
||||||
void setActive(const CGHeroInstance * hero) { activeHero = hero; }
|
void setActive(const CGHeroInstance * hero) { activeHero = hero; }
|
||||||
void lockHero(const CGHeroInstance * hero) { lockedHeroes.insert(hero); }
|
void lockHero(const CGHeroInstance * hero, HeroLockedReason lockReason) { lockedHeroes[hero] = lockReason; }
|
||||||
void unlockHero(const CGHeroInstance * hero) { lockedHeroes.erase(hero); }
|
void unlockHero(const CGHeroInstance * hero) { lockedHeroes.erase(hero); }
|
||||||
bool canMove(const CGHeroInstance * hero) { return hero->movement; }
|
bool arePathHeroesLocked(const AIPath & path) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resetAiState();
|
void resetAiState();
|
||||||
|
@@ -201,10 +201,10 @@ float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
|
|||||||
|
|
||||||
for(auto obj : objectsUnderTreat)
|
for(auto obj : objectsUnderTreat)
|
||||||
{
|
{
|
||||||
objectValue += getStrategicalValue(obj);
|
vstd::amax(objectValue, getStrategicalValue(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
return objectValue + enemy->level / 15.0f;
|
return objectValue / 2.0f + enemy->level / 15.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getStrategicalValue(const CGObjectInstance * target)
|
float getStrategicalValue(const CGObjectInstance * target)
|
||||||
@@ -400,7 +400,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
|||||||
assert(result >= 0);
|
assert(result >= 0);
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
logAi->trace("Evaluated %s, hero %s, loss: %f, turns: %f, gold: %d, army gain: %d, danger: %d, role: %s, result %f",
|
logAi->trace("Evaluated %s, hero %s, loss: %f, turns: %f, gold: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, result %f",
|
||||||
task->name(),
|
task->name(),
|
||||||
hero->name,
|
hero->name,
|
||||||
armyLossPersentage,
|
armyLossPersentage,
|
||||||
@@ -409,6 +409,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
|||||||
armyReward,
|
armyReward,
|
||||||
danger,
|
danger,
|
||||||
heroRole ? "scout" : "main",
|
heroRole ? "scout" : "main",
|
||||||
|
strategicalValue,
|
||||||
result);
|
result);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -23,8 +23,11 @@ extern FuzzyHelper * fh;
|
|||||||
|
|
||||||
using namespace Goals;
|
using namespace Goals;
|
||||||
|
|
||||||
ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero)
|
ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(
|
||||||
:CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero)
|
const CGTownInstance * town,
|
||||||
|
const CGHeroInstance * garrisonHero,
|
||||||
|
HeroLockedReason lockingReason)
|
||||||
|
:CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +93,7 @@ void ExchangeSwapTownHeroes::accept(VCAI * ai)
|
|||||||
cb->swapGarrisonHero(town); // selected hero left in garrison with strongest army
|
cb->swapGarrisonHero(town); // selected hero left in garrison with strongest army
|
||||||
}
|
}
|
||||||
|
|
||||||
ai->nullkiller->lockHero(garrisonHero);
|
ai->nullkiller->lockHero(garrisonHero, lockingReason);
|
||||||
|
|
||||||
if(town->visitingHero && town->visitingHero != garrisonHero)
|
if(town->visitingHero && town->visitingHero != garrisonHero)
|
||||||
{
|
{
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CGoal.h"
|
#include "CGoal.h"
|
||||||
|
#include "..\Engine\Nullkiller.h"
|
||||||
|
|
||||||
namespace Goals
|
namespace Goals
|
||||||
{
|
{
|
||||||
@@ -18,9 +19,13 @@ namespace Goals
|
|||||||
private:
|
private:
|
||||||
const CGTownInstance * town;
|
const CGTownInstance * town;
|
||||||
const CGHeroInstance * garrisonHero;
|
const CGHeroInstance * garrisonHero;
|
||||||
|
HeroLockedReason lockingReason;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero = nullptr);
|
ExchangeSwapTownHeroes(
|
||||||
|
const CGTownInstance * town,
|
||||||
|
const CGHeroInstance * garrisonHero = nullptr,
|
||||||
|
HeroLockedReason lockingReason = HeroLockedReason::NOT_LOCKED);
|
||||||
|
|
||||||
TGoalVec getAllPossibleSubgoals() override
|
TGoalVec getAllPossibleSubgoals() override
|
||||||
{
|
{
|
||||||
|
@@ -69,7 +69,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
if(vstd::contains(blockedIndexes, i))
|
if(vstd::contains(blockedIndexes, i))
|
||||||
{
|
{
|
||||||
blockedIndexes.insert(node.parentIndex);
|
blockedIndexes.insert(node.parentIndex);
|
||||||
ai->nullkiller->lockHero(hero.get());
|
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
{
|
{
|
||||||
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString());
|
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString());
|
||||||
|
|
||||||
ai->nullkiller->lockHero(hero.get());
|
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,13 +148,9 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not lock hero if it is simple one hero chain
|
|
||||||
if(chainPath.exchangeCount == 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// no exception means we were not able to rich the tile
|
// no exception means we were not able to rich the tile
|
||||||
ai->nullkiller->lockHero(hero.get());
|
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN);
|
||||||
blockedIndexes.insert(node.parentIndex);
|
blockedIndexes.insert(node.parentIndex);
|
||||||
}
|
}
|
||||||
catch(goalFulfilledException)
|
catch(goalFulfilledException)
|
||||||
|
@@ -32,5 +32,6 @@ namespace Goals
|
|||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
std::string completeMessage() const override;
|
std::string completeMessage() const override;
|
||||||
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
||||||
|
const AIPath & getPath() const { return chainPath; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -77,7 +77,8 @@ void AINodeStorage::clear()
|
|||||||
{
|
{
|
||||||
actors.clear();
|
actors.clear();
|
||||||
heroChainPass = false;
|
heroChainPass = false;
|
||||||
heroChainTurn = 1;
|
heroChainTurn = 0;
|
||||||
|
heroChainMaxTurns = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
|
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
|
||||||
@@ -251,6 +252,16 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
|||||||
return neighbours;
|
return neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AINodeStorage::increaseHeroChainTurnLimit()
|
||||||
|
{
|
||||||
|
if(heroChainTurn >= heroChainMaxTurns)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
heroChainTurn++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool AINodeStorage::calculateHeroChain()
|
bool AINodeStorage::calculateHeroChain()
|
||||||
{
|
{
|
||||||
heroChainPass = true;
|
heroChainPass = true;
|
||||||
|
@@ -102,6 +102,7 @@ private:
|
|||||||
std::vector<CGPathNode *> heroChain;
|
std::vector<CGPathNode *> heroChain;
|
||||||
bool heroChainPass; // true if we need to calculate hero chain
|
bool heroChainPass; // true if we need to calculate hero chain
|
||||||
int heroChainTurn;
|
int heroChainTurn;
|
||||||
|
int heroChainMaxTurns;
|
||||||
PlayerColor playerID;
|
PlayerColor playerID;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -113,6 +114,8 @@ public:
|
|||||||
|
|
||||||
void initialize(const PathfinderOptions & options, const CGameState * gs) override;
|
void initialize(const PathfinderOptions & options, const CGameState * gs) override;
|
||||||
|
|
||||||
|
bool increaseHeroChainTurnLimit();
|
||||||
|
|
||||||
virtual std::vector<CGPathNode *> getInitialNodes() override;
|
virtual std::vector<CGPathNode *> getInitialNodes() override;
|
||||||
|
|
||||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
virtual std::vector<CGPathNode *> calculateNeighbours(
|
||||||
|
@@ -62,12 +62,25 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
|
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
|
||||||
|
bool continueCalculation = false;
|
||||||
|
|
||||||
do {
|
do
|
||||||
|
{
|
||||||
logAi->trace("Recalculate paths pass %d", pass++);
|
logAi->trace("Recalculate paths pass %d", pass++);
|
||||||
cb->calculatePaths(config);
|
cb->calculatePaths(config);
|
||||||
|
|
||||||
logAi->trace("Recalculate chain pass %d", pass);
|
if(useHeroChain)
|
||||||
useHeroChain = useHeroChain && storage->calculateHeroChain();
|
{
|
||||||
} while(useHeroChain);
|
logAi->trace("Recalculate chain pass %d", pass);
|
||||||
|
|
||||||
|
continueCalculation = storage->calculateHeroChain();
|
||||||
|
|
||||||
|
if(!continueCalculation)
|
||||||
|
{
|
||||||
|
logAi->trace("Increase chain turn limit");
|
||||||
|
|
||||||
|
continueCalculation = storage->increaseHeroChainTurnLimit() && storage->calculateHeroChain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(continueCalculation);
|
||||||
}
|
}
|
||||||
|
@@ -546,15 +546,22 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
|
|||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
if(sop->what == ObjProperty::OWNER)
|
if(sop->what == ObjProperty::OWNER)
|
||||||
{
|
{
|
||||||
if(myCb->getPlayerRelations(playerID, (PlayerColor)sop->val) == PlayerRelations::ENEMIES)
|
auto relations = myCb->getPlayerRelations(playerID, (PlayerColor)sop->val);
|
||||||
|
auto obj = myCb->getObj(sop->id, false);
|
||||||
|
|
||||||
|
if(obj)
|
||||||
{
|
{
|
||||||
//we want to visit objects owned by oppponents
|
if(relations == PlayerRelations::ENEMIES)
|
||||||
auto obj = myCb->getObj(sop->id, false);
|
|
||||||
if(obj)
|
|
||||||
{
|
{
|
||||||
|
//we want to visit objects owned by oppponents
|
||||||
addVisitableObj(obj); // TODO: Remove once save compatability broken. In past owned objects were removed from this set
|
addVisitableObj(obj); // TODO: Remove once save compatability broken. In past owned objects were removed from this set
|
||||||
vstd::erase_if_present(alreadyVisited, obj);
|
vstd::erase_if_present(alreadyVisited, obj);
|
||||||
}
|
}
|
||||||
|
else if(relations == PlayerRelations::SAME_PLAYER && obj->ID == Obj::TOWN && nullkiller)
|
||||||
|
{
|
||||||
|
// reevaluate defence for a new town
|
||||||
|
nullkiller->dangerHitMap->reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user