1
0
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:
Andrii Danylchenko 2024-03-09 16:20:00 +02:00
parent 6245adb9a4
commit bec2c0cac2
9 changed files with 286 additions and 259 deletions

View File

@ -157,12 +157,13 @@ void DangerHitMapAnalyzer::calculateTileOwners()
if(hitMap.shape()[0] != mapSize.x || hitMap.shape()[1] != mapSize.y || hitMap.shape()[2] != mapSize.z) 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]); 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 *, const CGTownInstance *> heroTownMap;
std::map<const CGHeroInstance *, HeroRole> townHeroes;
auto addTownHero = [&](const CGTownInstance * town) 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; CRandomGenerator rng;
auto visitablePos = town->visitablePos(); auto visitablePos = town->visitablePos();
@ -238,11 +239,6 @@ void DangerHitMapAnalyzer::calculateTileOwners()
hitMap[pos.x][pos.y][pos.z].closestTown = enemyTown; 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 const std::vector<HitMapInfo> & DangerHitMapAnalyzer::getTownThreats(const CGTownInstance * town) const

View File

@ -189,15 +189,7 @@ bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
return true; //all of the following is met return true; //all of the following is met
} }
void ObjectClusterizer::clusterize() Obj ObjectClusterizer::IgnoredObjectTypes[] = {
{
auto start = std::chrono::high_resolution_clock::now();
nearObjects.reset();
farObjects.reset();
blockedObjects.clear();
Obj ignoreObjects[] = {
Obj::BOAT, Obj::BOAT,
Obj::EYE_OF_MAGI, Obj::EYE_OF_MAGI,
Obj::MONOLITH_ONE_WAY_ENTRANCE, Obj::MONOLITH_ONE_WAY_ENTRANCE,
@ -216,7 +208,15 @@ void ObjectClusterizer::clusterize()
Obj::REDWOOD_OBSERVATORY, Obj::REDWOOD_OBSERVATORY,
Obj::CARTOGRAPHER, Obj::CARTOGRAPHER,
Obj::PILLAR_OF_FIRE 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"); logAi->debug("Begin object clusterization");
@ -225,137 +225,18 @@ void ObjectClusterizer::clusterize()
ai->memory->visitableObjs.end()); ai->memory->visitableObjs.end());
#if NKAI_TRACE_LEVEL == 0 #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 #else
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();
for(int i = r.begin(); i != r.end(); i++) for(int i = r.begin(); i != r.end(); i++)
{ {
auto obj = objs[i]; clusterizeObject(objs[i], priorityEvaluator.get());
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
}
} }
#if NKAI_TRACE_LEVEL == 0 #if NKAI_TRACE_LEVEL == 0
}); });
#else
}
#endif #endif
logAi->trace("Near objects count: %i", nearObjects.objects.size()); logAi->trace("Near objects count: %i", nearObjects.objects.size());
@ -376,4 +257,123 @@ 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)
{
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
}
}
} }

View File

@ -49,9 +49,13 @@ 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:
static Obj IgnoredObjectTypes[];
ObjectCluster nearObjects; ObjectCluster nearObjects;
ObjectCluster farObjects; ObjectCluster farObjects;
ClusterMap blockedObjects; ClusterMap blockedObjects;
@ -68,6 +72,7 @@ public:
private: private:
bool shouldVisitObject(const CGObjectInstance * obj) const; bool shouldVisitObject(const CGObjectInstance * obj) const;
void clusterizeObject(const CGObjectInstance * obj, PriorityEvaluator * priorityEvaluator);
}; };
} }

View File

@ -11,8 +11,8 @@
#pragma once #pragma once
#define NKAI_PATHFINDER_TRACE_LEVEL 0 #define NKAI_PATHFINDER_TRACE_LEVEL 0
#define NKAI_GRAPH_TRACE_LEVEL 0 constexpr int NKAI_GRAPH_TRACE_LEVEL = 0;
#define NKAI_TRACE_LEVEL 1 #define NKAI_TRACE_LEVEL 0
#include "../../../lib/pathfinder/CGPathNode.h" #include "../../../lib/pathfinder/CGPathNode.h"
#include "../../../lib/pathfinder/INodeStorage.h" #include "../../../lib/pathfinder/INodeStorage.h"

View File

