From 08295933566a331282453f9be0d32415c379cc6d Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Mon, 26 Dec 2022 20:17:39 +0200 Subject: [PATCH] NKAI: composite pathfinder actions and fix for guarded bordergate --- AI/Nullkiller/AIUtility.cpp | 2 +- AI/Nullkiller/Analyzers/ObjectClusterizer.cpp | 2 +- AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 25 +++++++- AI/Nullkiller/Pathfinding/AINodeStorage.h | 2 + .../Pathfinding/Actions/SpecialAction.cpp | 63 +++++++++++++++++++ .../Pathfinding/Actions/SpecialAction.h | 34 +++++++++- .../Rules/AILayerTransitionRule.cpp | 2 +- .../Rules/AIMovementAfterDestinationRule.cpp | 9 ++- 8 files changed, 131 insertions(+), 8 deletions(-) diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index a7ad46dfa..5dbdafa25 100644 --- a/AI/Nullkiller/AIUtility.cpp +++ b/AI/Nullkiller/AIUtility.cpp @@ -255,7 +255,7 @@ bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, Pla { auto quest = dynamic_cast(obj); - if(quest->passableFor(playerColor)) + if(quest->wasMyColorVisited(playerColor)) return true; } diff --git a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp index 88db0921a..4d09c2971 100644 --- a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp +++ b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp @@ -114,7 +114,7 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons if(blockerObject) { - blockers.push_back(blockerObject); + blockers.insert(blockers.begin(), blockerObject); } } diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index 1f03dbb0b..8577a9f2f 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -52,6 +52,27 @@ AISharedStorage::~AISharedStorage() } } +void AIPathNode::addSpecialAction(std::shared_ptr action) +{ + if(!specialAction) + { + specialAction = action; + } + else + { + auto parts = specialAction->getParts(); + + if(parts.empty()) + { + parts.push_back(specialAction); + } + + parts.push_back(action); + + specialAction = std::make_shared(parts); + } +} + AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes) : sizes(Sizes), ai(ai), cb(ai->cb.get()), nodes(Sizes) { @@ -765,7 +786,7 @@ void HeroChainCalculationTask::addHeroChain(const std::vector if(exchangeNode->actor->actorAction) { exchangeNode->theNodeBefore = carrier; - exchangeNode->specialAction = exchangeNode->actor->actorAction; + exchangeNode->addSpecialAction(exchangeNode->actor->actorAction); } exchangeNode->chainOther = other; @@ -1045,7 +1066,7 @@ struct TowmPortalFinder movementCost); node->theNodeBefore = bestNode; - node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown)); + node->addSpecialAction(std::make_shared(targetTown)); } return nodeOptional; diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index 10989da5d..bf4b852e3 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -55,6 +55,8 @@ struct AIPathNode : public CGPathNode return accessible == CGPathNode::EAccessibility::NOT_SET || accessible == CGPathNode::EAccessibility::BLOCKED; } + + void addSpecialAction(std::shared_ptr action); }; struct AIPathNodeInfo diff --git a/AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp b/AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp index d85395a8b..4fc1e4a45 100644 --- a/AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp +++ b/AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp @@ -27,4 +27,67 @@ void SpecialAction::execute(const CGHeroInstance * hero) const throw cannotFulfillGoalException("Can not execute " + toString()); } +bool CompositeAction::canAct(const AIPathNode * source) const +{ + for(auto part : parts) + { + if(!part->canAct(source)) return false; + } + + return true; +} + +Goals::TSubgoal CompositeAction::decompose(const CGHeroInstance * hero) const +{ + for(auto part : parts) + { + auto goal = part->decompose(hero); + + if(!goal->invalid()) return goal; + } + + return SpecialAction::decompose(hero); +} + +void CompositeAction::execute(const CGHeroInstance * hero) const +{ + for(auto part : parts) + { + part->execute(hero); + } +} + +void CompositeAction::applyOnDestination( + const CGHeroInstance * hero, + CDestinationNodeInfo & destination, + const PathNodeInfo & source, + AIPathNode * dstNode, + const AIPathNode * srcNode) const +{ + for(auto part : parts) + { + part->applyOnDestination(hero, destination, source, dstNode, srcNode); + } +} + +std::string CompositeAction::toString() const +{ + std::string result = ""; + + for(auto part : parts) + { + result += ", " + part->toString(); + } + + return result; +} + +const CGObjectInstance * CompositeAction::targetObject() const +{ + if(parts.empty()) + return nullptr; + + return parts.front()->targetObject(); +} + } diff --git a/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h b/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h index b0d8799ad..7ed87814c 100644 --- a/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h +++ b/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h @@ -36,7 +36,7 @@ public: const CGHeroInstance * hero, CDestinationNodeInfo & destination, const PathNodeInfo & source, - AIPathNode * dstMode, + AIPathNode * dstNode, const AIPathNode * srcNode) const { } @@ -44,6 +44,38 @@ public: virtual std::string toString() const = 0; virtual const CGObjectInstance * targetObject() const { return nullptr; } + + virtual std::vector> getParts() const + { + return {}; + } +}; + +class CompositeAction : public SpecialAction +{ +private: + std::vector> parts; + +public: + CompositeAction(std::vector> parts) : parts(parts) {} + + bool canAct(const AIPathNode * source) const override; + void execute(const CGHeroInstance * hero) const override; + std::string toString() const override; + const CGObjectInstance * targetObject() const override; + Goals::TSubgoal decompose(const CGHeroInstance * hero) const override; + + std::vector> getParts() const override + { + return parts; + } + + void applyOnDestination( + const CGHeroInstance * hero, + CDestinationNodeInfo & destination, + const PathNodeInfo & source, + AIPathNode * dstNode, + const AIPathNode * srcNode) const override; }; } diff --git a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp index 596dda5de..39af7c893 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp @@ -133,7 +133,7 @@ namespace AIPathfinding if(boatNode->action == CGPathNode::UNKNOWN) { - boatNode->specialAction = virtualBoat; + boatNode->addSpecialAction(virtualBoat); destination.blocked = false; destination.action = CGPathNode::ENodeAction::EMBARK; destination.node = boatNode; diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp index 032ecd946..dafcb4758 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp @@ -157,7 +157,7 @@ namespace AIPathfinding nodeStorage->updateAINode(destination.node, [&](AIPathNode * node) { - node->specialAction.reset(new QuestAction(questAction)); + node->addSpecialAction(std::make_shared(questAction)); }); } @@ -279,6 +279,11 @@ namespace AIPathfinding if(loss < actualArmyValue) { + if(destNode->specialAction) + { + battleNode->specialAction = destNode->specialAction; + } + destination.node = battleNode; nodeStorage->commit(destination, source); @@ -288,7 +293,7 @@ namespace AIPathfinding AIPreviousNodeRule(nodeStorage).process(source, destination, pathfinderConfig, pathfinderHelper); - battleNode->specialAction = std::make_shared(destination.coord); + battleNode->addSpecialAction(std::make_shared(destination.coord)); #if NKAI_PATHFINDER_TRACE_LEVEL >= 1 logAi->trace(