1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

AI pathfinding shared storage

This commit is contained in:
Andrii Danylchenko 2021-05-15 19:22:49 +03:00 committed by Andrii Danylchenko
parent be4f803d4a
commit 0bff5f9eb6
14 changed files with 262 additions and 191 deletions

View File

@ -26,14 +26,14 @@ AINodeStorage::AINodeStorage(const int3 & Sizes)
AINodeStorage::~AINodeStorage() = default;
void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero)
void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
{
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
int3 pos;
const PlayerColor player = ai->playerID;
const int3 sizes = gs->getMapSize();
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap;
const PlayerColor player = hero->tempOwner;
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(player)->fogOfWarMap;
//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
const bool useFlying = options.useFlying;
@ -70,6 +70,11 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
}
}
void AINodeStorage::reset()
{
actors.clear();
}
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
{
return static_cast<const AIPathNode *>(node);
@ -82,25 +87,20 @@ void AINodeStorage::updateAINode(CGPathNode * node, std::function<void(AIPathNod
updater(aiNode);
}
bool AINodeStorage::isBattleNode(const CGPathNode * node) const
{
return (getAINode(node)->chainMask & BATTLE_CHAIN) > 0;
}
boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber)
boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, const ChainActor * actor)
{
auto chains = nodes[pos.x][pos.y][pos.z][layer];
for(AIPathNode & node : chains)
{
if(node.chainMask == chainNumber)
if(node.actor == actor)
{
return &node;
}
if(node.chainMask == 0)
if(!node.actor)
{
node.chainMask = chainNumber;
node.actor = actor;
return &node;
}
@ -109,19 +109,33 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, c
return boost::none;
}
CGPathNode * AINodeStorage::getInitialNode()
std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
{
auto hpos = hero->getPosition(false);
auto initialNode =
getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN)
.get();
std::vector<CGPathNode *> initialNodes;
initialNode->turns = 0;
initialNode->moveRemains = hero->movement;
initialNode->danger = 0;
initialNode->cost = 0.0;
for(auto actorPtr : actors)
{
const ChainActor * actor = actorPtr.get();
AIPathNode * initialNode =
getOrCreateNode(actor->initialPosition, actor->layer, actor)
.get();
return initialNode;
initialNode->turns = actor->initialTurn;
initialNode->moveRemains = actor->initialMovement;
initialNode->danger = 0;
initialNode->cost = 0.0;
if(actor->isMovable)
{
initialNodes.push_back(initialNode);
}
else
{
initialNode->locked = true;
}
}
return initialNodes;
}
void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility)
@ -130,7 +144,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat
{
AIPathNode & heroNode = nodes[coord.x][coord.y][coord.z][layer][i];
heroNode.chainMask = 0;
heroNode.actor = nullptr;
heroNode.danger = 0;
heroNode.manaCost = 0;
heroNode.specialAction.reset();
@ -152,9 +166,9 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
dstNode->theNodeBefore = srcNode->theNodeBefore;
dstNode->manaCost = srcNode->manaCost;
if(dstNode->specialAction)
if(dstNode->specialAction && dstNode->actor)
{
dstNode->specialAction->applyOnDestination(getHero(), destination, source, dstNode, srcNode);
dstNode->specialAction->applyOnDestination(dstNode->actor->hero, destination, source, dstNode, srcNode);
}
});
}
@ -173,7 +187,7 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
{
for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1))
{
auto nextNode = getOrCreateNode(neighbour, i, srcNode->chainMask);
auto nextNode = getOrCreateNode(neighbour, i, srcNode->actor);
if(!nextNode || nextNode.get()->accessible == CGPathNode::NOT_SET)
continue;
@ -185,11 +199,37 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
return neighbours;
}
void AINodeStorage::setHero(HeroPtr heroPtr, const VCAI * _ai)
const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
{
auto aiNode = getAINode(node);
return aiNode->actor->hero;
}
const std::set<const CGHeroInstance *> AINodeStorage::getAllHeroes() const
{
std::set<const CGHeroInstance *> heroes;
for(auto actor : actors)
{
if(actor->hero)
heroes.insert(hero);
}
return heroes;
}
void AINodeStorage::setHeroes(std::vector<HeroPtr> heroes, const VCAI * _ai)
{
hero = heroPtr.get();
cb = _ai->myCb.get();
ai = _ai;
for(auto & hero : heroes)
{
uint64_t mask = 1 << actors.size();
actors.push_back(std::make_shared<ChainActor>(hero.get(), mask));
}
}
std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
@ -206,7 +246,7 @@ std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
for(auto & neighbour : accessibleExits)
{
auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask);
auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->actor);
if(!node)
continue;
@ -269,7 +309,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
if(targetTown->visitingHero)
continue;
auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN);
auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->actor->castActor);
if(nodeOptional)
{
@ -297,13 +337,14 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
for(const AIPathNode & node : chains)
{
auto sameNode = node.chainMask == destinationNode->chainMask;
auto sameNode = node.actor == destinationNode->actor;
if(sameNode || node.action == CGPathNode::ENodeAction::UNKNOWN)
{
continue;
}
if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0)
if(node.danger <= destinationNode->danger && destinationNode->actor == node.actor->battleActor)
{
if(node.cost < destinationNode->cost)
{
@ -323,11 +364,20 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
return false;
}
bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const
bool AINodeStorage::isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const
{
const AIPathNode & node = nodes[pos.x][pos.y][pos.z][layer][0];
auto chains = nodes[pos.x][pos.y][pos.z][layer];
return node.action != CGPathNode::ENodeAction::UNKNOWN;
for(const AIPathNode & node : chains)
{
if(node.action != CGPathNode::ENodeAction::UNKNOWN
&& node.actor && node.actor->hero == hero.h)
{
return true;
}
}
return false;
}
std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
@ -338,7 +388,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
for(const AIPathNode & node : chains)
{
if(node.action == CGPathNode::ENodeAction::UNKNOWN)
if(node.action == CGPathNode::ENodeAction::UNKNOWN || !node.actor)
{
continue;
}
@ -346,6 +396,8 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
AIPath path;
const AIPathNode * current = &node;
path.targetHero = node.actor->hero;
while(current != nullptr && current->coord != initialPos)
{
AIPathNodeInfo pathNode;
@ -411,3 +463,49 @@ uint64_t AIPath::getTotalDanger(HeroPtr hero) const
return danger;
}
ChainActor::ChainActor(const CGHeroInstance * hero, int chainMask)
:hero(hero), isMovable(true), chainMask(chainMask)
{
initialPosition = hero->visitablePos();
layer = hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND;
initialMovement = hero->movement;
initialTurn = 0;
setupSpecialActors();
}
void ChainActor::copyFrom(ChainActor * base)
{
hero = base->hero;
layer = base->layer;
initialMovement = base->initialMovement;
initialTurn = base->initialTurn;
}
void ChainActor::setupSpecialActors()
{
auto allActors = std::vector<ChainActor *>{ this };
specialActors.resize(7);
for(int i = 1; i < 8; i++)
{
ChainActor & specialActor = specialActors[i - 1];
specialActor.copyFrom(this);
specialActor.allowBattle = (i & 1) > 0;
specialActor.allowSpellCast = (i & 2) > 0;
specialActor.allowUseResources = (i & 4) > 0;
allActors.push_back(&specialActor);
}
for(int i = 0; i <= 8; i++)
{
ChainActor * actor = allActors[i];
actor->battleActor = allActors[i | 1];
actor->castActor = allActors[i | 2];
actor->resourceActor = allActors[i | 4];
}
}

View File

@ -17,14 +17,43 @@
#include "../Goals/AbstractGoal.h"
#include "Actions/ISpecialAction.h"
struct AIPathNode;
class ChainActor
{
private:
std::vector<ChainActor> specialActors;
void copyFrom(ChainActor * base);
void setupSpecialActors();
public:
// chain flags, can be combined meaning hero exchange and so on
uint64_t chainMask;
bool isMovable;
bool allowUseResources;
bool allowBattle;
bool allowSpellCast;
const CGHeroInstance * hero;
const ChainActor * battleActor;
const ChainActor * castActor;
const ChainActor * resourceActor;
int3 initialPosition;
EPathfindingLayer layer;
uint32_t initialMovement;
uint32_t initialTurn;
ChainActor()
{
}
ChainActor(const CGHeroInstance * hero, int chainMask);
};
struct AIPathNode : public CGPathNode
{
uint32_t chainMask;
uint64_t danger;
uint32_t manaCost;
std::shared_ptr<const ISpecialAction> specialAction;
const ChainActor * actor;
};
struct AIPathNodeInfo
@ -40,6 +69,7 @@ struct AIPath
std::vector<AIPathNodeInfo> nodes;
std::shared_ptr<const ISpecialAction> specialAction;
uint64_t targetObjectDanger;
const CGHeroInstance * targetHero;
AIPath();
@ -65,26 +95,21 @@ private:
const VCAI * ai;
const CGHeroInstance * hero;
std::unique_ptr<FuzzyHelper> dangerEvaluator;
std::vector<std::shared_ptr<ChainActor>> actors;
STRONG_INLINE
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
public:
/// more than 1 chain layer allows us to have more than 1 path to each tile so we can chose more optimal one.
static const int NUM_CHAINS = 3;
// chain flags, can be combined
static const int NORMAL_CHAIN = 1;
static const int BATTLE_CHAIN = 2;
static const int CAST_CHAIN = 4;
static const int RESOURCE_CHAIN = 8;
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
static const int NUM_CHAINS = 3 * GameConstants::MAX_HEROES_PER_PLAYER;
AINodeStorage(const int3 & sizes);
~AINodeStorage();
void initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero) override;
void initialize(const PathfinderOptions & options, const CGameState * gs) override;
virtual CGPathNode * getInitialNode() override;
virtual std::vector<CGPathNode *> getInitialNodes() override;
virtual std::vector<CGPathNode *> calculateNeighbours(
const PathNodeInfo & source,
@ -101,18 +126,18 @@ public:
const AIPathNode * getAINode(const CGPathNode * node) const;
void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater);
bool isBattleNode(const CGPathNode * node) const;
bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber);
boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
bool isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const;
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
void setHero(HeroPtr heroPtr, const VCAI * ai);
void setHeroes(std::vector<HeroPtr> heroes, const VCAI * ai);
const CGHeroInstance * getHero() const
{
return hero;
}
const CGHeroInstance * getHero(const CGPathNode * node) const;
const std::set<const CGHeroInstance *> getAllHeroes() const;
void reset();
uint64_t evaluateDanger(const int3 & tile) const
{

View File

@ -13,8 +13,7 @@
#include "../../../CCallback.h"
#include "../../../lib/mapping/CMap.h"
std::vector<std::shared_ptr<AINodeStorage>> AIPathfinder::storagePool;
std::map<HeroPtr, std::shared_ptr<AINodeStorage>> AIPathfinder::storageMap;
std::shared_ptr<AINodeStorage> AIPathfinder::storage;
AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai)
:cb(cb), ai(ai)
@ -23,22 +22,17 @@ AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai)
void AIPathfinder::init()
{
storagePool.clear();
storageMap.clear();
storage.reset();
}
bool AIPathfinder::isTileAccessible(const HeroPtr & hero, const int3 & tile) const
{
std::shared_ptr<const AINodeStorage> nodeStorage = getStorage(hero);
return nodeStorage->isTileAccessible(tile, EPathfindingLayer::LAND)
|| nodeStorage->isTileAccessible(tile, EPathfindingLayer::SAIL);
return storage->isTileAccessible(hero, tile, EPathfindingLayer::LAND)
|| storage->isTileAccessible(hero, tile, EPathfindingLayer::SAIL);
}
std::vector<AIPath> AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 & tile) const
{
std::shared_ptr<const AINodeStorage> nodeStorage = getStorage(hero);
const TerrainTile * tileInfo = cb->getTile(tile, false);
if(!tileInfo)
@ -46,95 +40,26 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 &
return std::vector<AIPath>();
}
return nodeStorage->getChainInfo(tile, !tileInfo->isWater());
return storage->getChainInfo(tile, !tileInfo->isWater());
}
void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
{
storageMap.clear();
auto calculatePaths = [&](const CGHeroInstance * hero, std::shared_ptr<AIPathfinding::AIPathfinderConfig> config)
if(!storage)
{
logAi->debug("Recalculate paths for %s", hero->name);
cb->calculatePaths(config, hero);
};
std::vector<Task> calculationTasks;
for(HeroPtr hero : heroes)
{
std::shared_ptr<AINodeStorage> nodeStorage;
if(storageMap.size() < storagePool.size())
{
nodeStorage = storagePool.at(storageMap.size());
}
else
{
nodeStorage = std::make_shared<AINodeStorage>(cb->getMapSize());
storagePool.push_back(nodeStorage);
}
storageMap[hero] = nodeStorage;
nodeStorage->setHero(hero, ai);
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
calculationTasks.push_back(std::bind(calculatePaths, hero.get(), config));
storage = std::make_shared<AINodeStorage>(cb->getMapSize());
}
int threadsCount = std::min(
boost::thread::hardware_concurrency(),
(uint32_t)calculationTasks.size());
logAi->debug("Recalculate all paths");
if(threadsCount <= 1)
{
for(auto task : calculationTasks)
{
task();
}
}
else
{
CThreadHelper helper(&calculationTasks, threadsCount);
storage.reset();
storage->setHeroes(heroes, ai);
helper.run();
}
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
cb->calculatePaths(config);
}
void AIPathfinder::updatePaths(const HeroPtr & hero)
{
std::shared_ptr<AINodeStorage> nodeStorage;
if(!vstd::contains(storageMap, hero))
{
if(storageMap.size() < storagePool.size())
{
nodeStorage = storagePool.at(storageMap.size());
}
else
{
nodeStorage = std::make_shared<AINodeStorage>(cb->getMapSize());
storagePool.push_back(nodeStorage);
}
storageMap[hero] = nodeStorage;
nodeStorage->setHero(hero, ai);
}
else
{
nodeStorage = storageMap.at(hero);
}
logAi->debug("Recalculate paths for %s", hero->name);
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
cb->calculatePaths(config, hero.get());
updatePaths(std::vector<HeroPtr>{hero});
}
std::shared_ptr<const AINodeStorage> AIPathfinder::getStorage(const HeroPtr & hero) const
{
return storageMap.at(hero);
}

