Explorar o código

AI: extract pathfinding special actions and rules to separate files

Andrii Danylchenko %!s(int64=6) %!d(string=hai) anos
pai
achega
5454848938

+ 15 - 0
AI/VCAI/CMakeLists.txt

@@ -12,6 +12,13 @@ set(VCAI_SRCS
 		Pathfinding/AIPathfinder.cpp
 		Pathfinding/AINodeStorage.cpp
 		Pathfinding/PathfindingManager.cpp
+		Pathfinding/Actions/BattleAction.cpp
+		Pathfinding/Actions/BoatActions.cpp
+		Pathfinding/Actions/TownPortalAction.cpp
+		Pathfinding/Rules/AILayerTransitionRule.cpp
+		Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
+		Pathfinding/Rules/AIMovementToDestinationRule.cpp
+		Pathfinding/Rules/AIPreviousNodeRule.cpp
 		AIUtility.cpp
 		AIhelper.cpp
 		ResourceManager.cpp
@@ -54,6 +61,14 @@ set(VCAI_HEADERS
 		Pathfinding/AIPathfinder.h
 		Pathfinding/AINodeStorage.h
 		Pathfinding/PathfindingManager.h
+		Pathfinding/Actions/ISpecialAction.h
+		Pathfinding/Actions/BattleAction.h
+		Pathfinding/Actions/BoatActions.h
+		Pathfinding/Actions/TownPortalAction.h
+		Pathfinding/Rules/AILayerTransitionRule.h
+		Pathfinding/Rules/AIMovementAfterDestinationRule.h
+		Pathfinding/Rules/AIMovementToDestinationRule.h
+		Pathfinding/Rules/AIPreviousNodeRule.h
 		AIUtility.h
 		AIhelper.h
 		ResourceManager.h

+ 3 - 22
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -9,13 +9,14 @@
 */
 #include "StdInc.h"
 #include "AINodeStorage.h"
+#include "Actions/TownPortalAction.h"
 #include "../Goals/Goals.h"
 #include "../../../CCallback.h"
 #include "../../../lib/mapping/CMap.h"
 #include "../../../lib/mapObjects/MapObjects.h"
-
 #include "../../../lib/PathfinderUtil.h"
 #include "../../../lib/CPlayerState.h"
+
 extern boost::thread_specific_ptr<CCallback> cb;
 
 
@@ -191,26 +192,6 @@ void AINodeStorage::setHero(HeroPtr heroPtr)
 	hero = heroPtr.get();
 }
 
-class TownPortalAction : public ISpecialAction
-{
-private:
-	const CGTownInstance * target;
-	const HeroPtr  hero;
-
-public:
-	TownPortalAction(const CGTownInstance * target)
-		:target(target)
-	{
-	}
-
-	virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
-	{
-		const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
-
-		return sptr(Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL).settown(targetTown).settile(targetTown->visitablePos()));
-	}
-};
-
 std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
 	const PathNodeInfo & source,
 	const PathfinderConfig * pathfinderConfig,
@@ -299,7 +280,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
 				AIPathNode * node = nodeOptional.get();
 
 				node->theNodeBefore = source.node;
-				node->specialAction.reset(new TownPortalAction(targetTown));
+				node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown));
 				node->moveRemains = source.node->moveRemains;
 				
 				neighbours.push_back(node);

+ 1 - 15
AI/VCAI/Pathfinding/AINodeStorage.h

@@ -14,24 +14,10 @@
 #include "../../../lib/mapObjects/CGHeroInstance.h"
 #include "../AIUtility.h"
 #include "../Goals/AbstractGoal.h"
+#include "Actions/ISpecialAction.h"
 
 struct AIPathNode;
 
