| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 | /** 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"#include "../../Engine/Nullkiller.h"#include "../../../../lib/pathfinder/CPathfinder.h"#include "../../../../lib/pathfinder/TurnInfo.h"namespace NKAI{namespace AIPathfinding{	AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * 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)		{			if(source.node->layer == EPathfindingLayer::LAND				&& (destination.node->layer == EPathfindingLayer::AIR || destination.node->layer == EPathfindingLayer::WATER))			{				if(pathfinderHelper->getTurnInfo()->isLayerAvailable(destination.node->layer))					return;				else					destination.blocked = true;			}			else			{				return;			}		}		if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)		{			std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);			if(virtualBoat && tryUseSpecialAction(destination, source, virtualBoat, EPathNodeAction::EMBARK))			{#if NKAI_PATHFINDER_TRACE_LEVEL >= 1				logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());#endif			}		}		if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::WATER)		{			if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::WATER_WALK_CAST)			{				destination.blocked = false;				return;			}			auto action = waterWalkingActions.find(nodeStorage->getHero(source.node));			if(action != waterWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))			{#if NKAI_PATHFINDER_TRACE_LEVEL >= 2				logAi->trace("Casting water walk while moving %s -> %s!", source.coord.toString(), destination.coord.toString());#endif			}		}		if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::AIR)		{			if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::FLY_CAST)			{				destination.blocked = false;				return;			}			auto action = airWalkingActions.find(nodeStorage->getHero(source.node));			if(action != airWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))			{#if NKAI_PATHFINDER_TRACE_LEVEL >= 2				logAi->trace("Casting fly while moving %s -> %s!", source.coord.toString(), destination.coord.toString());#endif			}		}	}	void AILayerTransitionRule::setup()	{		SpellID waterWalk = SpellID::WATER_WALK;		SpellID airWalk = SpellID::FLY;		for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())		{			if(hero->canCastThisSpell(waterWalk.toSpell()) && hero->mana >= hero->getSpellCost(waterWalk.toSpell()))			{				waterWalkingActions[hero] = std::make_shared<WaterWalkingAction>(hero);			}			if(hero->canCastThisSpell(airWalk.toSpell()) && hero->mana >= hero->getSpellCost(airWalk.toSpell()))			{				airWalkingActions[hero] = std::make_shared<AirWalkingAction>(hero);			}		}		collectVirtualBoats();	}	void AILayerTransitionRule::collectVirtualBoats()	{		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->memory->visitableObjs)		{			if(obj->ID != Obj::TOWN) //towns were handled in the previous loop			{				if(const auto * shipyard = dynamic_cast<const IShipyard *>(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>(cb, shipyard);				logAi->debug("Virtual boat added at %s", boatLocation.toString());			}		}		for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())		{			auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();			if(hero->canCastThisSpell(summonBoatSpell)				&& hero->getSpellSchoolLevel(summonBoatSpell) >= MasteryLevel::ADVANCED)			{				// TODO: For lower school level we might need to check the existance of some boat				summonableVirtualBoats[hero] = std::make_shared<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		{			const CGHeroInstance * hero = nodeStorage->getHero(source.node);			if(vstd::contains(summonableVirtualBoats, hero)				&& summonableVirtualBoats.at(hero)->canAct(nodeStorage->getAINode(source.node)))			{				virtualBoat = summonableVirtualBoats.at(hero);			}		}		return virtualBoat;	}	bool AILayerTransitionRule::tryUseSpecialAction(		CDestinationNodeInfo & destination,		const PathNodeInfo & source,		std::shared_ptr<const SpecialAction> specialAction,		EPathNodeAction targetAction) const	{		bool result = false;		nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)			{				auto castNodeOptional = nodeStorage->getOrCreateNode(					node->coord,					node->layer,					specialAction->getActor(node->actor));				if(castNodeOptional)				{					AIPathNode * castNode = castNodeOptional.value();					if(castNode->action == EPathNodeAction::UNKNOWN)					{						castNode->addSpecialAction(specialAction);						destination.blocked = false;						destination.action = targetAction;						destination.node = castNode;						result = true;					}					else					{#if NKAI_PATHFINDER_TRACE_LEVEL >= 1						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;	}}}
 |