View File

@ -17,12 +17,10 @@
class AIPathfinder
{
private:
static std::vector<std::shared_ptr<AINodeStorage>> storagePool;
static std::map<HeroPtr, std::shared_ptr<AINodeStorage>> storageMap;
static std::shared_ptr<AINodeStorage> storage;
CPlayerSpecificInfoCallback * cb;
VCAI * ai;
std::shared_ptr<const AINodeStorage> getStorage(const HeroPtr & hero) const;
public:
AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
std::vector<AIPath> getPathInfo(const HeroPtr & hero, const int3 & tile) const;

View File

@ -37,7 +37,19 @@ namespace AIPathfinding
CPlayerSpecificInfoCallback * cb,
VCAI * ai,
std::shared_ptr<AINodeStorage> nodeStorage)
:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage))
:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)), aiNodeStorage(nodeStorage)
{
}
CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs)
{
if(!pathfindingHelper)
{
auto hero = aiNodeStorage->getHero(source.node);
pathfindingHelper.reset(new CPathfinderHelper(gs, hero, options));
}
return pathfindingHelper.get();
}
}

View File

@ -17,10 +17,16 @@ namespace AIPathfinding
{
class AIPathfinderConfig : public PathfinderConfig
{
private:
std::unique_ptr<CPathfinderHelper> pathfindingHelper;
std::shared_ptr<AINodeStorage> aiNodeStorage;
public:
AIPathfinderConfig(
CPlayerSpecificInfoCallback * cb,
VCAI * ai,
std::shared_ptr<AINodeStorage> nodeStorage);
virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
};
}

