| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 | 
							- /*
 
- * AIPathfinderConfig.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 "AIPathfinderConfig.h"
 
- #include "../Goals/Goals.h"
 
- #include "../../../CCallback.h"
 
- #include "../../../lib/mapping/CMap.h"
 
- #include "../../../lib/mapObjects/MapObjects.h"
 
- namespace AIPathfinding
 
- {
 
- 	class BuildBoatAction : public ISpecialAction
 
- 	{
 
- 	private:
 
- 		const IShipyard * shipyard;
 
- 	public:
 
- 		BuildBoatAction(const IShipyard * shipyard)
 
- 			:shipyard(shipyard)
 
- 		{
 
- 		}
 
- 		virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
 
- 		{
 
- 			return sptr(Goals::BuildBoat(shipyard));
 
- 		}
 
- 	};
 
- 	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;
 
- 	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
 
- 				&& vstd::contains(virtualBoats, destination.coord))
 
- 			{
 
- 				logAi->trace("Bypassing virtual boat at %s!", destination.coord.toString());
 
- 				nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
 
- 				{
 
- 					std::shared_ptr<const BuildBoatAction> 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<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());
 
- 				}
 
- 			}
 
- 		}
 
- 	};
 
- 	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;
 
- 				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<BattleAction>(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<AINodeStorage> nodeStorage;
 
- 	public:
 
- 		AIMovementToDestinationRule(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
 
- 		{
 
- 			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;
 
- 			}
 
- 			destination.blocked = true;
 
- 		}
 
- 	};
 
- 	class AIPreviousNodeRule : public MovementToDestinationRule
 
- 	{
 
- 	private:
 
- 		CPlayerSpecificInfoCallback * cb;
 
- 		std::shared_ptr<AINodeStorage> nodeStorage;
 
- 	public:
 
- 		AIPreviousNodeRule(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(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<std::shared_ptr<IPathfindingRule>> makeRuleset(
 
- 		CPlayerSpecificInfoCallback * cb,
 
- 		VCAI * ai,
 
- 		std::shared_ptr<AINodeStorage> nodeStorage)
 
- 	{
 
- 		std::vector<std::shared_ptr<IPathfindingRule>> rules = {
 
- 			std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage),
 
- 			std::make_shared<DestinationActionRule>(),
 
- 			std::make_shared<AIMovementToDestinationRule>(cb, nodeStorage),
 
- 			std::make_shared<MovementCostRule>(),
 
- 			std::make_shared<AIPreviousNodeRule>(cb, nodeStorage),
 
- 			std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage)
 
- 		};
 
- 		return rules;
 
- 	}
 
- 	AIPathfinderConfig::AIPathfinderConfig(
 
- 		CPlayerSpecificInfoCallback * cb,
 
- 		VCAI * ai,
 
- 		std::shared_ptr<AINodeStorage> nodeStorage)
 
- 		:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage))
 
- 	{
 
- 	}
 
- }
 
 
  |