From 812df63aaeff61b115e209116e782c67506e50e5 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 3 Nov 2018 15:25:14 +0200 Subject: [PATCH] Freeze when try to bypass a few adjasent guards --- AI/VCAI/Pathfinding/AIPathfinder.cpp | 2 +- AI/VCAI/Pathfinding/AIPathfinderConfig.cpp | 692 ++++++++++----------- AI/VCAI/Pathfinding/AIPathfinderConfig.h | 17 +- 3 files changed, 349 insertions(+), 362 deletions(-) diff --git a/AI/VCAI/Pathfinding/AIPathfinder.cpp b/AI/VCAI/Pathfinding/AIPathfinder.cpp index 35bf0040f..7f3254504 100644 --- a/AI/VCAI/Pathfinding/AIPathfinder.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinder.cpp @@ -49,7 +49,7 @@ std::vector AIPathfinder::getPathInfo(HeroPtr hero, int3 tile) storageMap[hero] = nodeStorage; - auto config = std::make_shared(cb, ai, nodeStorage); + auto config = std::make_shared(cb, ai, nodeStorage); nodeStorage->setHero(hero.get()); cb->calculatePaths(config, hero.get()); diff --git a/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp b/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp index ea5279a23..3c678dc88 100644 --- a/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp @@ -13,395 +13,379 @@ #include "../../../lib/mapping/CMap.h" #include "../../../lib/mapObjects/MapObjects.h" -class BuildBoatAction : public ISpecialAction +namespace AIPathfinding { -private: - const IShipyard * shipyard; - -public: - BuildBoatAction(const IShipyard * shipyard) - :shipyard(shipyard) + class BuildBoatAction : public ISpecialAction { - } + private: + const IShipyard * shipyard; - virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override + public: + BuildBoatAction(const IShipyard * shipyard) + :shipyard(shipyard) + { + } + + virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override + { + return sptr(Goals::BuildBoat(shipyard)); + } + }; + + class BattleAction : public ISpecialAction { - return sptr(Goals::BuildBoat(shipyard)); - } -}; + private: + const int3 target; + const HeroPtr hero; -class AILayerTransitionRule : public LayerTransitionRule -{ -private: - CPlayerSpecificInfoCallback * cb; - VCAI * ai; - std::map> virtualBoats; - std::shared_ptr nodeStorage; + public: + BattleAction(const int3 target) + :target(target) + { + } -public: - AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr nodeStorage) - :cb(cb), ai(ai), nodeStorage(nodeStorage) + virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override + { + return sptr(Goals::VisitTile(target).sethero(hero)); + } + }; + + class AILayerTransitionRule : public LayerTransitionRule { - setup(); - } + private: + CPlayerSpecificInfoCallback * cb; + VCAI * ai; + std::map> virtualBoats; + std::shared_ptr nodeStorage; - virtual void process( - const PathNodeInfo & source, - CDestinationNodeInfo & destination, - const PathfinderConfig * pathfinderConfig, - CPathfinderHelper * pathfinderHelper) const override - { - LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper); - - if(!destination.blocked) + public: + AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr nodeStorage) + :cb(cb), ai(ai), nodeStorage(nodeStorage) { - return; + setup(); } - if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL - && vstd::contains(virtualBoats, destination.coord)) + virtual void process( + const PathNodeInfo & source, + CDestinationNodeInfo & destination, + const PathfinderConfig * pathfinderConfig, + CPathfinderHelper * pathfinderHelper) const override { - logAi->trace("Bypassing virtual boat at %s!", destination.coord.toString()); - - nodeStorage->updateAINode(destination.node, [&](AIPathNode * node) - { - std::shared_ptr virtualBoat = virtualBoats.at(destination.coord); - - auto boatNodeOptional = nodeStorage->getOrCreateNode( - node->coord, - node->layer, - node->chainMask | AINodeStorage::RESOURCE_CHAIN); - - if(boatNodeOptional) - { - AIPathNode * boatNode = boatNodeOptional.get(); - - boatNode->specialAction = virtualBoat; - destination.blocked = false; - destination.action = CGPathNode::ENodeAction::EMBARK; - destination.node = boatNode; - } - else - { - logAi->trace( - "Can not allocate boat node while moving %s -> %s", - source.coord.toString(), - destination.coord.toString()); - } - }); - } - } - -private: - void setup() - { - std::vector shipyards; - - for(const CGTownInstance * t : cb->getTownsInfo()) - { - if(t->hasBuilt(BuildingID::SHIPYARD)) - shipyards.push_back(t); - } - - for(const CGObjectInstance * obj : ai->visitableObjs) - { - if(obj->ID != Obj::TOWN) //towns were handled in the previous loop - { - if(const IShipyard * shipyard = IShipyard::castFrom(obj)) - shipyards.push_back(shipyard); - } - } - - for(const IShipyard * shipyard : shipyards) - { - if(shipyard->shipyardStatus() == IShipyard::GOOD) - { - int3 boatLocation = shipyard->bestLocation(); - virtualBoats[boatLocation] = std::make_shared(shipyard); - logAi->debug("Virtual boat added at %s", boatLocation.toString()); - } - } - } -}; - -class AIMovementAfterDestinationRule : public MovementAfterDestinationRule -{ -private: - CPlayerSpecificInfoCallback * cb; - std::shared_ptr nodeStorage; - -public: - AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr nodeStorage) - :cb(cb), nodeStorage(nodeStorage) - { - } - - virtual void process( - const PathNodeInfo & source, - CDestinationNodeInfo & destination, - const PathfinderConfig * pathfinderConfig, - CPathfinderHelper * pathfinderHelper) const override - { - if(nodeStorage->hasBetterChain(source, destination)) - { - destination.blocked = true; - - return; - } - - auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper); - - if(blocker == BlockingReason::NONE) - return; - - if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject) - { - auto objID = destination.nodeObject->ID; - if((objID == Obj::HERO && destination.objectRelations != PlayerRelations::ENEMIES) - || objID == Obj::SUBTERRANEAN_GATE || objID == Obj::MONOLITH_TWO_WAY - || objID == Obj::MONOLITH_ONE_WAY_ENTRANCE || objID == Obj::MONOLITH_ONE_WAY_EXIT - || objID == Obj::WHIRLPOOL) - { - destination.blocked = true; - } - - return; - } - - if(blocker == BlockingReason::DESTINATION_VISIT) - { - return; - } - - if(blocker == BlockingReason::DESTINATION_GUARDED) - { - auto srcGuardians = cb->getGuardingCreatures(source.coord); - auto destGuardians = cb->getGuardingCreatures(destination.coord); - - if(destGuardians.empty()) - { - destination.blocked = true; - - return; - } - - vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool - { - return vstd::contains(srcGuardians, destGuard); - }); - - auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size(); - if(guardsAlreadyBypassed && nodeStorage->isBattleNode(source.node)) - { - logAi->trace( - "Bypass guard at destination while moving %s -> %s", - source.coord.toString(), - destination.coord.toString()); - - return; - } - - const AIPathNode * destNode = nodeStorage->getAINode(destination.node); - auto battleNodeOptional = nodeStorage->getOrCreateNode( - destination.coord, - destination.node->layer, - destNode->chainMask | AINodeStorage::BATTLE_CHAIN); - - if(!battleNodeOptional) - { - logAi->trace( - "Can not allocate battle node while moving %s -> %s", - source.coord.toString(), - destination.coord.toString()); - - destination.blocked = true; - - return; - } - - AIPathNode * battleNode = battleNodeOptional.get(); - - if(battleNode->locked) - { - logAi->trace( - "Block bypass guard at destination while moving %s -> %s", - source.coord.toString(), - destination.coord.toString()); - - destination.blocked = true; - - return; - } - - auto hero = nodeStorage->getHero(); - auto danger = evaluateDanger(destination.coord, hero); - - destination.node = battleNode; - nodeStorage->commit(destination, source); - - if(battleNode->danger < danger) - { - battleNode->danger = danger; - } - - logAi->trace( - "Begin bypass guard at destination with danger %s while moving %s -> %s", - std::to_string(danger), - source.coord.toString(), - destination.coord.toString()); - - return; - } - - destination.blocked = true; - } -}; - -class AIMovementToDestinationRule : public MovementToDestinationRule -{ -private: - CPlayerSpecificInfoCallback * cb; - std::shared_ptr nodeStorage; - -public: - AIMovementToDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr nodeStorage) - :cb(cb), nodeStorage(nodeStorage) - { - } - - virtual void process( - const PathNodeInfo & source, - CDestinationNodeInfo & destination, - const PathfinderConfig * pathfinderConfig, - CPathfinderHelper * pathfinderHelper) const override - { - auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper); - - if(blocker == BlockingReason::NONE) - return; - - if(blocker == BlockingReason::DESTINATION_BLOCKED - && destination.action == CGPathNode::EMBARK - && nodeStorage->getAINode(destination.node)->specialAction) - { - return; - } - - if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node)) - { - auto srcGuardians = cb->getGuardingCreatures(source.coord); - auto destGuardians = cb->getGuardingCreatures(destination.coord); - - for(auto srcGuard : srcGuardians) - { - if(!vstd::contains(destGuardians, srcGuard)) - continue; - - auto guardPos = srcGuard->visitablePos(); - if(guardPos != source.coord && guardPos != destination.coord) - { - destination.blocked = true; // allow to pass monster only through guard tile - } - } + LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper); if(!destination.blocked) + { + return; + } + + if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL + && vstd::contains(virtualBoats, destination.coord)) + { + logAi->trace("Bypassing virtual boat at %s!", destination.coord.toString()); + + nodeStorage->updateAINode(destination.node, [&](AIPathNode * node) + { + std::shared_ptr virtualBoat = virtualBoats.at(destination.coord); + + auto boatNodeOptional = nodeStorage->getOrCreateNode( + node->coord, + node->layer, + node->chainMask | AINodeStorage::RESOURCE_CHAIN); + + if(boatNodeOptional) + { + AIPathNode * boatNode = boatNodeOptional.get(); + + boatNode->specialAction = virtualBoat; + destination.blocked = false; + destination.action = CGPathNode::ENodeAction::EMBARK; + destination.node = boatNode; + } + else + { + logAi->trace( + "Can not allocate boat node while moving %s -> %s", + source.coord.toString(), + destination.coord.toString()); + } + }); + } + } + + private: + void setup() + { + std::vector shipyards; + + for(const CGTownInstance * t : cb->getTownsInfo()) + { + if(t->hasBuilt(BuildingID::SHIPYARD)) + shipyards.push_back(t); + } + + for(const CGObjectInstance * obj : ai->visitableObjs) + { + if(obj->ID != Obj::TOWN) //towns were handled in the previous loop + { + if(const IShipyard * shipyard = IShipyard::castFrom(obj)) + shipyards.push_back(shipyard); + } + } + + for(const IShipyard * shipyard : shipyards) + { + if(shipyard->shipyardStatus() == IShipyard::GOOD) + { + int3 boatLocation = shipyard->bestLocation(); + virtualBoats[boatLocation] = std::make_shared(shipyard); + logAi->debug("Virtual boat added at %s", boatLocation.toString()); + } + } + } + }; + + class AIMovementAfterDestinationRule : public MovementAfterDestinationRule + { + private: + CPlayerSpecificInfoCallback * cb; + std::shared_ptr nodeStorage; + + public: + AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr nodeStorage) + :cb(cb), nodeStorage(nodeStorage) + { + } + + virtual void process( + const PathNodeInfo & source, + CDestinationNodeInfo & destination, + const PathfinderConfig * pathfinderConfig, + CPathfinderHelper * pathfinderHelper) const override + { + if(nodeStorage->hasBetterChain(source, destination)) + { + destination.blocked = true; + + return; + } + + auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper); + + if(blocker == BlockingReason::NONE) + return; + + if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject) + { + auto objID = destination.nodeObject->ID; + if((objID == Obj::HERO && destination.objectRelations != PlayerRelations::ENEMIES) + || objID == Obj::SUBTERRANEAN_GATE || objID == Obj::MONOLITH_TWO_WAY + || objID == Obj::MONOLITH_ONE_WAY_ENTRANCE || objID == Obj::MONOLITH_ONE_WAY_EXIT + || objID == Obj::WHIRLPOOL) + { + destination.blocked = true; + } + + return; + } + + if(blocker == BlockingReason::DESTINATION_VISIT) + { + return; + } + + if(blocker == BlockingReason::DESTINATION_GUARDED) + { + auto srcGuardians = cb->getGuardingCreatures(source.coord); + auto destGuardians = cb->getGuardingCreatures(destination.coord); + + if(destGuardians.empty()) + { + destination.blocked = true; + + return; + } + + vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool + { + return vstd::contains(srcGuardians, destGuard); + }); + + auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size(); + if(guardsAlreadyBypassed && nodeStorage->isBattleNode(source.node)) + { + logAi->trace( + "Bypass guard at destination while moving %s -> %s", + source.coord.toString(), + destination.coord.toString()); + + return; + } + + const AIPathNode * destNode = nodeStorage->getAINode(destination.node); + auto battleNodeOptional = nodeStorage->getOrCreateNode( + destination.coord, + destination.node->layer, + destNode->chainMask | AINodeStorage::BATTLE_CHAIN); + + if(!battleNodeOptional) + { + logAi->trace( + "Can not allocate battle node while moving %s -> %s", + source.coord.toString(), + destination.coord.toString()); + + destination.blocked = true; + + return; + } + + AIPathNode * battleNode = battleNodeOptional.get(); + + if(battleNode->locked) + { + logAi->trace( + "Block bypass guard at destination while moving %s -> %s", + source.coord.toString(), + destination.coord.toString()); + + destination.blocked = true; + + return; + } + + auto hero = nodeStorage->getHero(); + auto danger = evaluateDanger(destination.coord, hero); + + destination.node = battleNode; + nodeStorage->commit(destination, source); + + if(battleNode->danger < danger) + { + battleNode->danger = danger; + } + + battleNode->specialAction = std::make_shared(destination.coord); + + logAi->trace( + "Begin bypass guard at destination with danger %s while moving %s -> %s", + std::to_string(danger), + source.coord.toString(), + destination.coord.toString()); + + return; + } + + destination.blocked = true; + } + }; + + class AIMovementToDestinationRule : public MovementToDestinationRule + { + private: + CPlayerSpecificInfoCallback * cb; + std::shared_ptr nodeStorage; + + public: + AIMovementToDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr nodeStorage) + :cb(cb), nodeStorage(nodeStorage) + { + } + + virtual void process( + const PathNodeInfo & source, + CDestinationNodeInfo & destination, + const PathfinderConfig * pathfinderConfig, + CPathfinderHelper * pathfinderHelper) const override + { + auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper); + + if(blocker == BlockingReason::NONE) + return; + + if(blocker == BlockingReason::DESTINATION_BLOCKED + && destination.action == CGPathNode::EMBARK + && nodeStorage->getAINode(destination.node)->specialAction) + { + return; + } + + if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node)) { logAi->trace( "Bypass src guard while moving from %s to %s", source.coord.toString(), destination.coord.toString()); + + return; } - return; + destination.blocked = true; + } + }; + + class AIPreviousNodeRule : public MovementToDestinationRule + { + private: + CPlayerSpecificInfoCallback * cb; + std::shared_ptr nodeStorage; + + public: + AIPreviousNodeRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr nodeStorage) + :cb(cb), nodeStorage(nodeStorage) + { } - destination.blocked = true; - } -}; - -class AIPreviousNodeRule : public MovementToDestinationRule -{ -private: - CPlayerSpecificInfoCallback * cb; - std::shared_ptr nodeStorage; - -public: - AIPreviousNodeRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr nodeStorage) - :cb(cb), nodeStorage(nodeStorage) - { - } - - virtual void process( - const PathNodeInfo & source, - CDestinationNodeInfo & destination, - const PathfinderConfig * pathfinderConfig, - CPathfinderHelper * pathfinderHelper) const override - { -//FIXME: unused -// auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper); - - if(source.guarded) + virtual void process( + const PathNodeInfo & source, + CDestinationNodeInfo & destination, + const PathfinderConfig * pathfinderConfig, + CPathfinderHelper * pathfinderHelper) const override { - auto srcGuardian = cb->guardingCreaturePosition(source.node->coord); - - if(srcGuardian == source.node->coord) + if(source.node->action == CGPathNode::ENodeAction::BLOCKING_VISIT || source.node->action == CGPathNode::ENodeAction::VISIT) { - // guardian tile is used as chain junction + // we can not directly bypass objects, we need to interact with them first destination.node->theNodeBefore = source.node; logAi->trace( - "Link src node %s to destination node %s while bypassing guard", + "Link src node %s to destination node %s while bypassing visitable obj", source.coord.toString(), destination.coord.toString()); return; } + + auto aiSourceNode = nodeStorage->getAINode(source.node); + + if(aiSourceNode->specialAction) + { + // there is some action on source tile which should be performed before we can bypass it + destination.node->theNodeBefore = source.node; + } } - - if(source.node->action == CGPathNode::ENodeAction::BLOCKING_VISIT || source.node->action == CGPathNode::ENodeAction::VISIT) - { - // we can not directly bypass objects, we need to interact with them first - destination.node->theNodeBefore = source.node; - - logAi->trace( - "Link src node %s to destination node %s while bypassing visitable obj", - source.coord.toString(), - destination.coord.toString()); - - return; - } - - auto aiSourceNode = nodeStorage->getAINode(source.node); - - if(aiSourceNode->specialAction) - { - // there is some action on source tile which should be performed before we can bypass it - destination.node->theNodeBefore = source.node; - } - } -}; - -std::vector> makeRuleset( - CPlayerSpecificInfoCallback * cb, - VCAI * ai, - std::shared_ptr nodeStorage) -{ - std::vector> rules = { - std::make_shared(cb, ai, nodeStorage), - std::make_shared(), - std::make_shared(cb, nodeStorage), - std::make_shared(), - std::make_shared(cb, nodeStorage), - std::make_shared(cb, nodeStorage) }; - return rules; -} + std::vector> makeRuleset( + CPlayerSpecificInfoCallback * cb, + VCAI * ai, + std::shared_ptr nodeStorage) + { + std::vector> rules = { + std::make_shared(cb, ai, nodeStorage), + std::make_shared(), + std::make_shared(cb, nodeStorage), + std::make_shared(), + std::make_shared(cb, nodeStorage), + std::make_shared(cb, nodeStorage) + }; -AIPathfinderConfig::AIPathfinderConfig( - CPlayerSpecificInfoCallback * cb, - VCAI * ai, - std::shared_ptr nodeStorage) - :PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)) -{ + return rules; + } + + AIPathfinderConfig::AIPathfinderConfig( + CPlayerSpecificInfoCallback * cb, + VCAI * ai, + std::shared_ptr nodeStorage) + :PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)) + { + } } diff --git a/AI/VCAI/Pathfinding/AIPathfinderConfig.h b/AI/VCAI/Pathfinding/AIPathfinderConfig.h index 5e05fdf28..22ecd1d67 100644 --- a/AI/VCAI/Pathfinding/AIPathfinderConfig.h +++ b/AI/VCAI/Pathfinding/AIPathfinderConfig.h @@ -13,11 +13,14 @@ #include "AINodeStorage.h" #include "../VCAI.h" -class AIPathfinderConfig : public PathfinderConfig +namespace AIPathfinding { -public: - AIPathfinderConfig( - CPlayerSpecificInfoCallback * cb, - VCAI * ai, - std::shared_ptr nodeStorage); -}; \ No newline at end of file + class AIPathfinderConfig : public PathfinderConfig + { + public: + AIPathfinderConfig( + CPlayerSpecificInfoCallback * cb, + VCAI * ai, + std::shared_ptr nodeStorage); + }; +} \ No newline at end of file