@ -58,7 +58,7 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile, bool includeGra
return info; 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) if(!storage)
{ {
@ -125,7 +125,7 @@ void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes
logAi->trace("Recalculated paths in %ld", timeElapsed(start)); 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(); auto start = std::chrono::high_resolution_clock::now();
std::vector<const CGHeroInstance *> heroesVector; std::vector<const CGHeroInstance *> heroesVector;
@ -134,22 +134,23 @@ void AIPathfinder::updateGraphs(std::map<const CGHeroInstance *, HeroRole> heroe
for(auto hero : heroes) for(auto hero : heroes)
{ {
heroGraphs.emplace(hero.first->id, GraphPaths()); if(heroGraphs.try_emplace(hero.first->id, GraphPaths()).second)
heroesVector.push_back(hero.first); 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++) 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)
{ {
heroGraphs[hero.first->id].dumpToLog(); for(auto hero : heroes)
{
heroGraphs[hero.first->id].dumpToLog();
}
} }
#endif
logAi->trace("Graph paths updated in %lld", timeElapsed(start)); logAi->trace("Graph paths updated in %lld", timeElapsed(start));
} }

View File

@ -46,8 +46,8 @@ public:
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai); AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
std::vector<AIPath> getPathInfo(const int3 & tile, bool includeGraph = false) const; std::vector<AIPath> getPathInfo(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(std::map<const CGHeroInstance *, HeroRole> heroes, PathfinderSettings pathfinderSettings); void updatePaths(const std::map<const CGHeroInstance *, HeroRole> & heroes, PathfinderSettings pathfinderSettings);
void updateGraphs(std::map<const CGHeroInstance *, HeroRole> heroes); void updateGraphs(const std::map<const CGHeroInstance *, HeroRole> & heroes);
void init(); void init();
std::shared_ptr<AINodeStorage>getStorage() std::shared_ptr<AINodeStorage>getStorage()

View File

@ -19,17 +19,100 @@
namespace NKAI 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 *, HeroRole> actors;
std::map<const CGHeroInstance *, const CGObjectInstance *> actorObjectMap; 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; CRandomGenerator rng;
auto visitablePos = obj->visitablePos(); auto visitablePos = obj->visitablePos();
@ -40,105 +123,42 @@ void ObjectGraph::updateGraph(const Nullkiller * ai)
if(cb->getTile(visitablePos)->isWater()) if(cb->getTile(visitablePos)->isWater())
{ {
boats.push_back(new CGBoat(objectActor->cb)); objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
objectActor->boat = boats.back();
} }
actorObjectMap[objectActor] = obj; actorObjectMap[objectActor] = obj;
actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::SHIPYARD ? HeroRole::MAIN : HeroRole::SCOUT; actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::SHIPYARD ? HeroRole::MAIN : HeroRole::SCOUT;
addObject(obj);
target->addObject(obj);
}; };
};
for(auto obj : ai->memory->visitableObjs) bool ObjectGraph::tryAddConnection(
{ const int3 & from,
if(obj && obj->isVisitable() && obj->ID != Obj::HERO) const int3 & to,
{ float cost,
addObjectActor(obj); uint64_t danger)
} {
} return nodes[from].connections[to].update(cost, danger);
}
for(auto town : cb->getTownsInfo()) void ObjectGraph::updateGraph(const Nullkiller * ai)
{ {
addObjectActor(town); 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(), [this, &calculator](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
foreach_tile_pos(cb.get(), [&](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
{ {
if(nodes.find(pos) != nodes.end()) if(nodes.find(pos) != nodes.end())
return; return;
auto guarded = ai->cb->getGuardingCreaturePosition(pos).valid(); calculator.calculateConnections(pos);
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
}
}
}); });
for(auto h : actorObjectMap) if(NKAI_GRAPH_TRACE_LEVEL >= 1)
{ dumpToLog("graph");
delete h.first;
}
for(auto boat : boats)
{
delete boat;
}
#if NKAI_GRAPH_TRACE_LEVEL >= 1
dumpToLog("graph");
#endif
} }
void ObjectGraph::addObject(const CGObjectInstance * obj) void ObjectGraph::addObject(const CGObjectInstance * obj)
@ -199,14 +219,15 @@ void ObjectGraph::dumpToLog(std::string visualKey) const
{ {
for(auto & node : tile.second.connections) for(auto & node : tile.second.connections)
{ {
#if NKAI_GRAPH_TRACE_LEVEL >= 2 if(NKAI_GRAPH_TRACE_LEVEL >= 2)
logAi->trace( {
"%s -> %s: %f !%d", logAi->trace(
node.first.toString(), "%s -> %s: %f !%d",
tile.first.toString(), node.first.toString(),
node.second.cost, tile.first.toString(),
node.second.danger); node.second.cost,
#endif node.second.danger);
}
logBuilder.addLine(tile.first, node.first); logBuilder.addLine(tile.first, node.first);
} }
@ -278,14 +299,15 @@ void GraphPaths::dumpToLog() const
if(!node.previous.valid()) if(!node.previous.valid())
continue; continue;
#if NKAI_GRAPH_TRACE_LEVEL >= 2 if(NKAI_GRAPH_TRACE_LEVEL >= 2)
logAi->trace( {
"%s -> %s: %f !%d", logAi->trace(
node.previous.coord.toString(), "%s -> %s: %f !%d",
tile.first.toString(), node.previous.coord.toString(),
node.cost, tile.first.toString(),
node.danger); node.cost,
#endif node.danger);
}
logBuilder.addLine(node.previous.coord, tile.first); 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]; auto currentNode = currentTile->second[current.nodeType];
if(currentNode.cost == 0) if(!currentNode.previous.valid())
break; break;
allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE; 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); tilesToPass.push_back(current.coord);
if(currentNode.cost < 2) if(currentNode.cost < 2.0f)
break; break;
current = currentNode.previous; 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.turns = static_cast<ui8>(cost) + 1; // just in case lets select worst scenario
n.danger = danger; n.danger = danger;
n.targetHero = hero; n.targetHero = hero;
n.parentIndex = 0;
for(auto & node : path.nodes) for(auto & node : path.nodes)
{ {

View File

@ -76,6 +76,8 @@ public:
{ {
return nodes.at(tile); return nodes.at(tile);
} }
bool tryAddConnection(const int3 & from, const int3 & to, float cost, uint64_t danger);
}; };
struct GraphPathNode; struct GraphPathNode;

View File

@ -45,7 +45,7 @@ namespace AIPathfinding
if(!allowBypassObjects) if(!allowBypassObjects)
{ {
if (source.node->getCost() == 0) if (source.node->getCost() < 0.0001f)
return; return;
// when actor represents moster graph node, we need to let him escape monster // when actor represents moster graph node, we need to let him escape monster