-class ISpecialAction
-{
-public:
-	virtual Goals::TSubgoal whatToDo(HeroPtr hero) const = 0;
-
-	virtual void applyOnDestination(
-		HeroPtr hero,
-		CDestinationNodeInfo & destination,
-		const PathNodeInfo & source,
-		AIPathNode * dstMode,
-		const AIPathNode * srcNode) const
-	{
-	}
-};
-
 struct AIPathNode : public CGPathNode
 {
 	uint32_t chainMask;

+ 4 - 478
AI/VCAI/Pathfinding/AIPathfinderConfig.cpp

@@ -9,487 +9,13 @@
 */
 #include "StdInc.h"
 #include "AIPathfinderConfig.h"
-#include "../Goals/Goals.h"
-#include "../../../CCallback.h"
-#include "../../../lib/mapping/CMap.h"
-#include "../../../lib/mapObjects/MapObjects.h"
+#include "Rules/AILayerTransitionRule.h"
+#include "Rules/AIMovementAfterDestinationRule.h"
+#include "Rules/AIMovementToDestinationRule.h"
+#include "Rules/AIPreviousNodeRule.h"
 
 namespace AIPathfinding
 {
-	class VirtualBoatAction : public ISpecialAction
-	{
-	private:
-		uint64_t specialChain;
-
-	public:
-		VirtualBoatAction(uint64_t specialChain)
-			:specialChain(specialChain)
-		{
-		}
-
-		uint64_t getSpecialChain() const
-		{
-			return specialChain;
-		}
-	};
-
-	class BuildBoatAction : public VirtualBoatAction
-	{
-	private:
-		const IShipyard * shipyard;
-
-	public:
-		BuildBoatAction(const IShipyard * shipyard)
-			:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
-		{
-		}
-
-		virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
-		{
-			return sptr(Goals::BuildBoat(shipyard));
-		}
-	};
-
-	class SummonBoatAction : public VirtualBoatAction
-	{
-	public:
-		SummonBoatAction()
-			:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
-		{
-		}
-
-		virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
-		{
-			return sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
-		}
-
-		virtual void applyOnDestination(
-			HeroPtr hero,
-			CDestinationNodeInfo & destination,
-			const PathNodeInfo & source,
-			AIPathNode * dstMode,
-			const AIPathNode * srcNode) const override
-		{
-			dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
-			dstMode->theNodeBefore = source.node;
-		}
-
-		bool isAffordableBy(HeroPtr hero, const AIPathNode * source) const
-		{
-#ifdef VCMI_TRACE_PATHFINDER
-			logAi->trace(
-				"Hero %s has %d mana and needed %d and already spent %d",
-				hero->name,
-				hero->mana,
-				getManaCost(hero),
-				source->manaCost);
-#endif
-
-			return hero->mana >= source->manaCost + getManaCost(hero);
-		}
-
-	private:
-		uint32_t getManaCost(HeroPtr hero) const
-		{
-			SpellID summonBoat = SpellID::SUMMON_BOAT;
-
-			return hero->getSpellCost(summonBoat.toSpell());
-		}
-	};
-
-	class BattleAction : public ISpecialAction
-	{
-	private:
-		const int3  target;
-		const HeroPtr  hero;
-
-	public:
-		BattleAction(const int3 target)
-			:target(target)
-		{
-		}
-
-		virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
-		{
-			return sptr(Goals::VisitTile(target).sethero(hero));
-		}
-	};
-
-	class AILayerTransitionRule : public LayerTransitionRule
-	{
-	private:
-		CPlayerSpecificInfoCallback * cb;
-		VCAI * ai;
-		std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
-		std::shared_ptr<AINodeStorage> nodeStorage;
-		std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
-
-	public:
-		AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
-			:cb(cb), ai(ai), nodeStorage(nodeStorage)
-		{
-			setup();
-		}
-
-		virtual void process(
-			const PathNodeInfo & source,
-			CDestinationNodeInfo & destination,
-			const PathfinderConfig * pathfinderConfig,
-			CPathfinderHelper * pathfinderHelper) const override
-		{
-			LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper);
-
-			if(!destination.blocked)
-			{
-				return;
-			}
-
-			if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
-			{
-				std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
-
-				if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
-				{
-#ifdef VCMI_TRACE_PATHFINDER
-					logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
-#endif
-				}
-			}
-		}
-
-	private:
-		void setup()
-		{
-			std::vector<const IShipyard *> 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<BuildBoatAction>(shipyard);
-					logAi->debug("Virtual boat added at %s", boatLocation.toString());
-				}
-			}
-
-			auto hero = nodeStorage->getHero();
-			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
-				summonableVirtualBoat.reset(new SummonBoatAction());
-			}
-		}
-
-		std::shared_ptr<const VirtualBoatAction> findVirtualBoat(
-			CDestinationNodeInfo &destination,
-			const PathNodeInfo &source) const
-		{
-			std::shared_ptr<const VirtualBoatAction> virtualBoat;
-
-			if(vstd::contains(virtualBoats, destination.coord))
-			{
-				virtualBoat = virtualBoats.at(destination.coord);
-			}
-			else if(
-				summonableVirtualBoat
-				&& summonableVirtualBoat->isAffordableBy(nodeStorage->getHero(), nodeStorage->getAINode(source.node)))
-			{
-				virtualBoat = summonableVirtualBoat;
-			}
-
-			return virtualBoat;
-		}
-
-		bool tryEmbarkVirtualBoat(
-			CDestinationNodeInfo &destination,
-			const PathNodeInfo &source,
-			std::shared_ptr<const VirtualBoatAction> virtualBoat) const
-		{
-			bool result = false;
-
-			nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
-			{
-				auto boatNodeOptional = nodeStorage->getOrCreateNode(
-					node->coord,
-					node->layer,
-					node->chainMask | virtualBoat->getSpecialChain());
-
-				if(boatNodeOptional)
-				{
-					AIPathNode * boatNode = boatNodeOptional.get();
-
-					if(boatNode->action == CGPathNode::UNKNOWN)
-					{
-						boatNode->specialAction = virtualBoat;
-						destination.blocked = false;
-						destination.action = CGPathNode::ENodeAction::EMBARK;
-						destination.node = boatNode;
-						result = true;
-					}
-					else
-					{
-#ifdef VCMI_TRACE_PATHFINDER
-						logAi->trace(
-							"Special transition node already allocated. Blocked moving %s -> %s",
-							source.coord.toString(),
-							destination.coord.toString());
-#endif
-					}
-				}
-				else
-				{
-					logAi->debug(
-						"Can not allocate special transition node while moving %s -> %s",
-						source.coord.toString(),
-						destination.coord.toString());
-				}
-			});
-
-			return result;
-		}
-	};
-
-	class AIMovementAfterDestinationRule : public MovementAfterDestinationRule
-	{
-	private:
-		CPlayerSpecificInfoCallback * cb;
-		std::shared_ptr<AINodeStorage> nodeStorage;
-
-	public:
-		AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr<AINodeStorage> 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;
-				auto enemyHero = objID == Obj::HERO && destination.objectRelations == PlayerRelations::ENEMIES;
-
-				if(!enemyHero && !isObjectRemovable(destination.nodeObject))
-				{
-					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))
-				{
-#ifdef VCMI_TRACE_PATHFINDER
-					logAi->trace(
-						"Bypass guard at destination while moving %s -> %s",
-						source.coord.toString(),
-						destination.coord.toString());
-#endif
-
-					return;
-				}
-
-				const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
-				auto battleNodeOptional = nodeStorage->getOrCreateNode(
-					destination.coord,
-					destination.node->layer,
-					destNode->chainMask | AINodeStorage::BATTLE_CHAIN);
-
-				if(!battleNodeOptional)
-				{
-#ifdef VCMI_TRACE_PATHFINDER
-					logAi->trace(
-						"Can not allocate battle node while moving %s -> %s",
-						source.coord.toString(),
-						destination.coord.toString());
-#endif
-
-					destination.blocked = true;
-
-					return;
-				}
-
-				AIPathNode *  battleNode = battleNodeOptional.get();
-
-				if(battleNode->locked)
-				{
-#ifdef VCMI_TRACE_PATHFINDER
-					logAi->trace(
-						"Block bypass guard at destination while moving %s -> %s",
-						source.coord.toString(),
-						destination.coord.toString());
-#endif
-					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<BattleAction>(destination.coord);
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace(
-					"Begin bypass guard at destination with danger %s while moving %s -> %s",
-					std::to_string(danger),
-					source.coord.toString(),
-					destination.coord.toString());
-#endif
-				return;
-			}
-
-			destination.blocked = true;
-		}
-	};
-
-	class AIMovementToDestinationRule : public MovementToDestinationRule
-	{
-	private:
-		std::shared_ptr<AINodeStorage> nodeStorage;
-
-	public:
-		AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage)
-			: 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))
-			{
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace(
-					"Bypass src guard while moving from %s to %s",
-					source.coord.toString(),
-					destination.coord.toString());
-#endif
-				return;
-			}
-
-			destination.blocked = true;
-		}
-	};
-
-	class AIPreviousNodeRule : public MovementToDestinationRule
-	{
-	private:
-		std::shared_ptr<AINodeStorage> nodeStorage;
-
-	public:
-		AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage)
-			: nodeStorage(nodeStorage)
-		{
-		}
-
-		virtual void process(
-			const PathNodeInfo & source,
-			CDestinationNodeInfo & destination,
-			const PathfinderConfig * pathfinderConfig,
-			CPathfinderHelper * pathfinderHelper) const override
-		{
-			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;
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace(
-					"Link src node %s to destination node %s while bypassing visitable obj",
-					source.coord.toString(),
-					destination.coord.toString());
-#endif
-				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<std::shared_ptr<IPathfindingRule>> makeRuleset(
 		CPlayerSpecificInfoCallback * cb,
 		VCAI * ai,

+ 21 - 0
AI/VCAI/Pathfinding/Actions/BattleAction.cpp

@@ -0,0 +1,21 @@
+/*
+* BattleAction.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 "../../Goals/VisitTile.h"
+#include "BattleAction.h"
+
+namespace AIPathfinding
+{
+	Goals::TSubgoal BattleAction::whatToDo(const HeroPtr & hero) const
+	{
+		return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
+	}
+}

+ 30 - 0
AI/VCAI/Pathfinding/Actions/BattleAction.h

@@ -0,0 +1,30 @@
+/*
+* BattleAction.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 "ISpecialAction.h"
+
+namespace AIPathfinding
+{
+	class BattleAction : public ISpecialAction
+	{
+	private:
+		const int3 targetTile;
+
+	public:
+		BattleAction(const int3 targetTile)
+			:targetTile(targetTile)
+		{
+		}
+
+		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
+	};
+}

+ 61 - 0
AI/VCAI/Pathfinding/Actions/BoatActions.cpp

@@ -0,0 +1,61 @@
+/*
+* BoatActions.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 "../../Goals/AdventureSpellCast.h"
+#include "../../Goals/BuildBoat.h"
+#include "../../../../lib/mapping/CMap.h"
+#include "../../../../lib/mapObjects/MapObjects.h"
+#include "BoatActions.h"
+
+namespace AIPathfinding
+{
+	Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const
+	{
+		return Goals::sptr(Goals::BuildBoat(shipyard));
+	}
+
+	Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const
+	{
+		return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
+	}
+
+	void SummonBoatAction::applyOnDestination(
+		const HeroPtr & hero,
+		CDestinationNodeInfo & destination,
+		const PathNodeInfo & source,
+		AIPathNode * dstMode,
+		const AIPathNode * srcNode) const
+	{
+		dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
+		dstMode->theNodeBefore = source.node;
+	}
+
+	bool SummonBoatAction::isAffordableBy(const HeroPtr & hero, const AIPathNode * source) const
+	{
+#ifdef VCMI_TRACE_PATHFINDER
+		logAi->trace(
+			"Hero %s has %d mana and needed %d and already spent %d",
+			hero->name,
+			hero->mana,
+			getManaCost(hero),
+			source->manaCost);
+#endif
+
+		return hero->mana >= source->manaCost + getManaCost(hero);
+	}
+
+	uint32_t SummonBoatAction::getManaCost(const HeroPtr & hero) const
+	{
+		SpellID summonBoat = SpellID::SUMMON_BOAT;
+
+		return hero->getSpellCost(summonBoat.toSpell());
+	}
+}

+ 72 - 0
AI/VCAI/Pathfinding/Actions/BoatActions.h

@@ -0,0 +1,72 @@
+/*
+* BoatActions.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 "ISpecialAction.h"
+#include "../../../lib/mapping/CMap.h"
+#include "../../../lib/mapObjects/MapObjects.h"
+
+namespace AIPathfinding
+{
+	class VirtualBoatAction : public ISpecialAction
+	{
+	private:
+		uint64_t specialChain;
+
+	public:
+		VirtualBoatAction(uint64_t specialChain)
+			:specialChain(specialChain)
+		{
+		}
+
+		uint64_t getSpecialChain() const
+		{
+			return specialChain;
+		}
+	};
+	
+	class SummonBoatAction : public VirtualBoatAction
+	{
+	public:
+		SummonBoatAction()
+			:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
+		{
+		}
+
+		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
+
+		virtual void applyOnDestination(
+			const HeroPtr & hero,
+			CDestinationNodeInfo & destination,
+			const PathNodeInfo & source,
+			AIPathNode * dstMode,
+			const AIPathNode * srcNode) const override;
+
+		bool isAffordableBy(const HeroPtr & hero, const AIPathNode * source) const;
+
+	private:
+		uint32_t getManaCost(const HeroPtr & hero) const;
+	};
+
+	class BuildBoatAction : public VirtualBoatAction
+	{
+	private:
+		const IShipyard * shipyard;
+
+	public:
+		BuildBoatAction(const IShipyard * shipyard)
+			:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
+		{
+		}
+
+		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
+	};
+}

+ 31 - 0
AI/VCAI/Pathfinding/Actions/ISpecialAction.h

@@ -0,0 +1,31 @@
+/*
+* ISpecialAction.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 "../../AIUtility.h"
+#include "../../Goals/AbstractGoal.h"
+
+class AIPathNode;
+
+class ISpecialAction
+{
+public:
+	virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
+
+	virtual void applyOnDestination(
+		const HeroPtr & hero,
+		CDestinationNodeInfo & destination,
+		const PathNodeInfo & source,
+		AIPathNode * dstMode,
+		const AIPathNode * srcNode) const
+	{
+	}
+};

+ 24 - 0
AI/VCAI/Pathfinding/Actions/TownPortalAction.cpp

@@ -0,0 +1,24 @@
+/*
+* TownPortalAction.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 "../../Goals/AdventureSpellCast.h"
+#include "../../../../lib/mapping/CMap.h"
+#include "../../../../lib/mapObjects/MapObjects.h"
+#include "TownPortalAction.h"
+
+using namespace AIPathfinding;
+
+Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const
+{
+	const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
+
+	return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL).settown(targetTown).settile(targetTown->visitablePos()));
+}

+ 33 - 0
AI/VCAI/Pathfinding/Actions/TownPortalAction.h

@@ -0,0 +1,33 @@
+/*
+* TownPortalAction.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 "ISpecialAction.h"
+#include "../../../lib/mapping/CMap.h"
+#include "../../../lib/mapObjects/MapObjects.h"
+#include "../../Goals/AdventureSpellCast.h"
+
+namespace AIPathfinding
+{
+	class TownPortalAction : public ISpecialAction
+	{
+	private:
+		const CGTownInstance * target;
+
+	public:
+		TownPortalAction(const CGTownInstance * target)
+			:target(target)
+		{
+		}
+
+		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
+	};
+}

+ 154 - 0
AI/VCAI/Pathfinding/Rules/AILayerTransitionRule.cpp

@@ -0,0 +1,154 @@
+/*
+* AILayerTransitionRule.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 "AILayerTransitionRule.h"
+
+namespace AIPathfinding
+{
+	AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
+		:cb(cb), ai(ai), nodeStorage(nodeStorage)
+	{
+		setup();
+	}
+
+	void AILayerTransitionRule::process(
+		const PathNodeInfo & source,
+		CDestinationNodeInfo & destination,
+		const PathfinderConfig * pathfinderConfig,
+		CPathfinderHelper * pathfinderHelper) const
+	{
+		LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper);
+
+		if(!destination.blocked)
+		{
+			return;
+		}
+
+		if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
+		{
+			std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
+
+			if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
+			{
+#ifdef VCMI_TRACE_PATHFINDER
+				logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
+#endif
+			}
+		}
+	}
+
+	void AILayerTransitionRule::setup()
+	{
+		std::vector<const IShipyard *> 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<BuildBoatAction>(shipyard);
+				logAi->debug("Virtual boat added at %s", boatLocation.toString());
+			}
+		}
+
+		auto hero = nodeStorage->getHero();
+		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
+			summonableVirtualBoat.reset(new SummonBoatAction());
+		}
+	}
+
+	std::shared_ptr<const VirtualBoatAction> AILayerTransitionRule::findVirtualBoat(
+		CDestinationNodeInfo & destination,
+		const PathNodeInfo & source) const
+	{
+		std::shared_ptr<const VirtualBoatAction> virtualBoat;
+
+		if(vstd::contains(virtualBoats, destination.coord))
+		{
+			virtualBoat = virtualBoats.at(destination.coord);
+		}
+		else if(
+			summonableVirtualBoat
+			&& summonableVirtualBoat->isAffordableBy(nodeStorage->getHero(), nodeStorage->getAINode(source.node)))
+		{
+			virtualBoat = summonableVirtualBoat;
+		}
+
+		return virtualBoat;
+	}
+
+	bool AILayerTransitionRule::tryEmbarkVirtualBoat(
+		CDestinationNodeInfo & destination,
+		const PathNodeInfo & source,
+		std::shared_ptr<const VirtualBoatAction> virtualBoat) const
+	{
+		bool result = false;
+
+		nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
+		{
+			auto boatNodeOptional = nodeStorage->getOrCreateNode(
+				node->coord,
+				node->layer,
+				node->chainMask | virtualBoat->getSpecialChain());
+
+			if(boatNodeOptional)
+			{
+				AIPathNode * boatNode = boatNodeOptional.get();
+
+				if(boatNode->action == CGPathNode::UNKNOWN)
+				{
+					boatNode->specialAction = virtualBoat;
+					destination.blocked = false;
+					destination.action = CGPathNode::ENodeAction::EMBARK;
+					destination.node = boatNode;
+					result = true;
+				}
+				else
+				{
+#ifdef VCMI_TRACE_PATHFINDER
+					logAi->trace(
+						"Special transition node already allocated. Blocked moving %s -> %s",
+						source.coord.toString(),
+						destination.coord.toString());
+#endif
+				}
+			}
+			else
+			{
+				logAi->debug(
+					"Can not allocate special transition node while moving %s -> %s",
+					source.coord.toString(),
+					destination.coord.toString());
+			}
+		});
+
+		return result;
+	}
+}

+ 52 - 0
AI/VCAI/Pathfinding/Rules/AILayerTransitionRule.h

@@ -0,0 +1,52 @@
+/*
+* AILayerTransitionRule.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 "../Actions/BoatActions.h"
+#include "../../../../CCallback.h"
+#include "../../../../lib/mapping/CMap.h"
+#include "../../../../lib/mapObjects/MapObjects.h"
+
+namespace AIPathfinding
+{
+	class AILayerTransitionRule : public LayerTransitionRule
+	{
+	private:
+		CPlayerSpecificInfoCallback * cb;
+		VCAI * ai;
+		std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
+		std::shared_ptr<AINodeStorage> nodeStorage;
+		std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
+
+	public:
+		AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage);
+
+		virtual void process(
+			const PathNodeInfo & source,
+			CDestinationNodeInfo & destination,
+			const PathfinderConfig * pathfinderConfig,
+			CPathfinderHelper * pathfinderHelper) const override;
+
+	private:
+		void setup();
+
+		std::shared_ptr<const VirtualBoatAction> findVirtualBoat(
+			CDestinationNodeInfo & destination,
+			const PathNodeInfo & source) const;
+
+		bool tryEmbarkVirtualBoat(
+			CDestinationNodeInfo & destination,
+			const PathNodeInfo & source,
+			std::shared_ptr<const VirtualBoatAction> virtualBoat) const;
+	};
+}

+ 148 - 0
AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp

@@ -0,0 +1,148 @@
+/*
+* AIMovementAfterDestinationRule.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 "AIMovementAfterDestinationRule.h"
+#include "../Actions/BattleAction.h"
+
+namespace AIPathfinding
+{
+	AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
+		CPlayerSpecificInfoCallback * cb, 
+		std::shared_ptr<AINodeStorage> nodeStorage)
+		:cb(cb), nodeStorage(nodeStorage)
+	{
+	}
+
+	void AIMovementAfterDestinationRule::process(
+		const PathNodeInfo & source,
+		CDestinationNodeInfo & destination,
+		const PathfinderConfig * pathfinderConfig,
+		CPathfinderHelper * pathfinderHelper) const
+	{
+		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;
+			auto enemyHero = objID == Obj::HERO && destination.objectRelations == PlayerRelations::ENEMIES;
+
+			if(!enemyHero && !isObjectRemovable(destination.nodeObject))
+			{
+				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))
+			{
+#ifdef VCMI_TRACE_PATHFINDER
+				logAi->trace(
+					"Bypass guard at destination while moving %s -> %s",
+					source.coord.toString(),
+					destination.coord.toString());
+#endif
+
+				return;
+			}
+
+			const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
+			auto battleNodeOptional = nodeStorage->getOrCreateNode(
+				destination.coord,
+				destination.node->layer,
+				destNode->chainMask | AINodeStorage::BATTLE_CHAIN);
+
+			if(!battleNodeOptional)
+			{
+#ifdef VCMI_TRACE_PATHFINDER
+				logAi->trace(
+					"Can not allocate battle node while moving %s -> %s",
+					source.coord.toString(),
+					destination.coord.toString());
+#endif
+
+				destination.blocked = true;
+
+				return;
+			}
+
+			AIPathNode *  battleNode = battleNodeOptional.get();
+
+			if(battleNode->locked)
+			{
+#ifdef VCMI_TRACE_PATHFINDER
+				logAi->trace(
+					"Block bypass guard at destination while moving %s -> %s",
+					source.coord.toString(),
+					destination.coord.toString());
+#endif
+				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<BattleAction>(destination.coord);
+#ifdef VCMI_TRACE_PATHFINDER
+			logAi->trace(
+				"Begin bypass guard at destination with danger %s while moving %s -> %s",
+				std::to_string(danger),
+				source.coord.toString(),
+				destination.coord.toString());
+#endif
+			return;
+		}
+
+		destination.blocked = true;
+	}
+}

+ 36 - 0
AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.h

@@ -0,0 +1,36 @@
+/*
+* AIMovementAfterDestinationRule.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 AIMovementAfterDestinationRule : public MovementAfterDestinationRule
+	{
+	private:
+		CPlayerSpecificInfoCallback * cb;
+		std::shared_ptr<AINodeStorage> nodeStorage;
+
+	public:
+		AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr<AINodeStorage> nodeStorage);
+
+		virtual void process(
+			const PathNodeInfo & source,
+			CDestinationNodeInfo & destination,
+			const PathfinderConfig * pathfinderConfig,
+			CPathfinderHelper * pathfinderHelper) const override;
+	};
+}

+ 51 - 0
AI/VCAI/Pathfinding/Rules/AIMovementToDestinationRule.cpp

@@ -0,0 +1,51 @@
+/*
+* AIMovementToDestinationRule.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 "AIMovementToDestinationRule.h"
+
+namespace AIPathfinding
+{
+	AIMovementToDestinationRule::AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage)
+		: nodeStorage(nodeStorage)
+	{
+	}
+
+	void AIMovementToDestinationRule::process(
+		const PathNodeInfo & source,
+		CDestinationNodeInfo & destination,
+		const PathfinderConfig * pathfinderConfig,
+		CPathfinderHelper * pathfinderHelper) const
+	{
+		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))
+		{
+#ifdef VCMI_TRACE_PATHFINDER
+			logAi->trace(
+				"Bypass src guard while moving from %s to %s",
+				source.coord.toString(),
+				destination.coord.toString());
+#endif
+			return;
+		}
+
+		destination.blocked = true;
+	}
+}

+ 35 - 0
AI/VCAI/Pathfinding/Rules/AIMovementToDestinationRule.h

@@ -0,0 +1,35 @@
+/*
+* AIMovementToDestinationRule.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 AIMovementToDestinationRule : public MovementToDestinationRule
+	{
+	private:
+		std::shared_ptr<AINodeStorage> nodeStorage;
+
+	public:
+		AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage);
+
+		virtual void process(
+			const PathNodeInfo & source,
+			CDestinationNodeInfo & destination,
+			const PathfinderConfig * pathfinderConfig,
+			CPathfinderHelper * pathfinderHelper) const override;
+	};
+}

+ 47 - 0
AI/VCAI/Pathfinding/Rules/AIPreviousNodeRule.cpp

@@ -0,0 +1,47 @@
+/*
+* AIPreviousNodeRule.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 "AIPreviousNodeRule.h"
+
+namespace AIPathfinding
+{
+	AIPreviousNodeRule::AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage)
+		: nodeStorage(nodeStorage)
+	{
+	}
+
+	void AIPreviousNodeRule::process(
+		const PathNodeInfo & source,
+		CDestinationNodeInfo & destination,
+		const PathfinderConfig * pathfinderConfig,
+		CPathfinderHelper * pathfinderHelper) const
+	{
+		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;
+#ifdef VCMI_TRACE_PATHFINDER
+			logAi->trace(
+				"Link src node %s to destination node %s while bypassing visitable obj",
+				source.coord.toString(),
+				destination.coord.toString());
+#endif
+			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;
+		}
+	}
+}

+ 35 - 0
AI/VCAI/Pathfinding/Rules/AIPreviousNodeRule.h

@@ -0,0 +1,35 @@
+/*
+* AIPreviousNodeRule.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 AIPreviousNodeRule : public MovementToDestinationRule
+	{
+	private:
+		std::shared_ptr<AINodeStorage> nodeStorage;
+
+	public:
+		AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage);
+
+		virtual void process(
+			const PathNodeInfo & source,
+			CDestinationNodeInfo & destination,
+			const PathfinderConfig * pathfinderConfig,
+			CPathfinderHelper * pathfinderHelper) const override;
+	};
+}

+ 15 - 0
AI/VCAI/VCAI.vcxproj

@@ -168,10 +168,20 @@
     <ClCompile Include="Goals\Win.cpp" />
     <ClCompile Include="main.cpp" />
     <ClCompile Include="MapObjectsEvaluator.cpp" />
+    <ClInclude Include="Pathfinding\Actions\BattleAction.h" />
+    <ClInclude Include="Pathfinding\Actions\TownPortalAction.h" />
+    <ClCompile Include="Pathfinding\Actions\BattleAction.cpp" />
+    <ClCompile Include="Pathfinding\Actions\TownPortalAction.cpp" />
+    <ClInclude Include="Pathfinding\Actions\BoatActions.h" />
+    <ClCompile Include="Pathfinding\Actions\BoatActions.cpp" />
     <ClCompile Include="Pathfinding\AINodeStorage.cpp" />
     <ClCompile Include="Pathfinding\AIPathfinder.cpp" />
     <ClCompile Include="Pathfinding\AIPathfinderConfig.cpp" />
     <ClCompile Include="Pathfinding\PathfindingManager.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AILayerTransitionRule.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AIMovementAfterDestinationRule.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AIMovementToDestinationRule.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AIPreviousNodeRule.cpp" />
     <ClCompile Include="ResourceManager.cpp" />
     <ClCompile Include="SectorMap.cpp" />
     <ClCompile Include="StdInc.cpp">
@@ -214,10 +224,15 @@
     <ClInclude Include="Goals\VisitTile.h" />
     <ClInclude Include="Goals\Win.h" />
     <ClInclude Include="MapObjectsEvaluator.h" />
+    <ClInclude Include="Pathfinding\Actions\ISpecialAction.h" />
     <ClInclude Include="Pathfinding\AINodeStorage.h" />
     <ClInclude Include="Pathfinding\AIPathfinder.h" />
     <ClInclude Include="Pathfinding\AIPathfinderConfig.h" />
     <ClInclude Include="Pathfinding\PathfindingManager.h" />
+    <ClInclude Include="Pathfinding\Rules\AILayerTransitionRule.h" />
+    <ClInclude Include="Pathfinding\Rules\AIMovementAfterDestinationRule.h" />
+    <ClInclude Include="Pathfinding\Rules\AIMovementToDestinationRule.h" />
+    <ClInclude Include="Pathfinding\Rules\AIPreviousNodeRule.h" />
     <ClInclude Include="ResourceManager.h" />
     <ClInclude Include="SectorMap.h" />
     <ClInclude Include="StdInc.h" />

+ 25 - 0
AI/VCAI/VCAI.vcxproj.filters

@@ -90,6 +90,15 @@
     <ClCompile Include="Goals\AdventureSpellCast.cpp">
       <Filter>Goals</Filter>
     </ClCompile>
+    <ClCompile Include="Pathfinding\Actions\BoatActions.cpp">
+      <Filter>Pathfinding\Actions</Filter>
+    </ClCompile>
+    <ClCompile Include="Pathfinding\Actions\TownPortalAction.cpp" />
+    <ClCompile Include="Pathfinding\Actions\BattleAction.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AILayerTransitionRule.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AIMovementAfterDestinationRule.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AIMovementToDestinationRule.cpp" />
+    <ClCompile Include="Pathfinding\Rules\AIPreviousNodeRule.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="AIhelper.h" />
@@ -189,6 +198,16 @@
     <ClInclude Include="Goals\AdventureSpellCast.h">
       <Filter>Goals</Filter>
     </ClInclude>
+    <ClInclude Include="Pathfinding\Actions\ISpecialAction.h">
+      <Filter>Pathfinding\Actions</Filter>
+    </ClInclude>
+    <ClInclude Include="Pathfinding\Actions\TownPortalAction.h" />
+    <ClInclude Include="Pathfinding\Actions\BattleAction.h" />
+    <ClInclude Include="Pathfinding\Actions\BoatActions.h" />
+    <ClInclude Include="Pathfinding\Rules\AILayerTransitionRule.h" />
+    <ClInclude Include="Pathfinding\Rules\AIMovementAfterDestinationRule.h" />
+    <ClInclude Include="Pathfinding\Rules\AIMovementToDestinationRule.h" />
+    <ClInclude Include="Pathfinding\Rules\AIPreviousNodeRule.h" />
   </ItemGroup>
   <ItemGroup>
     <Filter Include="Pathfinding">
@@ -197,5 +216,11 @@
     <Filter Include="Goals">
       <UniqueIdentifier>{f97140a0-eee3-456f-b586-4b13265c01da}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Pathfinding\Rules">
+      <UniqueIdentifier>{beabfdb9-2e76-4daa-8d1a-81086387f319}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Pathfinding\Actions">
+      <UniqueIdentifier>{3ebb4852-a986-447a-b5cc-20992df76f0c}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
 </Project>