From 0bff5f9eb6b3e6dd3f77d8d6bbd5c0b85955ec64 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 15 May 2021 19:22:49 +0300 Subject: [PATCH] AI pathfinding shared storage --- AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 170 ++++++++++++++---- AI/Nullkiller/Pathfinding/AINodeStorage.h | 67 ++++--- AI/Nullkiller/Pathfinding/AIPathfinder.cpp | 101 ++--------- AI/Nullkiller/Pathfinding/AIPathfinder.h | 4 +- .../Pathfinding/AIPathfinderConfig.cpp | 14 +- .../Pathfinding/AIPathfinderConfig.h | 6 + .../Pathfinding/Actions/BoatActions.cpp | 10 ++ .../Pathfinding/Actions/BoatActions.h | 24 +-- .../Pathfinding/PathfindingManager.cpp | 11 +- .../Pathfinding/PathfindingManager.h | 4 +- .../Rules/AILayerTransitionRule.cpp | 30 ++-- .../Pathfinding/Rules/AILayerTransitionRule.h | 2 +- .../Rules/AIMovementAfterDestinationRule.cpp | 8 +- .../Rules/AIMovementToDestinationRule.cpp | 2 +- 14 files changed, 262 insertions(+), 191 deletions(-) diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index 95af9d144..d8fe96fbe 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -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(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap; - const PlayerColor player = hero->tempOwner; + const auto & fow = static_cast(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(node); @@ -82,25 +87,20 @@ void AINodeStorage::updateAINode(CGPathNode * node, std::functionchainMask & BATTLE_CHAIN) > 0; -} - -boost::optional AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber) +boost::optional 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 AINodeStorage::getOrCreateNode(const int3 & pos, c return boost::none; } -CGPathNode * AINodeStorage::getInitialNode() +std::vector AINodeStorage::getInitialNodes() { - auto hpos = hero->getPosition(false); - auto initialNode = - getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN) - .get(); + std::vector 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 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 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 AINodeStorage::getAllHeroes() const +{ + std::set heroes; + + for(auto actor : actors) + { + if(actor->hero) + heroes.insert(hero); + } + + return heroes; +} + +void AINodeStorage::setHeroes(std::vector 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(hero.get(), mask)); + } } std::vector AINodeStorage::calculateTeleportations( @@ -206,7 +246,7 @@ std::vector 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 AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const @@ -338,7 +388,7 @@ std::vector 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 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{ 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]; + } +} \ No newline at end of file diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index 6130e4e10..55941560e 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -17,14 +17,43 @@ #include "../Goals/AbstractGoal.h" #include "Actions/ISpecialAction.h" -struct AIPathNode; +class ChainActor +{ +private: + std::vector 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 specialAction; + const ChainActor * actor; }; struct AIPathNodeInfo @@ -40,6 +69,7 @@ struct AIPath std::vector nodes; std::shared_ptr specialAction; uint64_t targetObjectDanger; + const CGHeroInstance * targetHero; AIPath(); @@ -65,26 +95,21 @@ private: const VCAI * ai; const CGHeroInstance * hero; std::unique_ptr dangerEvaluator; + std::vector> 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 getInitialNodes() override; virtual std::vector calculateNeighbours( const PathNodeInfo & source, @@ -101,18 +126,18 @@ public: const AIPathNode * getAINode(const CGPathNode * node) const; void updateAINode(CGPathNode * node, std::function updater); - bool isBattleNode(const CGPathNode * node) const; bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const; - boost::optional getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber); + boost::optional getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor); std::vector 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 heroes, const VCAI * ai); - const CGHeroInstance * getHero() const - { - return hero; - } + const CGHeroInstance * getHero(const CGPathNode * node) const; + + const std::set getAllHeroes() const; + + void reset(); uint64_t evaluateDanger(const int3 & tile) const { diff --git a/AI/Nullkiller/Pathfinding/AIPathfinder.cpp b/AI/Nullkiller/Pathfinding/AIPathfinder.cpp index 4c9e6bfac..c6d163cfc 100644 --- a/AI/Nullkiller/Pathfinding/AIPathfinder.cpp +++ b/AI/Nullkiller/Pathfinding/AIPathfinder.cpp @@ -13,8 +13,7 @@ #include "../../../CCallback.h" #include "../../../lib/mapping/CMap.h" -std::vector> AIPathfinder::storagePool; -std::map> AIPathfinder::storageMap; +std::shared_ptr 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 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 AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 & tile) const { - std::shared_ptr nodeStorage = getStorage(hero); - const TerrainTile * tileInfo = cb->getTile(tile, false); if(!tileInfo) @@ -46,95 +40,26 @@ std::vector AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 & return std::vector(); } - return nodeStorage->getChainInfo(tile, !tileInfo->isWater()); + return storage->getChainInfo(tile, !tileInfo->isWater()); } void AIPathfinder::updatePaths(std::vector heroes) { - storageMap.clear(); - - auto calculatePaths = [&](const CGHeroInstance * hero, std::shared_ptr config) + if(!storage) { - logAi->debug("Recalculate paths for %s", hero->name); - - cb->calculatePaths(config, hero); - }; - - std::vector calculationTasks; - - for(HeroPtr hero : heroes) - { - std::shared_ptr nodeStorage; - - if(storageMap.size() < storagePool.size()) - { - nodeStorage = storagePool.at(storageMap.size()); - } - else - { - nodeStorage = std::make_shared(cb->getMapSize()); - storagePool.push_back(nodeStorage); - } - - storageMap[hero] = nodeStorage; - nodeStorage->setHero(hero, ai); - - auto config = std::make_shared(cb, ai, nodeStorage); - - calculationTasks.push_back(std::bind(calculatePaths, hero.get(), config)); + storage = std::make_shared(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(cb, ai, storage); + cb->calculatePaths(config); } void AIPathfinder::updatePaths(const HeroPtr & hero) { - std::shared_ptr nodeStorage; - - if(!vstd::contains(storageMap, hero)) - { - if(storageMap.size() < storagePool.size()) - { - nodeStorage = storagePool.at(storageMap.size()); - } - else - { - nodeStorage = std::make_shared(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(cb, ai, nodeStorage); - - cb->calculatePaths(config, hero.get()); + updatePaths(std::vector{hero}); } - -std::shared_ptr AIPathfinder::getStorage(const HeroPtr & hero) const -{ - return storageMap.at(hero); -} - diff --git a/AI/Nullkiller/Pathfinding/AIPathfinder.h b/AI/Nullkiller/Pathfinding/AIPathfinder.h index 8c7199b15..30ac8b41f 100644 --- a/AI/Nullkiller/Pathfinding/AIPathfinder.h +++ b/AI/Nullkiller/Pathfinding/AIPathfinder.h @@ -17,12 +17,10 @@ class AIPathfinder { private: - static std::vector> storagePool; - static std::map> storageMap; + static std::shared_ptr storage; CPlayerSpecificInfoCallback * cb; VCAI * ai; - std::shared_ptr getStorage(const HeroPtr & hero) const; public: AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai); std::vector getPathInfo(const HeroPtr & hero, const int3 & tile) const; diff --git a/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp b/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp index d1e4f727f..7547a2500 100644 --- a/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp +++ b/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp @@ -37,7 +37,19 @@ namespace AIPathfinding CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr 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(); + } } diff --git a/AI/Nullkiller/Pathfinding/AIPathfinderConfig.h b/AI/Nullkiller/Pathfinding/AIPathfinderConfig.h index e57fc9954..fb8017780 100644 --- a/AI/Nullkiller/Pathfinding/AIPathfinderConfig.h +++ b/AI/Nullkiller/Pathfinding/AIPathfinderConfig.h @@ -17,10 +17,16 @@ namespace AIPathfinding { class AIPathfinderConfig : public PathfinderConfig { + private: + std::unique_ptr pathfindingHelper; + std::shared_ptr aiNodeStorage; + public: AIPathfinderConfig( CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr nodeStorage); + + virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override; }; } diff --git a/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp b/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp index ce598ce10..254254669 100644 --- a/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp +++ b/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp @@ -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, diff --git a/AI/Nullkiller/Pathfinding/Actions/BoatActions.h b/AI/Nullkiller/Pathfinding/Actions/BoatActions.h index 7ac594488..e3db85971 100644 --- a/AI/Nullkiller/Pathfinding/Actions/BoatActions.h +++ b/AI/Nullkiller/Pathfinding/Actions/BoatActions.h @@ -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; }; } \ No newline at end of file diff --git a/AI/Nullkiller/Pathfinding/PathfindingManager.cpp b/AI/Nullkiller/Pathfinding/PathfindingManager.cpp index e4c7462f6..b839bd71d 100644 --- a/AI/Nullkiller/Pathfinding/PathfindingManager.cpp +++ b/AI/Nullkiller/Pathfinding/PathfindingManager.cpp @@ -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 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 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()); diff --git a/AI/Nullkiller/Pathfinding/PathfindingManager.h b/AI/Nullkiller/Pathfinding/PathfindingManager.h index 95ec9949b..a781e10ab 100644 --- a/AI/Nullkiller/Pathfinding/PathfindingManager.h +++ b/AI/Nullkiller/Pathfinding/PathfindingManager.h @@ -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 goalFactory) const; Goals::TSubgoal clearWayTo(HeroPtr hero, int3 firstTileToGet) const; diff --git a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp index 7a1fae0dc..bc7034d5b 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp @@ -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(); + } } } @@ -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) { diff --git a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h index 804722842..6cbfd8ea8 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h +++ b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h @@ -26,7 +26,7 @@ namespace AIPathfinding VCAI * ai; std::map> virtualBoats; std::shared_ptr nodeStorage; - std::shared_ptr summonableVirtualBoat; + std::map> summonableVirtualBoats; public: AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr nodeStorage); diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp index 83e51161b..a12c2e98f 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp @@ -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) { diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp index e34575157..c6fec5dc2 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp @@ -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(