mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	NKAI: water and air walking
This commit is contained in:
		| @@ -72,7 +72,13 @@ void DangerHitMapAnalyzer::updateHitMap() | ||||
| 		if(ai->cb->getPlayerRelations(ai->playerID, pair.first) != PlayerRelations::ENEMIES) | ||||
| 			continue; | ||||
|  | ||||
| 		ai->pathfinder->updatePaths(pair.second, PathfinderSettings()); | ||||
| 		PathfinderSettings ps; | ||||
|  | ||||
| 		ps.mainTurnDistanceLimit = 10; | ||||
| 		ps.scoutTurnDistanceLimit = 10; | ||||
| 		ps.useHeroChain = false; | ||||
|  | ||||
| 		ai->pathfinder->updatePaths(pair.second, ps); | ||||
|  | ||||
| 		boost::this_thread::interruption_point(); | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,7 @@ set(Nullkiller_SRCS | ||||
| 		Pathfinding/Actions/BuyArmyAction.cpp | ||||
| 		Pathfinding/Actions/BoatActions.cpp | ||||
| 		Pathfinding/Actions/TownPortalAction.cpp | ||||
| 		Pathfinding/Actions/AdventureSpellCastMovementActions.cpp | ||||
| 		Pathfinding/Rules/AILayerTransitionRule.cpp | ||||
| 		Pathfinding/Rules/AIMovementAfterDestinationRule.cpp | ||||
| 		Pathfinding/Rules/AIMovementToDestinationRule.cpp | ||||
| @@ -69,6 +70,7 @@ set(Nullkiller_HEADERS | ||||
| 		Pathfinding/Actions/BuyArmyAction.h | ||||
| 		Pathfinding/Actions/BoatActions.h | ||||
| 		Pathfinding/Actions/TownPortalAction.h | ||||
| 		Pathfinding/Actions/AdventureSpellCastMovementActions.h | ||||
| 		Pathfinding/Rules/AILayerTransitionRule.h | ||||
| 		Pathfinding/Rules/AIMovementAfterDestinationRule.h | ||||
| 		Pathfinding/Rules/AIMovementToDestinationRule.h | ||||
|   | ||||
| @@ -45,7 +45,7 @@ struct AIPathNode : public CGPathNode | ||||
| { | ||||
| 	uint64_t danger; | ||||
| 	uint64_t armyLoss; | ||||
| 	uint32_t manaCost; | ||||
| 	int32_t manaCost; | ||||
| 	const AIPathNode * chainOther; | ||||
| 	std::shared_ptr<const SpecialAction> specialAction; | ||||
| 	const ChainActor * actor; | ||||
|   | ||||
| @@ -44,6 +44,7 @@ namespace AIPathfinding | ||||
| 		std::shared_ptr<AINodeStorage> nodeStorage) | ||||
| 		:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)), aiNodeStorage(nodeStorage) | ||||
| 	{ | ||||
| 		options.canUseCast = true; | ||||
| 	} | ||||
|  | ||||
| 	AIPathfinderConfig::~AIPathfinderConfig() = default; | ||||
|   | ||||
| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
| * AdventureSpellCastMovementActions.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 "../../AIGateway.h" | ||||
| #include "../../Goals/AdventureSpellCast.h" | ||||
| #include "../../Goals/CaptureObject.h" | ||||
| #include "../../Goals/Invalid.h" | ||||
| #include "../../Goals/BuildBoat.h" | ||||
| #include "../../../../lib/mapObjects/MapObjects.h" | ||||
| #include "AdventureSpellCastMovementActions.h" | ||||
|  | ||||
| namespace NKAI | ||||
| { | ||||
|  | ||||
| namespace AIPathfinding | ||||
| { | ||||
| 	AdventureCastAction::AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero) | ||||
| 		:spellToCast(spellToCast), hero(hero) | ||||
| 	{ | ||||
| 		manaCost = hero->getSpellCost(spellToCast.toSpell()); | ||||
| 	} | ||||
|  | ||||
| 	WaterWalkingAction::WaterWalkingAction(const CGHeroInstance * hero) | ||||
| 		:AdventureCastAction(SpellID::WATER_WALK, hero) | ||||
| 	{ } | ||||
|  | ||||
| 	AirWalkingAction::AirWalkingAction(const CGHeroInstance * hero) | ||||
| 		: AdventureCastAction(SpellID::FLY, hero) | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	void AdventureCastAction::applyOnDestination( | ||||
| 		const CGHeroInstance * hero, | ||||
| 		CDestinationNodeInfo & destination, | ||||
| 		const PathNodeInfo & source, | ||||
| 		AIPathNode * dstMode, | ||||
| 		const AIPathNode * srcNode) const | ||||
| 	{ | ||||
| 		dstMode->manaCost = srcNode->manaCost + manaCost; | ||||
| 		dstMode->theNodeBefore = source.node; | ||||
| 	} | ||||
|  | ||||
| 	void AdventureCastAction::execute(const CGHeroInstance * hero) const | ||||
| 	{ | ||||
| 		assert(hero == this->hero); | ||||
|  | ||||
| 		Goals::AdventureSpellCast(hero, spellToCast).accept(ai); | ||||
| 	} | ||||
|  | ||||
| 	bool AdventureCastAction::canAct(const AIPathNode * source) const | ||||
| 	{ | ||||
| 		assert(hero == this->hero); | ||||
|  | ||||
| 		auto hero = source->actor->hero; | ||||
|  | ||||
| #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 + manaCost; | ||||
| 	} | ||||
|  | ||||
| 	std::string AdventureCastAction::toString() const | ||||
| 	{ | ||||
| 		return "Cast " + spellToCast.toSpell()->getNameTranslated() + " by " + hero->getNameTranslated(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
| * AdventureSpellCastMovementActions.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 "SpecialAction.h" | ||||
| #include "../../../../lib/mapObjects/MapObjects.h" | ||||
|  | ||||
| namespace NKAI | ||||
| { | ||||
|  | ||||
| namespace AIPathfinding | ||||
| { | ||||
| 	class AdventureCastAction : public SpecialAction | ||||
| 	{ | ||||
| 	private: | ||||
| 		SpellID spellToCast; | ||||
| 		const CGHeroInstance * hero; | ||||
| 		int manaCost; | ||||
|  | ||||
| 	public: | ||||
| 		AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero); | ||||
|  | ||||
| 		virtual void execute(const CGHeroInstance * hero) const override; | ||||
|  | ||||
| 		virtual void applyOnDestination( | ||||
| 			const CGHeroInstance * hero, | ||||
| 			CDestinationNodeInfo & destination, | ||||
| 			const PathNodeInfo & source, | ||||
| 			AIPathNode * dstMode, | ||||
| 			const AIPathNode * srcNode) const override; | ||||
|  | ||||
| 		virtual bool canAct(const AIPathNode * source) const override; | ||||
|  | ||||
| 		virtual std::string toString() const override; | ||||
| 	}; | ||||
|  | ||||
| 	class WaterWalkingAction : public AdventureCastAction | ||||
| 	{ | ||||
| 	public: | ||||
| 		WaterWalkingAction(const CGHeroInstance * hero); | ||||
| 	}; | ||||
|  | ||||
| 	class AirWalkingAction : public AdventureCastAction | ||||
| 	{ | ||||
| 	public: | ||||
| 		AirWalkingAction(const CGHeroInstance * hero); | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| } | ||||
| @@ -114,7 +114,7 @@ namespace AIPathfinding | ||||
| 			source->manaCost); | ||||
| #endif | ||||
|  | ||||
| 		return hero->mana >= (si32)(source->manaCost + getManaCost(hero)); | ||||
| 		return hero->mana >= source->manaCost + getManaCost(hero); | ||||
| 	} | ||||
|  | ||||
| 	std::string SummonBoatAction::toString() const | ||||
| @@ -122,7 +122,7 @@ namespace AIPathfinding | ||||
| 		return "Summon Boat"; | ||||
| 	} | ||||
|  | ||||
| 	uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const | ||||
| 	int32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const | ||||
| 	{ | ||||
| 		SpellID summonBoat = SpellID::SUMMON_BOAT; | ||||
|  | ||||
|   | ||||
| @@ -20,8 +20,6 @@ namespace AIPathfinding | ||||
| { | ||||
| 	class VirtualBoatAction : public SpecialAction | ||||
| 	{ | ||||
| 	public: | ||||
| 		virtual const ChainActor * getActor(const ChainActor * sourceActor) const = 0; | ||||
| 	}; | ||||
| 	 | ||||
| 	class SummonBoatAction : public VirtualBoatAction | ||||
| @@ -43,7 +41,7 @@ namespace AIPathfinding | ||||
| 		virtual std::string toString() const override; | ||||
|  | ||||
| 	private: | ||||
| 		uint32_t getManaCost(const CGHeroInstance * hero) const; | ||||
| 		int32_t getManaCost(const CGHeroInstance * hero) const; | ||||
| 	}; | ||||
|  | ||||
| 	class BuildBoatAction : public VirtualBoatAction | ||||
|   | ||||
| @@ -22,6 +22,7 @@ namespace NKAI | ||||
| { | ||||
|  | ||||
| struct AIPathNode; | ||||
| class ChainActor; | ||||
|  | ||||
| class SpecialAction | ||||
| { | ||||
| @@ -54,6 +55,11 @@ public: | ||||
| 	{ | ||||
| 		return {}; | ||||
| 	} | ||||
|  | ||||
| 	virtual const ChainActor * getActor(const ChainActor * sourceActor) const | ||||
| 	{ | ||||
| 		return sourceActor; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class CompositeAction : public SpecialAction | ||||
|   | ||||
| @@ -10,6 +10,8 @@ | ||||
| #include "StdInc.h" | ||||
| #include "AILayerTransitionRule.h" | ||||
| #include "../../Engine/Nullkiller.h" | ||||
| #include "../../../../lib/pathfinder/CPathfinder.h" | ||||
| #include "../../../../lib/pathfinder/TurnInfo.h" | ||||
|  | ||||
| namespace NKAI | ||||
| { | ||||
| @@ -31,23 +33,79 @@ namespace AIPathfinding | ||||
|  | ||||
| 		if(!destination.blocked) | ||||
| 		{ | ||||
| 			return; | ||||
| 			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 && tryEmbarkVirtualBoat(destination, source, virtualBoat)) | ||||
| 			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) | ||||
| 		{ | ||||
| 			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) | ||||
| 		{ | ||||
| 			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())) | ||||
| 			{ | ||||
| 				waterWalkingActions[hero] = std::make_shared<WaterWalkingAction>(hero); | ||||
| 			} | ||||
|  | ||||
| 			if(hero->canCastThisSpell(airWalk.toSpell())) | ||||
| 			{ | ||||
| 				airWalkingActions[hero] = std::make_shared<AirWalkingAction>(hero); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		collectVirtualBoats(); | ||||
| 	} | ||||
|  | ||||
| 	void AILayerTransitionRule::collectVirtualBoats() | ||||
| 	{ | ||||
| 		std::vector<const IShipyard *> shipyards; | ||||
|  | ||||
| @@ -113,50 +171,56 @@ namespace AIPathfinding | ||||
| 		return virtualBoat; | ||||
| 	} | ||||
|  | ||||
| 	bool AILayerTransitionRule::tryEmbarkVirtualBoat( | ||||
| 	bool AILayerTransitionRule::tryUseSpecialAction( | ||||
| 		CDestinationNodeInfo & destination, | ||||
| 		const PathNodeInfo & source, | ||||
| 		std::shared_ptr<const VirtualBoatAction> virtualBoat) const | ||||
| 		std::shared_ptr<const SpecialAction> specialAction, | ||||
| 		EPathNodeAction targetAction) const | ||||
| 	{ | ||||
| 		bool result = false; | ||||
|  | ||||
| 		nodeStorage->updateAINode(destination.node, [&](AIPathNode * node) | ||||
| 		if(!specialAction->canAct(nodeStorage->getAINode(source.node))) | ||||
| 		{ | ||||
| 			auto boatNodeOptional = nodeStorage->getOrCreateNode( | ||||
| 				node->coord, | ||||
| 				node->layer, | ||||
| 				virtualBoat->getActor(node->actor)); | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 			if(boatNodeOptional) | ||||
| 		nodeStorage->updateAINode(destination.node, [&](AIPathNode * node) | ||||
| 			{ | ||||
| 				AIPathNode * boatNode = boatNodeOptional.value(); | ||||
| 				auto castNodeOptional = nodeStorage->getOrCreateNode( | ||||
| 					node->coord, | ||||
| 					node->layer, | ||||
| 					specialAction->getActor(node->actor)); | ||||
|  | ||||
| 				if(boatNode->action == EPathNodeAction::UNKNOWN) | ||||
| 				if(castNodeOptional) | ||||
| 				{ | ||||
| 					boatNode->addSpecialAction(virtualBoat); | ||||
| 					destination.blocked = false; | ||||
| 					destination.action = EPathNodeAction::EMBARK; | ||||
| 					destination.node = boatNode; | ||||
| 					result = true; | ||||
| 					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 | ||||
| 				{ | ||||
| #if NKAI_PATHFINDER_TRACE_LEVEL >= 1 | ||||
| 					logAi->trace( | ||||
| 						"Special transition node already allocated. Blocked moving %s -> %s", | ||||
| 					logAi->debug( | ||||
| 						"Can not allocate special transition node while 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; | ||||
| 	} | ||||
|   | ||||
| @@ -13,6 +13,7 @@ | ||||
| #include "../AINodeStorage.h" | ||||
| #include "../../AIGateway.h" | ||||
| #include "../Actions/BoatActions.h" | ||||
| #include "../Actions/AdventureSpellCastMovementActions.h" | ||||
| #include "../../../../CCallback.h" | ||||
| #include "../../../../lib/mapObjects/MapObjects.h" | ||||
| #include "../../../../lib/pathfinder/PathfindingRules.h" | ||||
| @@ -29,6 +30,8 @@ namespace AIPathfinding | ||||
| 		std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats; | ||||
| 		std::shared_ptr<AINodeStorage> nodeStorage; | ||||
| 		std::map<const CGHeroInstance *, std::shared_ptr<const SummonBoatAction>> summonableVirtualBoats; | ||||
| 		std::map<const CGHeroInstance *, std::shared_ptr<const WaterWalkingAction>> waterWalkingActions; | ||||
| 		std::map<const CGHeroInstance *, std::shared_ptr<const AirWalkingAction>> airWalkingActions; | ||||
|  | ||||
| 	public: | ||||
| 		AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage); | ||||
| @@ -41,15 +44,17 @@ namespace AIPathfinding | ||||
|  | ||||
| 	private: | ||||
| 		void setup(); | ||||
| 		void collectVirtualBoats(); | ||||
|  | ||||
| 		std::shared_ptr<const VirtualBoatAction> findVirtualBoat( | ||||
| 			CDestinationNodeInfo & destination, | ||||
| 			const PathNodeInfo & source) const; | ||||
|  | ||||
| 		bool tryEmbarkVirtualBoat( | ||||
| 		bool tryUseSpecialAction( | ||||
| 			CDestinationNodeInfo & destination, | ||||
| 			const PathNodeInfo & source, | ||||
| 			std::shared_ptr<const VirtualBoatAction> virtualBoat) const; | ||||
| 			std::shared_ptr<const SpecialAction> specialAction, | ||||
| 			EPathNodeAction targetAction) const; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| #include "../TerrainHandler.h" | ||||
| #include "../mapObjects/CGHeroInstance.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "spells/CSpellHandler.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| @@ -472,6 +473,12 @@ CPathfinderHelper::CPathfinderHelper(CGameState * gs, const CGHeroInstance * Her | ||||
| 	turnsInfo.reserve(16); | ||||
| 	updateTurnInfo(); | ||||
| 	initializePatrol(); | ||||
|  | ||||
| 	SpellID flySpell = SpellID::FLY; | ||||
| 	canCastFly = Hero->canCastThisSpell(flySpell.toSpell()); | ||||
|  | ||||
| 	SpellID waterWalk = SpellID::WATER_WALK; | ||||
| 	canCastWaterWalk = Hero->canCastThisSpell(waterWalk.toSpell()); | ||||
| } | ||||
|  | ||||
| CPathfinderHelper::~CPathfinderHelper() | ||||
| @@ -501,12 +508,18 @@ bool CPathfinderHelper::isLayerAvailable(const EPathfindingLayer & layer) const | ||||
| 		if(!options.useFlying) | ||||
| 			return false; | ||||
|  | ||||
| 		if(canCastFly && options.canUseCast) | ||||
| 			return true; | ||||
|  | ||||
| 		break; | ||||
|  | ||||
| 	case EPathfindingLayer::WATER: | ||||
| 		if(!options.useWaterWalking) | ||||
| 			return false; | ||||
|  | ||||
| 		if(canCastWaterWalk && options.canUseCast) | ||||
| 			return true; | ||||
|  | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -72,6 +72,8 @@ public: | ||||
| 	const CGHeroInstance * hero; | ||||
| 	std::vector<TurnInfo *> turnsInfo; | ||||
| 	const PathfinderOptions & options; | ||||
| 	bool canCastFly; | ||||
| 	bool canCastWaterWalk; | ||||
|  | ||||
| 	CPathfinderHelper(CGameState * gs, const CGHeroInstance * Hero, const PathfinderOptions & Options); | ||||
| 	virtual ~CPathfinderHelper(); | ||||
|   | ||||
| @@ -31,6 +31,7 @@ PathfinderOptions::PathfinderOptions() | ||||
| 	, oneTurnSpecialLayersLimit(true) | ||||
| 	, originalMovementRules(false) | ||||
| 	, turnLimit(std::numeric_limits<uint8_t>::max()) | ||||
| 	, canUseCast(false) | ||||
| { | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -71,6 +71,11 @@ struct DLL_LINKAGE PathfinderOptions | ||||
| 	/// Max number of turns to compute. Default = infinite | ||||
| 	uint8_t turnLimit; | ||||
|  | ||||
| 	/// <summary> | ||||
| 	/// For AI. Allows water walk and fly layers if hero can cast appropriate spells | ||||
| 	/// </summary> | ||||
| 	bool canUseCast; | ||||
|  | ||||
| 	PathfinderOptions(); | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user