mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
NKAI: fix sonar and refactoring
This commit is contained in:
parent
6245adb9a4
commit
bec2c0cac2
@ -157,12 +157,13 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
||||
if(hitMap.shape()[0] != mapSize.x || hitMap.shape()[1] != mapSize.y || hitMap.shape()[2] != mapSize.z)
|
||||
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
||||
|
||||
std::map<const CGHeroInstance *, HeroRole> townHeroes;
|
||||
std::vector<std::unique_ptr<CGHeroInstance>> temporaryHeroes;
|
||||
std::map<const CGHeroInstance *, const CGTownInstance *> heroTownMap;
|
||||
std::map<const CGHeroInstance *, HeroRole> townHeroes;
|
||||
|
||||
auto addTownHero = [&](const CGTownInstance * town)
|
||||
{
|
||||
auto townHero = new CGHeroInstance(town->cb);
|
||||
auto townHero = temporaryHeroes.emplace_back(std::make_unique<CGHeroInstance>(town->cb)).get();
|
||||
CRandomGenerator rng;
|
||||
auto visitablePos = town->visitablePos();
|
||||
|
||||
@ -238,11 +239,6 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
||||
hitMap[pos.x][pos.y][pos.z].closestTown = enemyTown;
|
||||
}
|
||||
});
|
||||
|
||||
for(auto h : townHeroes)
|
||||
{
|
||||
delete h.first;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<HitMapInfo> & DangerHitMapAnalyzer::getTownThreats(const CGTownInstance * town) const
|
||||
|
@ -189,15 +189,7 @@ bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
|
||||
return true; //all of the following is met
|
||||
}
|
||||
|
||||
void ObjectClusterizer::clusterize()
|
||||
{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
nearObjects.reset();
|
||||
farObjects.reset();
|
||||
blockedObjects.clear();
|
||||
|
||||
Obj ignoreObjects[] = {
|
||||
Obj ObjectClusterizer::IgnoredObjectTypes[] = {
|
||||
Obj::BOAT,
|
||||
Obj::EYE_OF_MAGI,
|
||||
Obj::MONOLITH_ONE_WAY_ENTRANCE,
|
||||
@ -216,7 +208,15 @@ void ObjectClusterizer::clusterize()
|
||||
Obj::REDWOOD_OBSERVATORY,
|
||||
Obj::CARTOGRAPHER,
|
||||
Obj::PILLAR_OF_FIRE
|
||||
};
|
||||
};
|
||||
|
||||
void ObjectClusterizer::clusterize()
|
||||
{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
nearObjects.reset();
|
||||
farObjects.reset();
|
||||
blockedObjects.clear();
|
||||
|
||||
logAi->debug("Begin object clusterization");
|
||||
|
||||
@ -225,137 +225,18 @@ void ObjectClusterizer::clusterize()
|
||||
ai->memory->visitableObjs.end());
|
||||
|
||||
#if NKAI_TRACE_LEVEL == 0
|
||||
parallel_for(blocked_range<size_t>(0, objs.size()), [&](const blocked_range<size_t> & r)
|
||||
parallel_for(blocked_range<size_t>(0, objs.size()), [&](const blocked_range<size_t> & r) {
|
||||
#else
|
||||
blocked_range<size_t> r(0, objs.size());
|
||||
#endif
|
||||
{
|
||||
auto priorityEvaluator = ai->priorityEvaluators->acquire();
|
||||
|
||||
for(int i = r.begin(); i != r.end(); i++)
|
||||
{
|
||||
auto obj = objs[i];
|
||||
|
||||
if(!shouldVisitObject(obj))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Skip object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
|
||||
#endif
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(obj->visitablePos(), true);
|
||||
|
||||
if(paths.empty())
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("No paths found.");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
|
||||
{
|
||||
return p1.movementCost() < p2.movementCost();
|
||||
});
|
||||
|
||||
if(vstd::contains(ignoreObjects, obj->ID))
|
||||
{
|
||||
farObjects.addObject(obj, paths.front(), 0);
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Object ignored. Moved to far objects with path %s", paths.front().toString());
|
||||
#endif
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
std::set<const CGHeroInstance *> heroesProcessed;
|
||||
|
||||
for(auto & path : paths)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Checking path %s", path.toString());
|
||||
#endif
|
||||
|
||||
if(!shouldVisit(ai, path.targetHero, obj))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Hero %s does not need to visit %s", path.targetHero->getObjectName(), obj->getObjectName());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if(path.nodes.size() > 1)
|
||||
{
|
||||
auto blocker = getBlocker(path);
|
||||
|
||||
if(blocker)
|
||||
{
|
||||
if(vstd::contains(heroesProcessed, path.targetHero))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Hero %s is already processed.", path.targetHero->getObjectName());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
heroesProcessed.insert(path.targetHero);
|
||||
|
||||
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
|
||||
|
||||
if(priority < MIN_PRIORITY)
|
||||
continue;
|
||||
|
||||
ClusterMap::accessor cluster;
|
||||
blockedObjects.insert(
|
||||
cluster,
|
||||
ClusterMap::value_type(blocker, std::make_shared<ObjectCluster>(blocker)));
|
||||
|
||||
cluster->second->addObject(obj, path, priority);
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path added to cluster %s%s", blocker->getObjectName(), blocker->visitablePos().toString());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
heroesProcessed.insert(path.targetHero);
|
||||
|
||||
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
|
||||
|
||||
if(priority < MIN_PRIORITY)
|
||||
continue;
|
||||
|
||||
bool interestingObject = path.turn() <= 2 || priority > 0.5f;
|
||||
|
||||
if(interestingObject)
|
||||
{
|
||||
nearObjects.addObject(obj, path, priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
farObjects.addObject(obj, path, priority);
|
||||
}
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path %s added to %s objects. Turn: %d, priority: %f",
|
||||
path.toString(),
|
||||
interestingObject ? "near" : "far",
|
||||
path.turn(),
|
||||
priority);
|
||||
#endif
|
||||
}
|
||||
clusterizeObject(objs[i], priorityEvaluator.get());
|
||||
}
|
||||
#if NKAI_TRACE_LEVEL == 0
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
|
||||
logAi->trace("Near objects count: %i", nearObjects.objects.size());
|
||||
@ -376,4 +257,123 @@ void ObjectClusterizer::clusterize()
|
||||
logAi->trace("Clusterization complete in %ld", timeElapsed(start));
|
||||
}
|
||||
|
||||
void ObjectClusterizer::clusterizeObject(const CGObjectInstance * obj, PriorityEvaluator * priorityEvaluator)
|
||||
{
|
||||
if(!shouldVisitObject(obj))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Skip object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
|
||||
#endif
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(obj->visitablePos(), true);
|
||||
|
||||
if(paths.empty())
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("No paths found.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
|
||||
{
|
||||
return p1.movementCost() < p2.movementCost();
|
||||
});
|
||||
|
||||
if(vstd::contains(IgnoredObjectTypes, obj->ID))
|
||||
{
|
||||
farObjects.addObject(obj, paths.front(), 0);
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Object ignored. Moved to far objects with path %s", paths.front().toString());
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<const CGHeroInstance *> heroesProcessed;
|
||||
|
||||
for(auto & path : paths)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Checking path %s", path.toString());
|
||||
#endif
|
||||
|
||||
if(!shouldVisit(ai, path.targetHero, obj))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Hero %s does not need to visit %s", path.targetHero->getObjectName(), obj->getObjectName());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if(path.nodes.size() > 1)
|
||||
{
|
||||
auto blocker = getBlocker(path);
|
||||
|
||||
if(blocker)
|
||||
{
|
||||
if(vstd::contains(heroesProcessed, path.targetHero))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Hero %s is already processed.", path.targetHero->getObjectName());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
heroesProcessed.insert(path.targetHero);
|
||||
|
||||
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
|
||||
|
||||
if(priority < MIN_PRIORITY)
|
||||
continue;
|
||||
|
||||
ClusterMap::accessor cluster;
|
||||
blockedObjects.insert(
|
||||
cluster,
|
||||
ClusterMap::value_type(blocker, std::make_shared<ObjectCluster>(blocker)));
|
||||
|
||||
cluster->second->addObject(obj, path, priority);
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path added to cluster %s%s", blocker->getObjectName(), blocker->visitablePos().toString());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
heroesProcessed.insert(path.targetHero);
|
||||
|
||||
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
|
||||
|
||||
if(priority < MIN_PRIORITY)
|
||||
continue;
|
||||
|
||||
bool interestingObject = path.turn() <= 2 || priority > 0.5f;
|
||||
|
||||
if(interestingObject)
|
||||
{
|
||||
nearObjects.addObject(obj, path, priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
farObjects.addObject(obj, path, priority);
|
||||
}
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path %s added to %s objects. Turn: %d, priority: %f",
|
||||
path.toString(),
|
||||
interestingObject ? "near" : "far",
|
||||
path.turn(),
|
||||
priority);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,9 +49,13 @@ public:
|
||||
|
||||
using ClusterMap = tbb::concurrent_hash_map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>>;
|
||||
|
||||
class PriorityEvaluator;
|
||||
|
||||
class ObjectClusterizer
|
||||
{
|
||||
private:
|
||||
static Obj IgnoredObjectTypes[];
|
||||
|
||||
ObjectCluster nearObjects;
|
||||
ObjectCluster farObjects;
|
||||
ClusterMap blockedObjects;
|
||||
@ -68,6 +72,7 @@ public:
|
||||
|
||||
private:
|
||||
bool shouldVisitObject(const CGObjectInstance * obj) const;
|
||||
void clusterizeObject(const CGObjectInstance * obj, PriorityEvaluator * priorityEvaluator);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -11,8 +11,8 @@
|
||||
#pragma once
|
||||
|
||||
#define NKAI_PATHFINDER_TRACE_LEVEL 0
|
||||
#define NKAI_GRAPH_TRACE_LEVEL 0
|
||||
#define NKAI_TRACE_LEVEL 1
|
||||
constexpr int NKAI_GRAPH_TRACE_LEVEL = 0;
|
||||
#define NKAI_TRACE_LEVEL 0
|
||||
|
||||
#include "../../../lib/pathfinder/CGPathNode.h"
|
||||
#include "../../../lib/pathfinder/INodeStorage.h"
|
||||
|
@ -58,7 +58,7 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile, bool includeGra
|
||||
return info;
|
||||
}
|
||||
|
||||
void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, PathfinderSettings pathfinderSettings)
|
||||
void AIPathfinder::updatePaths(const std::map<const CGHeroInstance *, HeroRole> & heroes, PathfinderSettings pathfinderSettings)
|
||||
{
|
||||
if(!storage)
|
||||
{
|
||||
@ -125,7 +125,7 @@ void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes
|
||||
logAi->trace("Recalculated paths in %ld", timeElapsed(start));
|
||||
}
|
||||
|
||||
void AIPathfinder::updateGraphs(std::map<const CGHeroInstance *, HeroRole> heroes)
|
||||
void AIPathfinder::updateGraphs(const std::map<const CGHeroInstance *, HeroRole> & heroes)
|
||||
{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
std::vector<const CGHeroInstance *> heroesVector;
|
||||
@ -134,22 +134,23 @@ void AIPathfinder::updateGraphs(std::map<const CGHeroInstance *, HeroRole> heroe
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
heroGraphs.emplace(hero.first->id, GraphPaths());
|
||||
heroesVector.push_back(hero.first);
|
||||
if(heroGraphs.try_emplace(hero.first->id, GraphPaths()).second)
|
||||
heroesVector.push_back(hero.first);
|
||||
}
|
||||
|
||||
parallel_for(blocked_range<size_t>(0, heroesVector.size()), [&](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++)
|
||||
heroGraphs.at(heroesVector[i]->id).calculatePaths(heroesVector[i], ai);
|
||||
});
|
||||
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 1
|
||||
for(auto hero : heroes)
|
||||
if(NKAI_GRAPH_TRACE_LEVEL >= 1)
|
||||
{
|
||||
heroGraphs[hero.first->id].dumpToLog();
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
heroGraphs[hero.first->id].dumpToLog();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
logAi->trace("Graph paths updated in %lld", timeElapsed(start));
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ public:
|
||||
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
|
||||
std::vector<AIPath> getPathInfo(const int3 & tile, bool includeGraph = false) const;
|
||||
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
|
||||
void updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, PathfinderSettings pathfinderSettings);
|
||||
void updateGraphs(std::map<const CGHeroInstance *, HeroRole> heroes);
|
||||
void updatePaths(const std::map<const CGHeroInstance *, HeroRole> & heroes, PathfinderSettings pathfinderSettings);
|
||||
void updateGraphs(const std::map<const CGHeroInstance *, HeroRole> & heroes);
|
||||
void init();
|
||||
|
||||
std::shared_ptr<AINodeStorage>getStorage()
|
||||
|
@ -19,17 +19,100 @@
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
void ObjectGraph::updateGraph(const Nullkiller * ai)
|
||||
class ObjectGraphCalculator
|
||||
{
|
||||
auto cb = ai->cb;
|
||||
private:
|
||||
ObjectGraph * target;
|
||||
const Nullkiller * ai;
|
||||
|
||||
std::map<const CGHeroInstance *, HeroRole> actors;
|
||||
std::map<const CGHeroInstance *, const CGObjectInstance *> actorObjectMap;
|
||||
std::vector<CGBoat *> boats;
|
||||
|
||||
auto addObjectActor = [&](const CGObjectInstance * obj)
|
||||
std::vector<std::unique_ptr<CGBoat>> temporaryBoats;
|
||||
std::vector<std::unique_ptr<CGHeroInstance>> temporaryActorHeroes;
|
||||
|
||||
public:
|
||||
ObjectGraphCalculator(ObjectGraph * target, const Nullkiller * ai)
|
||||
:ai(ai), target(target)
|
||||
{
|
||||
auto objectActor = new CGHeroInstance(obj->cb);
|
||||
for(auto obj : ai->memory->visitableObjs)
|
||||
{
|
||||
if(obj && obj->isVisitable() && obj->ID != Obj::HERO)
|
||||
{
|
||||
addObjectActor(obj);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto town : ai->cb->getTownsInfo())
|
||||
{
|
||||
addObjectActor(town);
|
||||
}
|
||||
|
||||
PathfinderSettings ps;
|
||||
|
||||
ps.mainTurnDistanceLimit = 5;
|
||||
ps.scoutTurnDistanceLimit = 1;
|
||||
ps.allowBypassObjects = false;
|
||||
|
||||
ai->pathfinder->updatePaths(actors, ps);
|
||||
}
|
||||
|
||||
void calculateConnections(const int3 & pos)
|
||||
{
|
||||
auto guarded = ai->cb->getGuardingCreaturePosition(pos).valid();
|
||||
|
||||
if(guarded)
|
||||
return;
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(pos);
|
||||
|
||||
for(AIPath & path1 : paths)
|
||||
{
|
||||
for(AIPath & path2 : paths)
|
||||
{
|
||||
if(path1.targetHero == path2.targetHero)
|
||||
continue;
|
||||
|
||||
auto obj1 = actorObjectMap[path1.targetHero];
|
||||
auto obj2 = actorObjectMap[path2.targetHero];
|
||||
|
||||
auto tile1 = cb->getTile(obj1->visitablePos());
|
||||
auto tile2 = cb->getTile(obj2->visitablePos());
|
||||
|
||||
if(tile2->isWater() && !tile1->isWater())
|
||||
{
|
||||
auto linkTile = cb->getTile(pos);
|
||||
|
||||
if(!linkTile->isWater() || obj1->ID != Obj::BOAT || obj1->ID != Obj::SHIPYARD)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto danger = ai->pathfinder->getStorage()->evaluateDanger(obj2->visitablePos(), path1.targetHero, true);
|
||||
|
||||
auto updated = target->tryAddConnection(
|
||||
obj1->visitablePos(),
|
||||
obj2->visitablePos(),
|
||||
path1.movementCost() + path2.movementCost(),
|
||||
danger);
|
||||
|
||||
if(NKAI_GRAPH_TRACE_LEVEL >= 2 && updated)
|
||||
{
|
||||
logAi->trace(
|
||||
"Connected %s[%s] -> %s[%s] through [%s], cost %2f",
|
||||
obj1->getObjectName(), obj1->visitablePos().toString(),
|
||||
obj2->getObjectName(), obj2->visitablePos().toString(),
|
||||
pos.toString(),
|
||||
path1.movementCost() + path2.movementCost());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void addObjectActor(const CGObjectInstance * obj)
|
||||
{
|
||||
auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(obj->cb)).get();
|
||||
|
||||
CRandomGenerator rng;
|
||||
auto visitablePos = obj->visitablePos();
|
||||
|
||||
@ -40,105 +123,42 @@ void ObjectGraph::updateGraph(const Nullkiller * ai)
|
||||
|
||||
if(cb->getTile(visitablePos)->isWater())
|
||||
{
|
||||
boats.push_back(new CGBoat(objectActor->cb));
|
||||
objectActor->boat = boats.back();
|
||||
objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
|
||||
}
|
||||
|
||||
actorObjectMap[objectActor] = obj;
|
||||
actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::SHIPYARD ? HeroRole::MAIN : HeroRole::SCOUT;
|
||||
addObject(obj);
|
||||
actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::SHIPYARD ? HeroRole::MAIN : HeroRole::SCOUT;
|
||||
|
||||
target->addObject(obj);
|
||||
};
|
||||
};
|
||||
|
||||
for(auto obj : ai->memory->visitableObjs)
|
||||
{
|
||||
if(obj && obj->isVisitable() && obj->ID != Obj::HERO)
|
||||
{
|
||||
addObjectActor(obj);
|
||||
}
|
||||
}
|
||||
bool ObjectGraph::tryAddConnection(
|
||||
const int3 & from,
|
||||
const int3 & to,
|
||||
float cost,
|
||||
uint64_t danger)
|
||||
{
|
||||
return nodes[from].connections[to].update(cost, danger);
|
||||
}
|
||||
|
||||
for(auto town : cb->getTownsInfo())
|
||||
{
|
||||
addObjectActor(town);
|
||||
}
|
||||
void ObjectGraph::updateGraph(const Nullkiller * ai)
|
||||
{
|
||||
auto cb = ai->cb;
|
||||
|
||||
PathfinderSettings ps;
|
||||
ObjectGraphCalculator calculator(this, ai);
|
||||
|
||||
ps.mainTurnDistanceLimit = 5;
|
||||
ps.scoutTurnDistanceLimit = 1;
|
||||
ps.allowBypassObjects = false;
|
||||
|
||||
ai->pathfinder->updatePaths(actors, ps);
|
||||
|
||||
foreach_tile_pos(cb.get(), [&](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
||||
foreach_tile_pos(cb.get(), [this, &calculator](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
||||
{
|
||||
if(nodes.find(pos) != nodes.end())
|
||||
return;
|
||||
|
||||
auto guarded = ai->cb->getGuardingCreaturePosition(pos).valid();
|
||||
|
||||
if(guarded)
|
||||
return;
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(pos);
|
||||
|
||||
for(AIPath & path1 : paths)
|
||||
{
|
||||
for(AIPath & path2 : paths)
|
||||
{
|
||||
if(path1.targetHero == path2.targetHero)
|
||||
continue;
|
||||
|
||||
auto obj1 = actorObjectMap[path1.targetHero];
|
||||
auto obj2 = actorObjectMap[path2.targetHero];
|
||||
|
||||
auto tile1 = cb->getTile(obj1->visitablePos());
|
||||
auto tile2 = cb->getTile(obj2->visitablePos());
|
||||
|
||||
if(tile2->isWater() && !tile1->isWater())
|
||||
{
|
||||
auto linkTile = cb->getTile(pos);
|
||||
|
||||
if(!linkTile->isWater() || obj1->ID != Obj::BOAT || obj1->ID != Obj::SHIPYARD)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto danger = ai->pathfinder->getStorage()->evaluateDanger(obj2->visitablePos(), path1.targetHero, true);
|
||||
|
||||
auto updated = nodes[obj1->visitablePos()].connections[obj2->visitablePos()].update(
|
||||
path1.movementCost() + path2.movementCost(),
|
||||
danger);
|
||||
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 2
|
||||
if(updated)
|
||||
{
|
||||
logAi->trace(
|
||||
"Connected %s[%s] -> %s[%s] through [%s], cost %2f",
|
||||
obj1->getObjectName(), obj1->visitablePos().toString(),
|
||||
obj2->getObjectName(), obj2->visitablePos().toString(),
|
||||
pos.toString(),
|
||||
path1.movementCost() + path2.movementCost());
|
||||
}
|
||||
#else
|
||||
(void)updated;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
calculator.calculateConnections(pos);
|
||||
});
|
||||
|
||||
for(auto h : actorObjectMap)
|
||||
{
|
||||
delete h.first;
|
||||
}
|
||||
if(NKAI_GRAPH_TRACE_LEVEL >= 1)
|
||||
dumpToLog("graph");
|
||||
|
||||
for(auto boat : boats)
|
||||
{
|
||||
delete boat;
|
||||
}
|
||||
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 1
|
||||
dumpToLog("graph");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectGraph::addObject(const CGObjectInstance * obj)
|
||||
@ -199,14 +219,15 @@ void ObjectGraph::dumpToLog(std::string visualKey) const
|
||||
{
|
||||
for(auto & node : tile.second.connections)
|
||||
{
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
"%s -> %s: %f !%d",
|
||||
node.first.toString(),
|
||||
tile.first.toString(),
|
||||
node.second.cost,
|
||||
node.second.danger);
|
||||
#endif
|
||||
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
|
||||
{
|
||||
logAi->trace(
|
||||
"%s -> %s: %f !%d",
|
||||
node.first.toString(),
|
||||
tile.first.toString(),
|
||||
node.second.cost,
|
||||
node.second.danger);
|
||||
}
|
||||
|
||||
logBuilder.addLine(tile.first, node.first);
|
||||
}
|
||||
@ -278,14 +299,15 @@ void GraphPaths::dumpToLog() const
|
||||
if(!node.previous.valid())
|
||||
continue;
|
||||
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
"%s -> %s: %f !%d",
|
||||
node.previous.coord.toString(),
|
||||
tile.first.toString(),
|
||||
node.cost,
|
||||
node.danger);
|
||||
#endif
|
||||
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
|
||||
{
|
||||
logAi->trace(
|
||||
"%s -> %s: %f !%d",
|
||||
node.previous.coord.toString(),
|
||||
tile.first.toString(),
|
||||
node.cost,
|
||||
node.danger);
|
||||
}
|
||||
|
||||
logBuilder.addLine(node.previous.coord, tile.first);
|
||||
}
|
||||
@ -343,7 +365,7 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
||||
|
||||
auto currentNode = currentTile->second[current.nodeType];
|
||||
|
||||
if(currentNode.cost == 0)
|
||||
if(!currentNode.previous.valid())
|
||||
break;
|
||||
|
||||
allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE;
|
||||
@ -352,7 +374,7 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
||||
|
||||
tilesToPass.push_back(current.coord);
|
||||
|
||||
if(currentNode.cost < 2)
|
||||
if(currentNode.cost < 2.0f)
|
||||
break;
|
||||
|
||||
current = currentNode.previous;
|
||||
@ -377,6 +399,7 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
||||
n.turns = static_cast<ui8>(cost) + 1; // just in case lets select worst scenario
|
||||
n.danger = danger;
|
||||
n.targetHero = hero;
|
||||
n.parentIndex = 0;
|
||||
|
||||
for(auto & node : path.nodes)
|
||||
{
|
||||
|
@ -76,6 +76,8 @@ public:
|
||||
{
|
||||
return nodes.at(tile);
|
||||
}
|
||||
|
||||
bool tryAddConnection(const int3 & from, const int3 & to, float cost, uint64_t danger);
|
||||
};
|
||||
|
||||
struct GraphPathNode;
|
||||
|
@ -45,7 +45,7 @@ namespace AIPathfinding
|
||||
|
||||
if(!allowBypassObjects)
|
||||
{
|
||||
if (source.node->getCost() == 0)
|
||||
if (source.node->getCost() < 0.0001f)
|
||||
return;
|
||||
|
||||
// when actor represents moster graph node, we need to let him escape monster
|
||||
|
Loading…
x
Reference in New Issue
Block a user