From 9d70b28c9b6f2a754e2a4e1f9cefe2865033d004 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 15 May 2021 20:59:43 +0300 Subject: [PATCH] AI: hero chain basic logic --- AI/Nullkiller/AIUtility.cpp | 14 +- AI/Nullkiller/AIUtility.h | 4 +- AI/Nullkiller/CMakeLists.txt | 4 + AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 108 +++++----- AI/Nullkiller/Pathfinding/AINodeStorage.h | 36 +--- AI/Nullkiller/Pathfinding/Actors.cpp | 204 ++++++++++++++++++ AI/Nullkiller/Pathfinding/Actors.h | 80 +++++++ .../Rules/AIMovementAfterDestinationRule.cpp | 3 + .../Pathfinding/Rules/AIMovementCostRule.cpp | 66 ++++++ .../Pathfinding/Rules/AIMovementCostRule.h | 35 +++ 10 files changed, 462 insertions(+), 92 deletions(-) create mode 100644 AI/Nullkiller/Pathfinding/Actors.cpp create mode 100644 AI/Nullkiller/Pathfinding/Actors.h create mode 100644 AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.cpp create mode 100644 AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.h diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index d7ab02f12..5713cf83c 100644 --- a/AI/Nullkiller/AIUtility.cpp +++ b/AI/Nullkiller/AIUtility.cpp @@ -297,7 +297,7 @@ creInfo infoFromDC(const dwellingContent & dc) return ci; } -ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t) +ui64 howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) { ui64 aivalue = 0; TResources availableRes = cb->getResourceAmount(); @@ -333,17 +333,17 @@ ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t) return aivalue; } -ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t) +ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) { ui64 ret = 0; - int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount(); + int freeHeroSlots = GameConstants::ARMY_SIZE - target->stacksCount(); std::vector toMove; - for(auto const slot : t->Slots()) + for(auto const slot : source->Slots()) { //can be merged woth another stack? - SlotID dst = h->getSlotFor(slot.second->getCreatureID()); - if(h->hasStackAtSlot(dst)) - ret += t->getPower(slot.first); + SlotID dst = target->getSlotFor(slot.second->getCreatureID()); + if(target->hasStackAtSlot(dst)) + ret += source->getPower(slot.first); else toMove.push_back(slot.second); } diff --git a/AI/Nullkiller/AIUtility.h b/AI/Nullkiller/AIUtility.h index 913780d74..7c5c3686a 100644 --- a/AI/Nullkiller/AIUtility.h +++ b/AI/Nullkiller/AIUtility.h @@ -173,8 +173,8 @@ bool isSafeToVisit(HeroPtr h, crint3 tile); bool compareHeroStrength(HeroPtr h1, HeroPtr h2); bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2); bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2); -ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t); -ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t); +ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source); +ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source); class CDistanceSorter { diff --git a/AI/Nullkiller/CMakeLists.txt b/AI/Nullkiller/CMakeLists.txt index 9e0db96b7..a6a038143 100644 --- a/AI/Nullkiller/CMakeLists.txt +++ b/AI/Nullkiller/CMakeLists.txt @@ -12,6 +12,7 @@ set(VCAI_SRCS Pathfinding/AIPathfinder.cpp Pathfinding/AINodeStorage.cpp Pathfinding/PathfindingManager.cpp + Pathfinding/Actors.cpp Pathfinding/Actions/BattleAction.cpp Pathfinding/Actions/BoatActions.cpp Pathfinding/Actions/TownPortalAction.cpp @@ -19,6 +20,7 @@ set(VCAI_SRCS Pathfinding/Rules/AIMovementAfterDestinationRule.cpp Pathfinding/Rules/AIMovementToDestinationRule.cpp Pathfinding/Rules/AIPreviousNodeRule.cpp + Pathfinding/Rules/AIMovementCostRule.cpp AIUtility.cpp AIhelper.cpp ResourceManager.cpp @@ -61,6 +63,7 @@ set(VCAI_HEADERS Pathfinding/AIPathfinder.h Pathfinding/AINodeStorage.h Pathfinding/PathfindingManager.h + Pathfinding/Actors.h Pathfinding/Actions/ISpecialAction.h Pathfinding/Actions/BattleAction.h Pathfinding/Actions/BoatActions.h @@ -69,6 +72,7 @@ set(VCAI_HEADERS Pathfinding/Rules/AIMovementAfterDestinationRule.h Pathfinding/Rules/AIMovementToDestinationRule.h Pathfinding/Rules/AIPreviousNodeRule.h + Pathfinding/Rules/AIMovementCostRule.h AIUtility.h AIhelper.h ResourceManager.h diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index a2b31ae26..440cf90e6 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -87,7 +87,10 @@ void AINodeStorage::updateAINode(CGPathNode * node, std::function AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, const ChainActor * actor) +boost::optional AINodeStorage::getOrCreateNode( + const int3 & pos, + const EPathfindingLayer layer, + const ChainActor * actor) { auto chains = nodes[pos.x][pos.y][pos.z][layer]; @@ -115,7 +118,7 @@ std::vector AINodeStorage::getInitialNodes() for(auto actorPtr : actors) { - const ChainActor * actor = actorPtr.get(); + ChainActor * actor = actorPtr.get(); AIPathNode * initialNode = getOrCreateNode(actor->initialPosition, actor->layer, actor) .get(); @@ -198,9 +201,61 @@ std::vector AINodeStorage::calculateNeighbours( } } + if((source.node->layer == EPathfindingLayer::LAND || source.node->layer == EPathfindingLayer::SAIL) + && source.node->turns < 1) + { + addHeroChain(neighbours, srcNode); + } + return neighbours; } +void AINodeStorage::addHeroChain(std::vector & result, const AIPathNode * srcNode) +{ + auto chains = nodes[srcNode->coord.x][srcNode->coord.y][srcNode->coord.z][srcNode->layer]; + + for(const AIPathNode & node : chains) + { + if(!node.locked || !node.actor || node.action == CGPathNode::ENodeAction::UNKNOWN && node.actor->hero) + { + continue; + } + + addHeroChain(result, srcNode, &node); + addHeroChain(result, &node, srcNode); + } +} + +void AINodeStorage::addHeroChain( + std::vector & result, + const AIPathNode * carrier, + const AIPathNode * other) +{ + if(carrier->actor->canExchange(other->actor)) + { + bool hasLessMp = carrier->turns > other->turns || carrier->moveRemains < other->moveRemains; + bool hasLessExperience = carrier->actor->hero->exp < other->actor->hero->exp; + + if(hasLessMp && hasLessExperience) + return; + + auto newActor = carrier->actor->exchange(other->actor); + auto chainNodeOptional = getOrCreateNode(carrier->coord, carrier->layer, newActor); + + if(!chainNodeOptional) + return; + + auto chainNode = chainNodeOptional.get(); + + if(chainNode->locked) + return; + + chainNode->specialAction = newActor->getExchangeAction(); + + result.push_back(chainNode); + } +} + const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const { auto aiNode = getAINode(node); @@ -230,7 +285,7 @@ void AINodeStorage::setHeroes(std::vector heroes, const VCAI * _ai) { uint64_t mask = 1 << actors.size(); - actors.push_back(std::make_shared(hero.get(), mask)); + actors.push_back(std::make_shared(hero.get(), mask)); } } @@ -465,51 +520,4 @@ uint64_t AIPath::getTotalDanger(HeroPtr hero) const uint64_t danger = pathDanger > targetObjectDanger ? pathDanger : targetObjectDanger; 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; - armyValue = hero->getTotalStrength(); - - 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 ab29f4a91..2a5d0a8f5 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -16,38 +16,7 @@ #include "../FuzzyHelper.h" #include "../Goals/AbstractGoal.h" #include "Actions/ISpecialAction.h" - -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; - uint64_t armyValue; - - ChainActor() - { - } - - ChainActor(const CGHeroInstance * hero, int chainMask); -}; +#include "Actors.h" struct AIPathNode : public CGPathNode { @@ -100,7 +69,8 @@ private: STRONG_INLINE void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility); - + void addHeroChain(std::vector & result, const AIPathNode * srcNode); + void addHeroChain(std::vector & result, const AIPathNode * carrier, const AIPathNode * other); public: /// 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; diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp new file mode 100644 index 000000000..a0e3b3100 --- /dev/null +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -0,0 +1,204 @@ +/* +* AINodeStorage.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#include "StdInc.h" +#include "Actors.h" +#include "../../../CCallback.h" +#include "../../../lib/mapping/CMap.h" +#include "../../../lib/mapObjects/MapObjects.h" + +#include "../Goals/VisitHero.h" + +class ExchangeAction : public ISpecialAction +{ +private: + const CGHeroInstance * target; + const CGHeroInstance * source; + +public: + ExchangeAction(const CGHeroInstance * target, const CGHeroInstance * source) + :target(target), source(source) + { } + + virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override + { + return Goals::sptr(Goals::VisitHero(target->id.getNum()).sethero(hero)); + } +}; + +ChainActor::ChainActor(const CGHeroInstance * hero, int chainMask) + :hero(hero), isMovable(true), chainMask(chainMask), creatureSet(hero), carrierParent(nullptr), otherParent(nullptr) +{ + baseActor = static_cast(this); + initialPosition = hero->visitablePos(); + layer = hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND; + initialMovement = hero->movement; + initialTurn = 0; + armyValue = hero->getTotalStrength(); +} + +ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy) + :hero(carrier->hero), isMovable(true), creatureSet(heroArmy), initialPosition(-1), + carrierParent(carrier), otherParent(other), chainMask(carrier->chainMask | other->chainMask) +{ + baseActor = static_cast(this); + armyValue = heroArmy->getArmyStrength(); +} + +HeroActor::HeroActor(const CGHeroInstance * hero, int chainMask) + :ChainActor(hero, chainMask) +{ + setupSpecialActors(); +} + +HeroActor::HeroActor(const ChainActor * carrier, const ChainActor * other) + :ChainActor( + carrier, + other, + pickBestCreatures(carrier->creatureSet, other->creatureSet)) +{ + setupSpecialActors(); + exchangeAction.reset(new ExchangeAction(carrier->hero, other->hero)); +} + +std::shared_ptr ChainActor::getExchangeAction() const +{ + return baseActor->exchangeAction; +} + +void ChainActor::setBaseActor(HeroActor * base) +{ + baseActor = base; + hero = base->hero; + layer = base->layer; + initialMovement = base->initialMovement; + initialTurn = base->initialTurn; + armyValue = base->armyValue; + chainMask = base->chainMask; + creatureSet = base->creatureSet; +} + +void HeroActor::setupSpecialActors() +{ + auto allActors = std::vector{ this }; + + for(int i = 1; i <= SPECIAL_ACTORS_COUNT; i++) + { + ChainActor & specialActor = specialActors[i - 1]; + + specialActor.setBaseActor(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 <= SPECIAL_ACTORS_COUNT; i++) + { + ChainActor * actor = allActors[i]; + + actor->battleActor = allActors[i | 1]; + actor->castActor = allActors[i | 2]; + actor->resourceActor = allActors[i | 4]; + } +} + +ChainActor * ChainActor::exchange(const ChainActor * other) const +{ + return baseActor->exchange(this, other); +} + +bool ChainActor::canExchange(const ChainActor * other) const +{ + return baseActor->canExchange(other->baseActor); +} + +namespace vstd +{ + template + typename M::mapped_type & getOrCompute(M &m, Key const& k, F f) + { + typedef typename M::mapped_type V; + + std::pair r = m.insert(typename M::value_type(k, V())); + V &v = r.first->second; + + if(r.second) + f(v); + + return v; + } +} + +bool HeroActor::canExchange(const HeroActor * other) +{ + return vstd::getOrCompute(canExchangeCache, other, [&](bool & result) { + result = (chainMask & other->chainMask) == 0 + && howManyReinforcementsCanGet(creatureSet, other->creatureSet) > armyValue / 10; + }); +} + +ChainActor * HeroActor::exchange(const ChainActor * specialActor, const ChainActor * other) +{ + HeroActor * result; + const HeroActor * otherBase = other->getBaseActor(); + + if(vstd::contains(exchangeMap, otherBase)) + result = exchangeMap.at(otherBase); + else + { + // TODO: decide where to release this CCreatureSet and HeroActor. Probably custom ~ctor? + result = new HeroActor(specialActor, other); + exchangeMap[otherBase] = result; + } + + if(specialActor == this) + return result; + + int index = vstd::find_pos_if(specialActors, [specialActor](const ChainActor & actor) -> bool { + return &actor == specialActor; + }); + + return &result->specialActors[index]; +} + +CCreatureSet * HeroActor::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const +{ + CCreatureSet * target = new CCreatureSet(); + const CCreatureSet * armies[] = { army1, army2 }; + + //we calculate total strength for each creature type available in armies + std::map creToPower; + for(auto armyPtr : armies) + { + for(auto & i : armyPtr->Slots()) + { + creToPower[i.second->type] += i.second->getPower(); + } + } + //TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc) + int armySize = creToPower.size(); + + vstd::amin(armySize, GameConstants::ARMY_SIZE); + + for(int i = 0; i < armySize && !creToPower.empty(); i++) //pick the creatures from which we can get most power, as many as dest can fit + { + typedef const std::pair & CrePowerPair; + auto creIt = boost::max_element(creToPower, [](CrePowerPair lhs, CrePowerPair rhs) + { + return lhs.second < rhs.second; + }); + + target->addToSlot(SlotID(i), creIt->first->idNumber, TQuantity(creIt->second)); + creToPower.erase(creIt); + } + + return target; +} \ No newline at end of file diff --git a/AI/Nullkiller/Pathfinding/Actors.h b/AI/Nullkiller/Pathfinding/Actors.h new file mode 100644 index 000000000..018d97aad --- /dev/null +++ b/AI/Nullkiller/Pathfinding/Actors.h @@ -0,0 +1,80 @@ +/* +* AINodeStorage.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#pragma once + +#include "../../../lib/CPathfinder.h" +#include "../../../lib/mapObjects/CGHeroInstance.h" +#include "../AIUtility.h" +#include "Actions/ISpecialAction.h" + +class HeroActor; + +class ChainActor +{ +protected: + HeroActor * baseActor; + ChainActor(const CGHeroInstance * hero, int chainMask); + ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy); + +public: + uint64_t chainMask; + bool isMovable; + bool allowUseResources; + bool allowBattle; + bool allowSpellCast; + const CGHeroInstance * hero; + const CCreatureSet * creatureSet; + const ChainActor * battleActor; + const ChainActor * castActor; + const ChainActor * resourceActor; + const ChainActor * carrierParent; + const ChainActor * otherParent; + int3 initialPosition; + EPathfindingLayer layer; + uint32_t initialMovement; + uint32_t initialTurn; + uint64_t armyValue; + + ChainActor(){} + ChainActor * exchange(const ChainActor * other) const; + bool canExchange(const ChainActor * other) const; + void setBaseActor(HeroActor * base); + const HeroActor * getBaseActor() const { return baseActor; } + std::shared_ptr getExchangeAction() const; + +}; + +class HeroActor : public ChainActor +{ +public: + static const int SPECIAL_ACTORS_COUNT = 7; + +private: + ChainActor specialActors[SPECIAL_ACTORS_COUNT]; + std::map exchangeMap; + std::map canExchangeCache; + + void setupSpecialActors(); + +public: + std::shared_ptr exchangeAction; + // chain flags, can be combined meaning hero exchange and so on + + HeroActor() + { + } + + HeroActor(const CGHeroInstance * hero, int chainMask); + HeroActor(const ChainActor * carrier, const ChainActor * other); + ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other); + bool canExchange(const HeroActor * other); + CCreatureSet * pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const; +}; diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp index 55aedcd44..45ec27230 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp @@ -44,6 +44,9 @@ namespace AIPathfinding if(!enemyHero && !isObjectRemovable(destination.nodeObject)) { + if(nodeStorage->getHero(destination.node) == destination.nodeHero) + return; + destination.blocked = true; } diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.cpp new file mode 100644 index 000000000..d2044317a --- /dev/null +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.cpp @@ -0,0 +1,66 @@ +/* +* AIMovementCostRule.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#include "StdInc.h" +#include "AIMovementCostRule.h" + +namespace AIPathfinding +{ + AIMovementCostRule::AIMovementCostRule(std::shared_ptr nodeStorage) + : nodeStorage(nodeStorage) + { + } + + void AIMovementCostRule::process( + const PathNodeInfo & source, + CDestinationNodeInfo & destination, + const PathfinderConfig * pathfinderConfig, + CPathfinderHelper * pathfinderHelper) const + { + auto srcNode = nodeStorage->getAINode(source.node); + auto dstNode = nodeStorage->getAINode(destination.node); + auto srcActor = srcNode->actor; + auto dstActor = dstNode->actor; + + if(srcActor == dstActor) + { + MovementCostRule::process(source, destination, pathfinderConfig, pathfinderHelper); + + return; + } + + auto carrierActor = dstActor->carrierParent; + auto otherActor = dstActor->otherParent; + + if(source.coord == destination.coord) + { + auto carrierNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, carrierActor).get(); + auto otherNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, otherActor).get(); + + if(carrierNode->turns >= otherNode->turns) + { + destination.turn = carrierNode->turns; + destination.cost = carrierNode->cost; + + return; + } + + double waitingCost = otherNode->turns - carrierNode->turns - 1.0 + + carrierNode->moveRemains / (double)pathfinderHelper->getMaxMovePoints(carrierNode->layer); + + destination.turn = otherNode->turns; + destination.cost = waitingCost; + } + else + { + // TODO: exchange through sail->land border might be more sofisticated + destination.blocked = true; + } + } +} diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.h b/AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.h new file mode 100644 index 000000000..3c205784f --- /dev/null +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementCostRule.h @@ -0,0 +1,35 @@ +/* +* AIMovementCostRule.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#pragma once + +#include "../AINodeStorage.h" +#include "../../VCAI.h" +#include "../../../../CCallback.h" +#include "../../../../lib/mapping/CMap.h" +#include "../../../../lib/mapObjects/MapObjects.h" + +namespace AIPathfinding +{ + class AIMovementCostRule : public MovementCostRule + { + private: + std::shared_ptr nodeStorage; + + public: + AIMovementCostRule(std::shared_ptr nodeStorage); + + virtual void process( + const PathNodeInfo & source, + CDestinationNodeInfo & destination, + const PathfinderConfig * pathfinderConfig, + CPathfinderHelper * pathfinderHelper) const override; + }; +}