mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
NKAI: optimize clusterization and pathfinding for object graph
This commit is contained in:
parent
017fb204a1
commit
419d6a648b
@ -266,6 +266,28 @@ void pforeachTilePos(const int3 & mapSize, TFunc fn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename TFunc>
|
||||||
|
void pforeachTilePaths(const int3 & mapSize, const Nullkiller * ai, TFunc fn)
|
||||||
|
{
|
||||||
|
for(int z = 0; z < mapSize.z; ++z)
|
||||||
|
{
|
||||||
|
parallel_for(blocked_range<size_t>(0, mapSize.x), [&](const blocked_range<size_t> & r)
|
||||||
|
{
|
||||||
|
int3 pos(0, 0, z);
|
||||||
|
std::vector<AIPath> paths;
|
||||||
|
|
||||||
|
for(pos.x = r.begin(); pos.x != r.end(); ++pos.x)
|
||||||
|
{
|
||||||
|
for(pos.y = 0; pos.y < mapSize.y; ++pos.y)
|
||||||
|
{
|
||||||
|
ai->pathfinder->calculatePathInfo(paths, pos);
|
||||||
|
fn(pos, paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CDistanceSorter
|
class CDistanceSorter
|
||||||
{
|
{
|
||||||
const CGHeroInstance * hero;
|
const CGHeroInstance * hero;
|
||||||
|
@ -82,9 +82,9 @@ void DangerHitMapAnalyzer::updateHitMap()
|
|||||||
|
|
||||||
boost::this_thread::interruption_point();
|
boost::this_thread::interruption_point();
|
||||||
|
|
||||||
pforeachTilePos(mapSize, [&](const int3 & pos)
|
pforeachTilePaths(mapSize, ai, [&](const int3 & pos, const std::vector<AIPath> & paths)
|
||||||
{
|
{
|
||||||
for(AIPath & path : ai->pathfinder->getPathInfo(pos))
|
for(const AIPath & path : paths)
|
||||||
{
|
{
|
||||||
if(path.getFirstBlockedAction())
|
if(path.getFirstBlockedAction())
|
||||||
continue;
|
continue;
|
||||||
@ -194,14 +194,14 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
|||||||
|
|
||||||
ai->pathfinder->updatePaths(townHeroes, ps);
|
ai->pathfinder->updatePaths(townHeroes, ps);
|
||||||
|
|
||||||
pforeachTilePos(mapSize, [&](const int3 & pos)
|
pforeachTilePaths(mapSize, ai, [&](const int3 & pos, const std::vector<AIPath> & paths)
|
||||||
{
|
{
|
||||||
float ourDistance = std::numeric_limits<float>::max();
|
float ourDistance = std::numeric_limits<float>::max();
|
||||||
float enemyDistance = std::numeric_limits<float>::max();
|
float enemyDistance = std::numeric_limits<float>::max();
|
||||||
const CGTownInstance * enemyTown = nullptr;
|
const CGTownInstance * enemyTown = nullptr;
|
||||||
const CGTownInstance * ourTown = nullptr;
|
const CGTownInstance * ourTown = nullptr;
|
||||||
|
|
||||||
for(AIPath & path : ai->pathfinder->getPathInfo(pos))
|
for(const AIPath & path : paths)
|
||||||
{
|
{
|
||||||
if(!path.targetHero || path.getFirstBlockedAction())
|
if(!path.targetHero || path.getFirstBlockedAction())
|
||||||
continue;
|
continue;
|
||||||
|
@ -109,6 +109,7 @@ void HeroManager::update()
|
|||||||
for(auto & hero : myHeroes)
|
for(auto & hero : myHeroes)
|
||||||
{
|
{
|
||||||
scores[hero] = evaluateFightingStrength(hero);
|
scores[hero] = evaluateFightingStrength(hero);
|
||||||
|
knownFightingStrength[hero->id] = hero->getFightingStrength();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto scoreSort = [&](const CGHeroInstance * h1, const CGHeroInstance * h2) -> bool
|
auto scoreSort = [&](const CGHeroInstance * h1, const CGHeroInstance * h2) -> bool
|
||||||
@ -192,6 +193,13 @@ bool HeroManager::heroCapReached() const
|
|||||||
|| heroCount >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP);
|
|| heroCount >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float HeroManager::getFightingStrengthCached(const CGHeroInstance * hero) const
|
||||||
|
{
|
||||||
|
auto cached = knownFightingStrength.find(hero->id);
|
||||||
|
|
||||||
|
return cached != knownFightingStrength.end() ? cached->second : hero->getFightingStrength();
|
||||||
|
}
|
||||||
|
|
||||||
float HeroManager::getMagicStrength(const CGHeroInstance * hero) const
|
float HeroManager::getMagicStrength(const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
auto hasFly = hero->spellbookContainsSpell(SpellID::FLY);
|
auto hasFly = hero->spellbookContainsSpell(SpellID::FLY);
|
||||||
|
@ -20,23 +20,6 @@
|
|||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
|
|
||||||
class DLL_EXPORT IHeroManager //: public: IAbstractManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~IHeroManager() = default;
|
|
||||||
virtual const std::map<HeroPtr, HeroRole> & getHeroRoles() const = 0;
|
|
||||||
virtual int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const = 0;
|
|
||||||
virtual HeroRole getHeroRole(const HeroPtr & hero) const = 0;
|
|
||||||
virtual void update() = 0;
|
|
||||||
virtual float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const = 0;
|
|
||||||
virtual float evaluateHero(const CGHeroInstance * hero) const = 0;
|
|
||||||
virtual bool canRecruitHero(const CGTownInstance * t = nullptr) const = 0;
|
|
||||||
virtual bool heroCapReached() const = 0;
|
|
||||||
virtual const CGHeroInstance * findHeroWithGrail() const = 0;
|
|
||||||
virtual const CGHeroInstance * findWeakHeroToDismiss(uint64_t armyLimit) const = 0;
|
|
||||||
virtual float getMagicStrength(const CGHeroInstance * hero) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_EXPORT ISecondarySkillRule
|
class DLL_EXPORT ISecondarySkillRule
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -55,7 +38,7 @@ public:
|
|||||||
float evaluateSecSkill(const CGHeroInstance * hero, SecondarySkill skill) const;
|
float evaluateSecSkill(const CGHeroInstance * hero, SecondarySkill skill) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_EXPORT HeroManager : public IHeroManager
|
class DLL_EXPORT HeroManager
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static const SecondarySkillEvaluator wariorSkillsScores;
|
static const SecondarySkillEvaluator wariorSkillsScores;
|
||||||
@ -64,20 +47,22 @@ private:
|
|||||||
CCallback * cb; //this is enough, but we downcast from CCallback
|
CCallback * cb; //this is enough, but we downcast from CCallback
|
||||||
const Nullkiller * ai;
|
const Nullkiller * ai;
|
||||||
std::map<HeroPtr, HeroRole> heroRoles;
|
std::map<HeroPtr, HeroRole> heroRoles;
|
||||||
|
std::map<ObjectInstanceID, float> knownFightingStrength;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB), ai(ai) {}
|
HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB), ai(ai) {}
|
||||||
const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
|
const std::map<HeroPtr, HeroRole> & getHeroRoles() const;
|
||||||
HeroRole getHeroRole(const HeroPtr & hero) const override;
|
HeroRole getHeroRole(const HeroPtr & hero) const;
|
||||||
int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;
|
int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const;
|
||||||
void update() override;
|
void update();
|
||||||
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
|
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const;
|
||||||
float evaluateHero(const CGHeroInstance * hero) const override;
|
float evaluateHero(const CGHeroInstance * hero) const;
|
||||||
bool canRecruitHero(const CGTownInstance * t = nullptr) const override;
|
bool canRecruitHero(const CGTownInstance * t = nullptr) const;
|
||||||
bool heroCapReached() const override;
|
bool heroCapReached() const;
|
||||||
const CGHeroInstance * findHeroWithGrail() const override;
|
const CGHeroInstance * findHeroWithGrail() const;
|
||||||
const CGHeroInstance * findWeakHeroToDismiss(uint64_t armyLimit) const override;
|
const CGHeroInstance * findWeakHeroToDismiss(uint64_t armyLimit) const;
|
||||||
float getMagicStrength(const CGHeroInstance * hero) const override;
|
float getMagicStrength(const CGHeroInstance * hero) const;
|
||||||
|
float getFightingStrengthCached(const CGHeroInstance * hero) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float evaluateFightingStrength(const CGHeroInstance * hero) const;
|
float evaluateFightingStrength(const CGHeroInstance * hero) const;
|
||||||
|
@ -90,64 +90,74 @@ std::vector<std::shared_ptr<ObjectCluster>> ObjectClusterizer::getLockedClusters
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<const CGObjectInstance *> ObjectClusterizer::getBlocker(const AIPathNodeInfo & node) const
|
||||||
|
{
|
||||||
|
std::vector<const CGObjectInstance *> blockers = {};
|
||||||
|
|
||||||
|
if(node.layer == EPathfindingLayer::LAND || node.layer == EPathfindingLayer::SAIL)
|
||||||
|
{
|
||||||
|
auto guardPos = ai->cb->getGuardingCreaturePosition(node.coord);
|
||||||
|
|
||||||
|
blockers = ai->cb->getVisitableObjs(node.coord);
|
||||||
|
|
||||||
|
if(guardPos.valid())
|
||||||
|
{
|
||||||
|
auto guard = ai->cb->getTopObj(ai->cb->getGuardingCreaturePosition(node.coord));
|
||||||
|
|
||||||
|
if(guard)
|
||||||
|
{
|
||||||
|
blockers.insert(blockers.begin(), guard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node.specialAction && node.actionIsBlocked)
|
||||||
|
{
|
||||||
|
auto blockerObject = node.specialAction->targetObject();
|
||||||
|
|
||||||
|
if(blockerObject)
|
||||||
|
{
|
||||||
|
blockers.insert(blockers.begin(), blockerObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(blockers.empty())
|
||||||
|
return std::optional< const CGObjectInstance *>();
|
||||||
|
|
||||||
|
auto blocker = blockers.front();
|
||||||
|
|
||||||
|
if(isObjectPassable(ai, blocker))
|
||||||
|
return std::optional< const CGObjectInstance *>();
|
||||||
|
|
||||||
|
if(blocker->ID == Obj::GARRISON
|
||||||
|
|| blocker->ID == Obj::GARRISON2)
|
||||||
|
{
|
||||||
|
if(dynamic_cast<const CArmedInstance *>(blocker)->getArmyStrength() == 0)
|
||||||
|
return std::optional< const CGObjectInstance *>();
|
||||||
|
else
|
||||||
|
return blocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(blocker->ID == Obj::MONSTER
|
||||||
|
|| blocker->ID == Obj::BORDERGUARD
|
||||||
|
|| blocker->ID == Obj::BORDER_GATE
|
||||||
|
|| blocker->ID == Obj::SHIPYARD
|
||||||
|
|| (blocker->ID == Obj::QUEST_GUARD && node.actionIsBlocked))
|
||||||
|
{
|
||||||
|
return blocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::optional< const CGObjectInstance *>();
|
||||||
|
}
|
||||||
|
|
||||||
const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) const
|
const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) const
|
||||||
{
|
{
|
||||||
for(auto node = path.nodes.rbegin(); node != path.nodes.rend(); node++)
|
for(auto node = path.nodes.rbegin(); node != path.nodes.rend(); node++)
|
||||||
{
|
{
|
||||||
std::vector<const CGObjectInstance *> blockers = {};
|
auto blocker = getBlocker(*node);
|
||||||
|
|
||||||
if(node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL)
|
if(blocker)
|
||||||
{
|
return *blocker;
|
||||||
auto guardPos = ai->cb->getGuardingCreaturePosition(node->coord);
|
|
||||||
|
|
||||||
blockers = ai->cb->getVisitableObjs(node->coord);
|
|
||||||
|
|
||||||
if(guardPos.valid())
|
|
||||||
{
|
|
||||||
auto guard = ai->cb->getTopObj(ai->cb->getGuardingCreaturePosition(node->coord));
|
|
||||||
|
|
||||||
if(guard)
|
|
||||||
{
|
|
||||||
blockers.insert(blockers.begin(), guard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(node->specialAction && node->actionIsBlocked)
|
|
||||||
{
|
|
||||||
auto blockerObject = node->specialAction->targetObject();
|
|
||||||
|
|
||||||
if(blockerObject)
|
|
||||||
{
|
|
||||||
blockers.insert(blockers.begin(), blockerObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(blockers.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto blocker = blockers.front();
|
|
||||||
|
|
||||||
if(isObjectPassable(ai, blocker))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(blocker->ID == Obj::GARRISON
|
|
||||||
|| blocker->ID == Obj::GARRISON2)
|
|
||||||
{
|
|
||||||
if(dynamic_cast<const CArmedInstance *>(blocker)->getArmyStrength() == 0)
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
return blocker;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(blocker->ID == Obj::MONSTER
|
|
||||||
|| blocker->ID == Obj::BORDERGUARD
|
|
||||||
|| blocker->ID == Obj::BORDER_GATE
|
|
||||||
|| blocker->ID == Obj::SHIPYARD
|
|
||||||
|| (blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked))
|
|
||||||
{
|
|
||||||
return blocker;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -230,10 +240,12 @@ void ObjectClusterizer::clusterize()
|
|||||||
blocked_range<size_t> r(0, objs.size());
|
blocked_range<size_t> r(0, objs.size());
|
||||||
#endif
|
#endif
|
||||||
auto priorityEvaluator = ai->priorityEvaluators->acquire();
|
auto priorityEvaluator = ai->priorityEvaluators->acquire();
|
||||||
|
auto heroes = ai->cb->getHeroesInfo();
|
||||||
|
std::vector<AIPath> pathCache;
|
||||||
|
|
||||||
for(int i = r.begin(); i != r.end(); i++)
|
for(int i = r.begin(); i != r.end(); i++)
|
||||||
{
|
{
|
||||||
clusterizeObject(objs[i], priorityEvaluator.get());
|
clusterizeObject(objs[i], priorityEvaluator.get(), pathCache, heroes);
|
||||||
}
|
}
|
||||||
#if NKAI_TRACE_LEVEL == 0
|
#if NKAI_TRACE_LEVEL == 0
|
||||||
});
|
});
|
||||||
@ -257,7 +269,11 @@ void ObjectClusterizer::clusterize()
|
|||||||
logAi->trace("Clusterization complete in %ld", timeElapsed(start));
|
logAi->trace("Clusterization complete in %ld", timeElapsed(start));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectClusterizer::clusterizeObject(const CGObjectInstance * obj, PriorityEvaluator * priorityEvaluator)
|
void ObjectClusterizer::clusterizeObject(
|
||||||
|
const CGObjectInstance * obj,
|
||||||
|
PriorityEvaluator * priorityEvaluator,
|
||||||
|
std::vector<AIPath> & pathCache,
|
||||||
|
std::vector<const CGHeroInstance *> & heroes)
|
||||||
{
|
{
|
||||||
if(!shouldVisitObject(obj))
|
if(!shouldVisitObject(obj))
|
||||||
{
|
{
|
||||||
@ -271,9 +287,14 @@ void ObjectClusterizer::clusterizeObject(const CGObjectInstance * obj, PriorityE
|
|||||||
logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
|
logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto paths = ai->pathfinder->getPathInfo(obj->visitablePos(), true);
|
if(ai->settings->isObjectGraphAllowed())
|
||||||
|
{
|
||||||
|
ai->pathfinder->calculateQuickPathsWithBlocker(pathCache, heroes, obj->visitablePos());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ai->pathfinder->calculatePathInfo(pathCache, obj->visitablePos(), false);
|
||||||
|
|
||||||
if(paths.empty())
|
if(pathCache.empty())
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("No paths found.");
|
logAi->trace("No paths found.");
|
||||||
@ -281,17 +302,17 @@ void ObjectClusterizer::clusterizeObject(const CGObjectInstance * obj, PriorityE
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
|
std::sort(pathCache.begin(), pathCache.end(), [](const AIPath & p1, const AIPath & p2) -> bool
|
||||||
{
|
{
|
||||||
return p1.movementCost() < p2.movementCost();
|
return p1.movementCost() < p2.movementCost();
|
||||||
});
|
});
|
||||||
|
|
||||||
if(vstd::contains(IgnoredObjectTypes, obj->ID))
|
if(vstd::contains(IgnoredObjectTypes, obj->ID))
|
||||||
{
|
{
|
||||||
farObjects.addObject(obj, paths.front(), 0);
|
farObjects.addObject(obj, pathCache.front(), 0);
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Object ignored. Moved to far objects with path %s", paths.front().toString());
|
logAi->trace("Object ignored. Moved to far objects with path %s", pathCache.front().toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -299,12 +320,25 @@ void ObjectClusterizer::clusterizeObject(const CGObjectInstance * obj, PriorityE
|
|||||||
|
|
||||||
std::set<const CGHeroInstance *> heroesProcessed;
|
std::set<const CGHeroInstance *> heroesProcessed;
|
||||||
|
|
||||||
for(auto & path : paths)
|
for(auto & path : pathCache)
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Checking path %s", path.toString());
|
logAi->trace("Checking path %s", path.toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if(ai->heroManager->getHeroRole(path.targetHero) == HeroRole::SCOUT)
|
||||||
|
{
|
||||||
|
if(path.movementCost() > 2.0f)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(path.movementCost() > 4.0f)
|
||||||
|
{
|
||||||
|
auto strategicalValue = valueEvaluator.getStrategicalValue(obj);
|
||||||
|
|
||||||
|
if(strategicalValue < 0.5f)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(!shouldVisit(ai, path.targetHero, obj))
|
if(!shouldVisit(ai, path.targetHero, obj))
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../Pathfinding/AINodeStorage.h"
|
#include "../Pathfinding/AINodeStorage.h"
|
||||||
|
#include "../Engine/PriorityEvaluator.h"
|
||||||
|
|
||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
@ -49,8 +50,6 @@ public:
|
|||||||
|
|
||||||
using ClusterMap = tbb::concurrent_hash_map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>>;
|
using ClusterMap = tbb::concurrent_hash_map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>>;
|
||||||
|
|
||||||
class PriorityEvaluator;
|
|
||||||
|
|
||||||
class ObjectClusterizer
|
class ObjectClusterizer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -60,6 +59,7 @@ private:
|
|||||||
ObjectCluster farObjects;
|
ObjectCluster farObjects;
|
||||||
ClusterMap blockedObjects;
|
ClusterMap blockedObjects;
|
||||||
const Nullkiller * ai;
|
const Nullkiller * ai;
|
||||||
|
RewardEvaluator valueEvaluator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void clusterize();
|
void clusterize();
|
||||||
@ -67,12 +67,17 @@ public:
|
|||||||
std::vector<const CGObjectInstance *> getFarObjects() const;
|
std::vector<const CGObjectInstance *> getFarObjects() const;
|
||||||
std::vector<std::shared_ptr<ObjectCluster>> getLockedClusters() const;
|
std::vector<std::shared_ptr<ObjectCluster>> getLockedClusters() const;
|
||||||
const CGObjectInstance * getBlocker(const AIPath & path) const;
|
const CGObjectInstance * getBlocker(const AIPath & path) const;
|
||||||
|
std::optional<const CGObjectInstance *> getBlocker(const AIPathNodeInfo & node) const;
|
||||||
|
|
||||||
ObjectClusterizer(const Nullkiller * ai): ai(ai) {}
|
ObjectClusterizer(const Nullkiller * ai): ai(ai), valueEvaluator(ai) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool shouldVisitObject(const CGObjectInstance * obj) const;
|
bool shouldVisitObject(const CGObjectInstance * obj) const;
|
||||||
void clusterizeObject(const CGObjectInstance * obj, PriorityEvaluator * priorityEvaluator);
|
void clusterizeObject(
|
||||||
|
const CGObjectInstance * obj,
|
||||||
|
PriorityEvaluator * priorityEvaluator,
|
||||||
|
std::vector<AIPath> & pathCache,
|
||||||
|
std::vector<const CGHeroInstance *> & heroes);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -180,6 +180,8 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
|||||||
|
|
||||||
logAi->debug("Scanning objects, count %d", objs.size());
|
logAi->debug("Scanning objects, count %d", objs.size());
|
||||||
|
|
||||||
|
std::vector<AIPath> paths;
|
||||||
|
|
||||||
for(auto objToVisit : objs)
|
for(auto objToVisit : objs)
|
||||||
{
|
{
|
||||||
if(!objectMatchesFilter(objToVisit))
|
if(!objectMatchesFilter(objToVisit))
|
||||||
@ -193,7 +195,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
|||||||
bool useObjectGraph = ai->nullkiller->settings->isObjectGraphAllowed()
|
bool useObjectGraph = ai->nullkiller->settings->isObjectGraphAllowed()
|
||||||
&& ai->nullkiller->getScanDepth() != ScanDepth::SMALL;
|
&& ai->nullkiller->getScanDepth() != ScanDepth::SMALL;
|
||||||
|
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos, useObjectGraph);
|
ai->nullkiller->pathfinder->calculatePathInfo(paths, pos, useObjectGraph);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||||
std::shared_ptr<ExecuteHeroChain> closestWay;
|
std::shared_ptr<ExecuteHeroChain> closestWay;
|
||||||
|
@ -43,6 +43,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
|
|||||||
{
|
{
|
||||||
auto center = cluster->calculateCenter();
|
auto center = cluster->calculateCenter();
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(center->visitablePos(), ai->nullkiller->settings->isObjectGraphAllowed());
|
auto paths = ai->nullkiller->pathfinder->getPathInfo(center->visitablePos(), ai->nullkiller->settings->isObjectGraphAllowed());
|
||||||
|
|
||||||
auto blockerPos = cluster->blocker->visitablePos();
|
auto blockerPos = cluster->blocker->visitablePos();
|
||||||
std::vector<AIPath> blockerPaths;
|
std::vector<AIPath> blockerPaths;
|
||||||
|
|
||||||
|
@ -35,12 +35,14 @@ Goals::TGoalVec StayAtTownBehavior::decompose() const
|
|||||||
if(!towns.size())
|
if(!towns.size())
|
||||||
return tasks;
|
return tasks;
|
||||||
|
|
||||||
|
std::vector<AIPath> paths;
|
||||||
|
|
||||||
for(auto town : towns)
|
for(auto town : towns)
|
||||||
{
|
{
|
||||||
if(!town->hasBuilt(BuildingID::MAGES_GUILD_1))
|
if(!town->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
|
ai->nullkiller->pathfinder->calculatePathInfo(paths, town->visitablePos());
|
||||||
|
|
||||||
for(auto & path : paths)
|
for(auto & path : paths)
|
||||||
{
|
{
|
||||||
|
@ -609,6 +609,14 @@ bool AINodeStorage::selectNextActor()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t AINodeStorage::evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
|
||||||
|
{
|
||||||
|
float fightingStrength = ai->heroManager->getFightingStrengthCached(hero);
|
||||||
|
double ratio = (double)danger / (armyValue * fightingStrength);
|
||||||
|
|
||||||
|
return (uint64_t)(armyValue * ratio * ratio);
|
||||||
|
}
|
||||||
|
|
||||||
void HeroChainCalculationTask::cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const
|
void HeroChainCalculationTask::cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const
|
||||||
{
|
{
|
||||||
vstd::erase_if(result, [&](const ExchangeCandidate & chainInfo) -> bool
|
vstd::erase_if(result, [&](const ExchangeCandidate & chainInfo) -> bool
|
||||||
@ -1326,12 +1334,8 @@ bool AINodeStorage::isTileAccessible(const HeroPtr & hero, const int3 & pos, con
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
|
void AINodeStorage::calculateChainInfo(std::vector<AIPath> & paths, const int3 & pos, bool isOnLand) const
|
||||||
{
|
{
|
||||||
std::vector<AIPath> paths;
|
|
||||||
|
|
||||||
paths.reserve(AIPathfinding::NUM_CHAINS / 4);
|
|
||||||
|
|
||||||
auto layer = isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL;
|
auto layer = isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL;
|
||||||
auto chains = nodes.get(pos);
|
auto chains = nodes.get(pos);
|
||||||
|
|
||||||
@ -1346,7 +1350,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AIPath path;
|
AIPath & path = paths.emplace_back();
|
||||||
|
|
||||||
path.targetHero = node.actor->hero;
|
path.targetHero = node.actor->hero;
|
||||||
path.heroArmy = node.actor->creatureSet;
|
path.heroArmy = node.actor->creatureSet;
|
||||||
@ -1357,11 +1361,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
|||||||
path.exchangeCount = node.actor->actorExchangeCount;
|
path.exchangeCount = node.actor->actorExchangeCount;
|
||||||
|
|
||||||
fillChainInfo(&node, path, -1);
|
fillChainInfo(&node, path, -1);
|
||||||
|
|
||||||
paths.push_back(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return paths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const
|
void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const
|
||||||
|
@ -265,7 +265,7 @@ public:
|
|||||||
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
||||||
|
|
||||||
std::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
|
std::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
|
||||||
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
|
void calculateChainInfo(std::vector<AIPath> & result, const int3 & pos, bool isOnLand) const;
|
||||||
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
|
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
|
||||||
void setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes);
|
void setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes);
|
||||||
void setScoutTurnDistanceLimit(uint8_t distanceLimit) { turnDistanceLimit[HeroRole::SCOUT] = distanceLimit; }
|
void setScoutTurnDistanceLimit(uint8_t distanceLimit) { turnDistanceLimit[HeroRole::SCOUT] = distanceLimit; }
|
||||||
@ -283,12 +283,7 @@ public:
|
|||||||
return dangerEvaluator->evaluateDanger(tile, hero, checkGuards);
|
return dangerEvaluator->evaluateDanger(tile, hero, checkGuards);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
|
uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const;
|
||||||
{
|
|
||||||
double ratio = (double)danger / (armyValue * hero->getFightingStrength());
|
|
||||||
|
|
||||||
return (uint64_t)(armyValue * ratio * ratio);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline EPathAccessibility getAccessibility(const int3 & tile, EPathfindingLayer layer) const
|
inline EPathAccessibility getAccessibility(const int3 & tile, EPathfindingLayer layer) const
|
||||||
{
|
{
|
||||||
|
@ -33,16 +33,31 @@ bool AIPathfinder::isTileAccessible(const HeroPtr & hero, const int3 & tile) con
|
|||||||
|| storage->isTileAccessible(hero, tile, EPathfindingLayer::SAIL);
|
|| storage->isTileAccessible(hero, tile, EPathfindingLayer::SAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile, bool includeGraph) const
|
void AIPathfinder::calculateQuickPathsWithBlocker(std::vector<AIPath> & result, const std::vector<const CGHeroInstance *> & heroes, const int3 & tile)
|
||||||
|
{
|
||||||
|
result.clear();
|
||||||
|
|
||||||
|
for(auto hero : heroes)
|
||||||
|
{
|
||||||
|
auto graph = heroGraphs.find(hero->id);
|
||||||
|
|
||||||
|
if(graph != heroGraphs.end())
|
||||||
|
graph->second->quickAddChainInfoWithBlocker(result, tile, hero, ai);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIPathfinder::calculatePathInfo(std::vector<AIPath> & result, const int3 & tile, bool includeGraph) const
|
||||||
{
|
{
|
||||||
const TerrainTile * tileInfo = cb->getTile(tile, false);
|
const TerrainTile * tileInfo = cb->getTile(tile, false);
|
||||||
|
|
||||||
|
result.clear();
|
||||||
|
|
||||||
if(!tileInfo)
|
if(!tileInfo)
|
||||||
{
|
{
|
||||||
return std::vector<AIPath>();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto info = storage->getChainInfo(tile, !tileInfo->isWater());
|
storage->calculateChainInfo(result, tile, !tileInfo->isWater());
|
||||||
|
|
||||||
if(includeGraph)
|
if(includeGraph)
|
||||||
{
|
{
|
||||||
@ -51,11 +66,9 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile, bool includeGra
|
|||||||
auto graph = heroGraphs.find(hero->id);
|
auto graph = heroGraphs.find(hero->id);
|
||||||
|
|
||||||
if(graph != heroGraphs.end())
|
if(graph != heroGraphs.end())
|
||||||
graph->second.addChainInfo(info, tile, hero, ai);
|
graph->second->addChainInfo(result, tile, hero, ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIPathfinder::updatePaths(const std::map<const CGHeroInstance *, HeroRole> & heroes, PathfinderSettings pathfinderSettings)
|
void AIPathfinder::updatePaths(const std::map<const CGHeroInstance *, HeroRole> & heroes, PathfinderSettings pathfinderSettings)
|
||||||
@ -134,21 +147,24 @@ void AIPathfinder::updateGraphs(const std::map<const CGHeroInstance *, HeroRole>
|
|||||||
|
|
||||||
for(auto hero : heroes)
|
for(auto hero : heroes)
|
||||||
{
|
{
|
||||||
if(heroGraphs.try_emplace(hero.first->id, GraphPaths()).second)
|
if(heroGraphs.try_emplace(hero.first->id).second)
|
||||||
|
{
|
||||||
|
heroGraphs[hero.first->id] = std::make_unique<GraphPaths>();
|
||||||
heroesVector.push_back(hero.first);
|
heroesVector.push_back(hero.first);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parallel_for(blocked_range<size_t>(0, heroesVector.size()), [this, &heroesVector](const blocked_range<size_t> & r)
|
parallel_for(blocked_range<size_t>(0, heroesVector.size()), [this, &heroesVector](const blocked_range<size_t> & r)
|
||||||
{
|
{
|
||||||
for(auto i = r.begin(); i != r.end(); i++)
|
for(auto i = r.begin(); i != r.end(); i++)
|
||||||
heroGraphs.at(heroesVector[i]->id).calculatePaths(heroesVector[i], ai);
|
heroGraphs.at(heroesVector[i]->id)->calculatePaths(heroesVector[i], ai);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(NKAI_GRAPH_TRACE_LEVEL >= 1)
|
if(NKAI_GRAPH_TRACE_LEVEL >= 1)
|
||||||
{
|
{
|
||||||
for(auto hero : heroes)
|
for(auto hero : heroes)
|
||||||
{
|
{
|
||||||
heroGraphs[hero.first->id].dumpToLog();
|
heroGraphs[hero.first->id]->dumpToLog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,20 +40,30 @@ private:
|
|||||||
std::shared_ptr<AINodeStorage> storage;
|
std::shared_ptr<AINodeStorage> storage;
|
||||||
CPlayerSpecificInfoCallback * cb;
|
CPlayerSpecificInfoCallback * cb;
|
||||||
Nullkiller * ai;
|
Nullkiller * ai;
|
||||||
std::map<ObjectInstanceID, GraphPaths> heroGraphs;
|
std::map<ObjectInstanceID, std::unique_ptr<GraphPaths>> heroGraphs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
|
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
|
||||||
std::vector<AIPath> getPathInfo(const int3 & tile, bool includeGraph = false) const;
|
void calculatePathInfo(std::vector<AIPath> & paths, const int3 & tile, bool includeGraph = false) const;
|
||||||
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
|
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
|
||||||
void updatePaths(const std::map<const CGHeroInstance *, HeroRole> & heroes, PathfinderSettings pathfinderSettings);
|
void updatePaths(const std::map<const CGHeroInstance *, HeroRole> & heroes, PathfinderSettings pathfinderSettings);
|
||||||
void updateGraphs(const std::map<const CGHeroInstance *, HeroRole> & heroes);
|
void updateGraphs(const std::map<const CGHeroInstance *, HeroRole> & heroes);
|
||||||
|
void calculateQuickPathsWithBlocker(std::vector<AIPath> & result, const std::vector<const CGHeroInstance *> & heroes, const int3 & tile);
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
std::shared_ptr<AINodeStorage>getStorage()
|
std::shared_ptr<AINodeStorage>getStorage()
|
||||||
{
|
{
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<AIPath> getPathInfo(const int3 & tile, bool includeGraph = false)
|
||||||
|
{
|
||||||
|
std::vector<AIPath> result;
|
||||||
|
|
||||||
|
calculatePathInfo(result, tile, includeGraph);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ class ObjectGraphCalculator
|
|||||||
private:
|
private:
|
||||||
ObjectGraph * target;
|
ObjectGraph * target;
|
||||||
const Nullkiller * ai;
|
const Nullkiller * ai;
|
||||||
|
std::mutex syncLock;
|
||||||
|
|
||||||
std::map<const CGHeroInstance *, HeroRole> actors;
|
std::map<const CGHeroInstance *, HeroRole> actors;
|
||||||
std::map<const CGHeroInstance *, const CGObjectInstance *> actorObjectMap;
|
std::map<const CGHeroInstance *, const CGObjectInstance *> actorObjectMap;
|
||||||
@ -41,7 +42,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectGraphCalculator(ObjectGraph * target, const Nullkiller * ai)
|
ObjectGraphCalculator(ObjectGraph * target, const Nullkiller * ai)
|
||||||
:ai(ai), target(target)
|
:ai(ai), target(target), syncLock()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,15 +66,17 @@ public:
|
|||||||
{
|
{
|
||||||
updatePaths();
|
updatePaths();
|
||||||
|
|
||||||
foreach_tile_pos(ai->cb.get(), [this](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
std::vector<AIPath> pathCache;
|
||||||
|
|
||||||
|
foreach_tile_pos(ai->cb.get(), [this, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
||||||
{
|
{
|
||||||
calculateConnections(pos);
|
calculateConnections(pos, pathCache);
|
||||||
});
|
});
|
||||||
|
|
||||||
removeExtraConnections();
|
removeExtraConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
float getNeighborConnectionsCost(const int3 & pos)
|
float getNeighborConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache)
|
||||||
{
|
{
|
||||||
float neighborCost = std::numeric_limits<float>::max();
|
float neighborCost = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
@ -85,9 +88,11 @@ public:
|
|||||||
foreach_neighbour(
|
foreach_neighbour(
|
||||||
ai->cb.get(),
|
ai->cb.get(),
|
||||||
pos,
|
pos,
|
||||||
[this, &neighborCost](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
|
[this, &neighborCost, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
|
||||||
{
|
{
|
||||||
auto costTotal = this->getConnectionsCost(neighbor);
|
ai->pathfinder->calculatePathInfo(pathCache, neighbor);
|
||||||
|
|
||||||
|
auto costTotal = this->getConnectionsCost(pathCache);
|
||||||
|
|
||||||
if(costTotal.connectionsCount > 2 && costTotal.avg < neighborCost)
|
if(costTotal.connectionsCount > 2 && costTotal.avg < neighborCost)
|
||||||
{
|
{
|
||||||
@ -105,7 +110,7 @@ public:
|
|||||||
|
|
||||||
void addMinimalDistanceJunctions()
|
void addMinimalDistanceJunctions()
|
||||||
{
|
{
|
||||||
foreach_tile_pos(ai->cb.get(), [this](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
pforeachTilePaths(ai->cb->getMapSize(), ai, [this](const int3 & pos, std::vector<AIPath> & paths)
|
||||||
{
|
{
|
||||||
if(target->hasNodeAt(pos))
|
if(target->hasNodeAt(pos))
|
||||||
return;
|
return;
|
||||||
@ -113,12 +118,12 @@ public:
|
|||||||
if(ai->cb->getGuardingCreaturePosition(pos).valid())
|
if(ai->cb->getGuardingCreaturePosition(pos).valid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ConnectionCostInfo currentCost = getConnectionsCost(pos);
|
ConnectionCostInfo currentCost = getConnectionsCost(paths);
|
||||||
|
|
||||||
if(currentCost.connectionsCount <= 2)
|
if(currentCost.connectionsCount <= 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float neighborCost = getNeighborConnectionsCost(pos);
|
float neighborCost = getNeighborConnectionsCost(pos, paths);
|
||||||
|
|
||||||
if(currentCost.avg < neighborCost)
|
if(currentCost.avg < neighborCost)
|
||||||
{
|
{
|
||||||
@ -139,20 +144,20 @@ private:
|
|||||||
ai->pathfinder->updatePaths(actors, ps);
|
ai->pathfinder->updatePaths(actors, ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateConnections(const int3 & pos)
|
void calculateConnections(const int3 & pos, std::vector<AIPath> & pathCache)
|
||||||
{
|
{
|
||||||
if(target->hasNodeAt(pos))
|
if(target->hasNodeAt(pos))
|
||||||
{
|
{
|
||||||
foreach_neighbour(
|
foreach_neighbour(
|
||||||
ai->cb.get(),
|
ai->cb.get(),
|
||||||
pos,
|
pos,
|
||||||
[this, &pos](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
|
[this, &pos, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
|
||||||
{
|
{
|
||||||
if(target->hasNodeAt(neighbor))
|
if(target->hasNodeAt(neighbor))
|
||||||
{
|
{
|
||||||
auto paths = ai->pathfinder->getPathInfo(neighbor);
|
ai->pathfinder->calculatePathInfo(pathCache, neighbor);
|
||||||
|
|
||||||
for(auto & path : paths)
|
for(auto & path : pathCache)
|
||||||
{
|
{
|
||||||
if(pos == path.targetHero->visitablePos())
|
if(pos == path.targetHero->visitablePos())
|
||||||
{
|
{
|
||||||
@ -166,11 +171,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto guardPos = ai->cb->getGuardingCreaturePosition(pos);
|
auto guardPos = ai->cb->getGuardingCreaturePosition(pos);
|
||||||
auto paths = ai->pathfinder->getPathInfo(pos);
|
|
||||||
|
ai->pathfinder->calculatePathInfo(pathCache, pos);
|
||||||
|
|
||||||
for(AIPath & path1 : paths)
|
for(AIPath & path1 : pathCache)
|
||||||
{
|
{
|
||||||
for(AIPath & path2 : paths)
|
for(AIPath & path2 : pathCache)
|
||||||
{
|
{
|
||||||
if(path1.targetHero == path2.targetHero)
|
if(path1.targetHero == path2.targetHero)
|
||||||
continue;
|
continue;
|
||||||
@ -287,6 +293,8 @@ private:
|
|||||||
|
|
||||||
void addJunctionActor(const int3 & visitablePos)
|
void addJunctionActor(const int3 & visitablePos)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(syncLock);
|
||||||
|
|
||||||
auto internalCb = temporaryActorHeroes.front()->cb;
|
auto internalCb = temporaryActorHeroes.front()->cb;
|
||||||
auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(internalCb)).get();
|
auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(internalCb)).get();
|
||||||
|
|
||||||
@ -297,7 +305,7 @@ private:
|
|||||||
objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
|
objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
|
||||||
objectActor->initObj(rng);
|
objectActor->initObj(rng);
|
||||||
|
|
||||||
if(cb->getTile(visitablePos)->isWater())
|
if(ai->cb->getTile(visitablePos)->isWater())
|
||||||
{
|
{
|
||||||
objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
|
objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
|
||||||
}
|
}
|
||||||
@ -310,9 +318,8 @@ private:
|
|||||||
target->registerJunction(visitablePos);
|
target->registerJunction(visitablePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionCostInfo getConnectionsCost(const int3 & pos) const
|
ConnectionCostInfo getConnectionsCost(std::vector<AIPath> & paths) const
|
||||||
{
|
{
|
||||||
auto paths = ai->pathfinder->getPathInfo(pos);
|
|
||||||
std::map<int3, float> costs;
|
std::map<int3, float> costs;
|
||||||
|
|
||||||
for(auto & path : paths)
|
for(auto & path : paths)
|
||||||
@ -466,9 +473,14 @@ bool GraphNodeComparer::operator()(const GraphPathNodePointer & lhs, const Graph
|
|||||||
return pathNodes.at(lhs.coord)[lhs.nodeType].cost > pathNodes.at(rhs.coord)[rhs.nodeType].cost;
|
return pathNodes.at(lhs.coord)[lhs.nodeType].cost > pathNodes.at(rhs.coord)[rhs.nodeType].cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GraphPaths::GraphPaths()
|
||||||
|
: visualKey(""), graph(), pathNodes()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai)
|
void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai)
|
||||||
{
|
{
|
||||||
graph = *ai->baseGraph;
|
graph.copyFrom(*ai->baseGraph);
|
||||||
graph.connectHeroes(ai);
|
graph.connectHeroes(ai);
|
||||||
|
|
||||||
visualKey = std::to_string(ai->playerID) + ":" + targetHero->getNameTranslated();
|
visualKey = std::to_string(ai->playerID) + ":" + targetHero->getNameTranslated();
|
||||||
@ -675,4 +687,119 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphPaths::quickAddChainInfoWithBlocker(std::vector<AIPath> & paths, int3 tile, const CGHeroInstance * hero, const Nullkiller * ai) const
|
||||||
|
{
|
||||||
|
auto nodes = pathNodes.find(tile);
|
||||||
|
|
||||||
|
if(nodes == pathNodes.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(auto & targetNode : nodes->second)
|
||||||
|
{
|
||||||
|
if(!targetNode.reachable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::vector<GraphPathNodePointer> tilesToPass;
|
||||||
|
|
||||||
|
uint64_t danger = targetNode.danger;
|
||||||
|
float cost = targetNode.cost;
|
||||||
|
bool allowBattle = false;
|
||||||
|
|
||||||
|
auto current = GraphPathNodePointer(nodes->first, targetNode.nodeType);
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
auto currentTile = pathNodes.find(current.coord);
|
||||||
|
|
||||||
|
if(currentTile == pathNodes.end())
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto currentNode = currentTile->second[current.nodeType];
|
||||||
|
|
||||||
|
if(!currentNode.previous.valid())
|
||||||
|
break;
|
||||||
|
|
||||||
|
allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE;
|
||||||
|
vstd::amax(danger, currentNode.danger);
|
||||||
|
vstd::amax(cost, currentNode.cost);
|
||||||
|
|
||||||
|
tilesToPass.push_back(current);
|
||||||
|
|
||||||
|
if(currentNode.cost < 2.0f)
|
||||||
|
break;
|
||||||
|
|
||||||
|
current = currentNode.previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tilesToPass.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto entryPaths = ai->pathfinder->getPathInfo(tilesToPass.back().coord);
|
||||||
|
|
||||||
|
for(auto & entryPath : entryPaths)
|
||||||
|
{
|
||||||
|
if(entryPath.targetHero != hero)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto & path = paths.emplace_back();
|
||||||
|
|
||||||
|
path.targetHero = entryPath.targetHero;
|
||||||
|
path.heroArmy = entryPath.heroArmy;
|
||||||
|
path.exchangeCount = entryPath.exchangeCount;
|
||||||
|
path.armyLoss = entryPath.armyLoss + ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), danger);
|
||||||
|
path.targetObjectDanger = ai->pathfinder->getStorage()->evaluateDanger(tile, path.targetHero, !allowBattle);
|
||||||
|
path.targetObjectArmyLoss = ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
|
||||||
|
|
||||||
|
AIPathNodeInfo n;
|
||||||
|
|
||||||
|
n.targetHero = hero;
|
||||||
|
n.parentIndex = -1;
|
||||||
|
|
||||||
|
// final node
|
||||||
|
n.coord = tile;
|
||||||
|
n.cost = targetNode.cost;
|
||||||
|
n.danger = targetNode.danger;
|
||||||
|
n.parentIndex = path.nodes.size();
|
||||||
|
path.nodes.push_back(n);
|
||||||
|
|
||||||
|
for(auto entryNode = entryPath.nodes.rbegin(); entryNode != entryPath.nodes.rend(); entryNode++)
|
||||||
|
{
|
||||||
|
auto blocker = ai->objectClusterizer->getBlocker(*entryNode);
|
||||||
|
|
||||||
|
if(blocker)
|
||||||
|
{
|
||||||
|
// blocker node
|
||||||
|
path.nodes.push_back(*entryNode);
|
||||||
|
path.nodes.back().parentIndex = path.nodes.size() - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!path.nodes.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
for(auto graphTile = tilesToPass.rbegin(); graphTile != tilesToPass.rend(); graphTile++)
|
||||||
|
{
|
||||||
|
auto & node = getNode(*graphTile);
|
||||||
|
|
||||||
|
n.coord = graphTile->coord;
|
||||||
|
n.cost = node.cost;
|
||||||
|
n.turns = static_cast<ui8>(node.cost);
|
||||||
|
n.danger = node.danger;
|
||||||
|
n.specialAction = node.specialAction;
|
||||||
|
n.parentIndex = path.nodes.size();
|
||||||
|
|
||||||
|
auto blocker = ai->objectClusterizer->getBlocker(n);
|
||||||
|
|
||||||
|
if(!blocker)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// blocker node
|
||||||
|
path.nodes.push_back(n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,11 @@ public:
|
|||||||
void removeConnection(const int3 & from, const int3 & to);
|
void removeConnection(const int3 & from, const int3 & to);
|
||||||
void dumpToLog(std::string visualKey) const;
|
void dumpToLog(std::string visualKey) const;
|
||||||
|
|
||||||
|
void copyFrom(const ObjectGraph & other)
|
||||||
|
{
|
||||||
|
nodes = other.nodes;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
void iterateConnections(const int3 & pos, Func fn)
|
void iterateConnections(const int3 & pos, Func fn)
|
||||||
{
|
{
|
||||||
@ -167,8 +172,10 @@ class GraphPaths
|
|||||||
std::string visualKey;
|
std::string visualKey;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
GraphPaths();
|
||||||
void calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai);
|
void calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai);
|
||||||
void addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHeroInstance * hero, const Nullkiller * ai) const;
|
void addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHeroInstance * hero, const Nullkiller * ai) const;
|
||||||
|
void quickAddChainInfoWithBlocker(std::vector<AIPath> & paths, int3 tile, const CGHeroInstance * hero, const Nullkiller * ai) const;
|
||||||
void dumpToLog() const;
|
void dumpToLog() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
Reference in New Issue
Block a user