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:
parent
be4f803d4a
commit
0bff5f9eb6
@ -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];
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user