View File

@ -22,11 +22,21 @@ namespace AIPathfinding
return Goals::sptr(Goals::BuildBoat(shipyard));
}
const ChainActor * BuildBoatAction::getActor(const ChainActor * sourceActor) const
{
return sourceActor->resourceActor;
}
Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const
{
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
}
const ChainActor * SummonBoatAction::getActor(const ChainActor * sourceActor) const
{
return sourceActor->castActor;
}
void SummonBoatAction::applyOnDestination(
const CGHeroInstance * hero,
CDestinationNodeInfo & destination,

View File

@ -18,29 +18,13 @@ namespace AIPathfinding
{
class VirtualBoatAction : public ISpecialAction
{
private:
uint64_t specialChain;
public:
VirtualBoatAction(uint64_t specialChain)
:specialChain(specialChain)
{
}
uint64_t getSpecialChain() const
{
return specialChain;
}
virtual const ChainActor * getActor(const ChainActor * sourceActor) const = 0;
};
class SummonBoatAction : public VirtualBoatAction
{
public:
SummonBoatAction()
:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
{
}
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
virtual void applyOnDestination(
@ -52,6 +36,8 @@ namespace AIPathfinding
bool isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const;
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
private:
uint32_t getManaCost(const CGHeroInstance * hero) const;
};
@ -63,10 +49,12 @@ namespace AIPathfinding
public:
BuildBoatAction(const IShipyard * shipyard)
:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
: shipyard(shipyard)
{
}
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
};
}

View File

@ -64,7 +64,7 @@ Goals::TGoalVec PathfindingManager::howToVisitObj(ObjectIdRef obj) const
Goals::TGoalVec PathfindingManager::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
{
auto result = findPath(hero, tile, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal
auto result = findPaths(tile, allowGatherArmy, hero, [&](int3 firstTileToGet) -> Goals::TSubgoal
{
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
});
@ -86,7 +86,7 @@ Goals::TGoalVec PathfindingManager::howToVisitObj(const HeroPtr & hero, ObjectId
int3 dest = obj->visitablePos();
auto result = findPath(hero, dest, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal
auto result = findPaths(dest, allowGatherArmy, hero, [&](int3 firstTileToGet) -> Goals::TSubgoal
{
if(obj->ID.num == Obj::HERO && obj->getOwner() == hero->getOwner())
return sptr(Goals::VisitHero(obj->id.getNum()).sethero(hero).setisAbstract(true));
@ -107,10 +107,10 @@ std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, con
return pathfinder->getPathInfo(hero, tile);
}
Goals::TGoalVec PathfindingManager::findPath(
HeroPtr hero,
Goals::TGoalVec PathfindingManager::findPaths(
crint3 dest,
bool allowGatherArmy,
HeroPtr hero,
const std::function<Goals::TSubgoal(int3)> doVisitTile) const
{
Goals::TGoalVec result;
@ -125,6 +125,9 @@ Goals::TGoalVec PathfindingManager::findPath(
for(auto path : chainInfo)
{
if(hero && hero.get() != path.targetHero)
continue;
int3 firstTileToGet = path.firstTileToGet();
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Path found size=%i, first tile=%s", path.nodes.size(), firstTileToGet.toString());

View File

@ -60,10 +60,10 @@ private:
void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
Goals::TGoalVec findPath(
HeroPtr hero,
Goals::TGoalVec findPaths(
crint3 dest,
bool allowGatherArmy,
HeroPtr hero,
const std::function<Goals::TSubgoal(int3)> goalFactory) const;
Goals::TSubgoal clearWayTo(HeroPtr hero, int3 firstTileToGet) const;

View File

@ -73,14 +73,16 @@ namespace AIPathfinding
}
}
auto hero = nodeStorage->getHero();
auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
if(hero->canCastThisSpell(summonBoatSpell)
&& hero->getSpellSchoolLevel(summonBoatSpell) >= SecSkillLevel::ADVANCED)
for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())
{
// TODO: For lower school level we might need to check the existance of some boat
summonableVirtualBoat.reset(new SummonBoatAction());
auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
if(hero->canCastThisSpell(summonBoatSpell)
&& hero->getSpellSchoolLevel(summonBoatSpell) >= SecSkillLevel::ADVANCED)
{
// TODO: For lower school level we might need to check the existance of some boat
summonableVirtualBoats[hero] = std::make_shared<SummonBoatAction>();
}
}
}
@ -94,11 +96,15 @@ namespace AIPathfinding
{
virtualBoat = virtualBoats.at(destination.coord);
}
else if(
summonableVirtualBoat
&& summonableVirtualBoat->isAffordableBy(nodeStorage->getHero(), nodeStorage->getAINode(source.node)))
else
{
virtualBoat = summonableVirtualBoat;
const CGHeroInstance * hero = nodeStorage->getHero(source.node);
if(vstd::contains(summonableVirtualBoats, hero)
&& summonableVirtualBoats.at(hero)->isAffordableBy(hero, nodeStorage->getAINode(source.node)))
{
virtualBoat = summonableVirtualBoats.at(hero);
}
}
return virtualBoat;
@ -116,7 +122,7 @@ namespace AIPathfinding
auto boatNodeOptional = nodeStorage->getOrCreateNode(
node->coord,
node->layer,
node->chainMask | virtualBoat->getSpecialChain());
virtualBoat->getActor(node->actor));
if(boatNodeOptional)
{

View File

@ -26,7 +26,7 @@ namespace AIPathfinding
VCAI * ai;
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
std::shared_ptr<AINodeStorage> nodeStorage;
std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
std::map<const CGHeroInstance *, std::shared_ptr<const SummonBoatAction>> summonableVirtualBoats;
public:
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage);

View File

@ -40,8 +40,7 @@ namespace AIPathfinding
if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject)
{
auto objID = destination.nodeObject->ID;
auto enemyHero = objID == Obj::HERO && destination.objectRelations == PlayerRelations::ENEMIES;
auto enemyHero = destination.nodeHero && destination.heroRelations == PlayerRelations::ENEMIES;
if(!enemyHero && !isObjectRemovable(destination.nodeObject))
{
@ -74,7 +73,8 @@ namespace AIPathfinding
});
auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
if(guardsAlreadyBypassed && nodeStorage->isBattleNode(source.node))
auto srcNode = nodeStorage->getAINode(source.node);
if(guardsAlreadyBypassed && srcNode->actor->allowBattle)
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
@ -90,7 +90,7 @@ namespace AIPathfinding
auto battleNodeOptional = nodeStorage->getOrCreateNode(
destination.coord,
destination.node->layer,
destNode->chainMask | AINodeStorage::BATTLE_CHAIN);
destNode->actor->battleActor);
if(!battleNodeOptional)
{

View File

@ -35,7 +35,7 @@ namespace AIPathfinding
return;
}
if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node))
if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->getAINode(source.node)->actor->allowBattle)
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(