| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2018-12-01 10:30:37 +02:00
										 |  |  | * AINodeStorage.cpp, part of VCMI engine | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | * | 
					
						
							|  |  |  | * 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 "AINodeStorage.h"
 | 
					
						
							| 
									
										
										
										
											2019-02-06 14:51:12 +02:00
										 |  |  | #include "Actions/TownPortalAction.h"
 | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | #include "../Goals/Goals.h"
 | 
					
						
							|  |  |  | #include "../../../CCallback.h"
 | 
					
						
							|  |  |  | #include "../../../lib/mapping/CMap.h"
 | 
					
						
							|  |  |  | #include "../../../lib/mapObjects/MapObjects.h"
 | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | #include "../../../lib/PathfinderUtil.h"
 | 
					
						
							|  |  |  | #include "../../../lib/CPlayerState.h"
 | 
					
						
							| 
									
										
										
										
											2019-02-06 14:51:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | extern boost::thread_specific_ptr<CCallback> cb; | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | AINodeStorage::AINodeStorage(const int3 & Sizes) | 
					
						
							|  |  |  | 	: sizes(Sizes) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | AINodeStorage::~AINodeStorage() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | 	//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int3 pos; | 
					
						
							|  |  |  | 	const int3 sizes = gs->getMapSize(); | 
					
						
							|  |  |  | 	const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap; | 
					
						
							|  |  |  | 	const PlayerColor player = hero->tempOwner; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
 | 
					
						
							|  |  |  | 	const bool useFlying = options.useFlying; | 
					
						
							|  |  |  | 	const bool useWaterWalking = options.useWaterWalking; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for(pos.x=0; pos.x < sizes.x; ++pos.x) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		for(pos.y=0; pos.y < sizes.y; ++pos.y) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			for(pos.z=0; pos.z < sizes.z; ++pos.z) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				const TerrainTile * tile = &gs->map->getTile(pos); | 
					
						
							|  |  |  | 				switch(tile->terType) | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 				case ETerrainType::ROCK: | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				case ETerrainType::WATER: | 
					
						
							|  |  |  | 					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs)); | 
					
						
							|  |  |  | 					if(useFlying) | 
					
						
							|  |  |  | 						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs)); | 
					
						
							|  |  |  | 					if(useWaterWalking) | 
					
						
							|  |  |  | 						resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs)); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs)); | 
					
						
							|  |  |  | 					if(useFlying) | 
					
						
							|  |  |  | 						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs)); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	return static_cast<const AIPathNode *>(node); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | void AINodeStorage::updateAINode(CGPathNode * node, std::function<void(AIPathNode *)> updater) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	auto aiNode = static_cast<AIPathNode *>(node); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	updater(aiNode); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | bool AINodeStorage::isBattleNode(const CGPathNode * node) const | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-10-10 16:07:28 +03:00
										 |  |  | 	return (getAINode(node)->chainMask & BATTLE_CHAIN) > 0; | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 	auto chains = nodes[pos.x][pos.y][pos.z][layer]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for(AIPathNode & node : chains) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if(node.chainMask == chainNumber) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			return &node; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(node.chainMask == 0) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			node.chainMask = chainNumber; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return &node; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return boost::none; | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CGPathNode * AINodeStorage::getInitialNode() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	auto hpos = hero->getPosition(false); | 
					
						
							| 
									
										
										
										
											2019-01-08 00:40:09 +03:00
										 |  |  | 	auto initialNode = | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 		getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN) | 
					
						
							|  |  |  | 		.get(); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	initialNode->turns = 0; | 
					
						
							|  |  |  | 	initialNode->moveRemains = hero->movement; | 
					
						
							|  |  |  | 	initialNode->danger = 0; | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | 	initialNode->cost = 0.0; | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return initialNode; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	for(int i = 0; i < NUM_CHAINS; i++) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		AIPathNode & heroNode = nodes[coord.x][coord.y][coord.z][layer][i]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 		heroNode.chainMask = 0; | 
					
						
							|  |  |  | 		heroNode.danger = 0; | 
					
						
							| 
									
										
										
										
											2018-12-09 15:20:46 +02:00
										 |  |  | 		heroNode.manaCost = 0; | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 		heroNode.specialAction.reset(); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 		heroNode.update(coord, layer, accessibility); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	const AIPathNode * srcNode = getAINode(source.node); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 06:00:00 +03:00
										 |  |  | 	updateAINode(destination.node, [&](AIPathNode * dstNode) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 		dstNode->moveRemains = destination.movementLeft; | 
					
						
							|  |  |  | 		dstNode->turns = destination.turn; | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | 		dstNode->cost = destination.cost; | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 		dstNode->danger = srcNode->danger; | 
					
						
							|  |  |  | 		dstNode->action = destination.action; | 
					
						
							| 
									
										
										
										
											2018-10-10 16:07:28 +03:00
										 |  |  | 		dstNode->theNodeBefore = srcNode->theNodeBefore; | 
					
						
							| 
									
										
										
										
											2018-12-09 15:20:46 +02:00
										 |  |  | 		dstNode->manaCost = srcNode->manaCost; | 
					
						
							| 
									
										
										
										
											2019-01-08 00:40:09 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-09 15:20:46 +02:00
										 |  |  | 		if(dstNode->specialAction) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			dstNode->specialAction->applyOnDestination(getHero(), destination, source, dstNode, srcNode); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<CGPathNode *> AINodeStorage::calculateNeighbours( | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	const PathNodeInfo & source, | 
					
						
							|  |  |  | 	const PathfinderConfig * pathfinderConfig, | 
					
						
							|  |  |  | 	const CPathfinderHelper * pathfinderHelper) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	std::vector<CGPathNode *> neighbours; | 
					
						
							| 
									
										
										
										
											2018-12-16 15:46:48 +01:00
										 |  |  | 	neighbours.reserve(16); | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	const AIPathNode * srcNode = getAINode(source.node); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 	auto accessibleNeighbourTiles = pathfinderHelper->getNeighbourTiles(source); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for(auto & neighbour : accessibleNeighbourTiles) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1)) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 			auto nextNode = getOrCreateNode(neighbour, i, srcNode->chainMask); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 			if(!nextNode || nextNode.get()->accessible == CGPathNode::NOT_SET) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 			neighbours.push_back(nextNode.get()); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return neighbours; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | void AINodeStorage::setHero(HeroPtr heroPtr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	hero = heroPtr.get(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | std::vector<CGPathNode *> AINodeStorage::calculateTeleportations( | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	const PathNodeInfo & source, | 
					
						
							|  |  |  | 	const PathfinderConfig * pathfinderConfig, | 
					
						
							|  |  |  | 	const CPathfinderHelper * pathfinderHelper) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	std::vector<CGPathNode *> neighbours; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | 	if(source.isNodeObjectVisitable()) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2019-01-14 13:20:46 +02:00
										 |  |  | 		auto accessibleExits = pathfinderHelper->getTeleportExits(source); | 
					
						
							|  |  |  | 		auto srcNode = getAINode(source.node); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:20:46 +02:00
										 |  |  | 		for(auto & neighbour : accessibleExits) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask); | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:20:46 +02:00
										 |  |  | 			if(!node) | 
					
						
							|  |  |  | 				continue; | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:20:46 +02:00
										 |  |  | 			neighbours.push_back(node.get()); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(hero->getPosition(false) == source.coord) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		calculateTownPortalTeleportations(source, neighbours); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return neighbours; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | void AINodeStorage::calculateTownPortalTeleportations( | 
					
						
							|  |  |  | 	const PathNodeInfo & source, | 
					
						
							|  |  |  | 	std::vector<CGPathNode *> & neighbours) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	SpellID spellID = SpellID::TOWN_PORTAL; | 
					
						
							|  |  |  | 	const CSpell * townPortal = spellID.toSpell(); | 
					
						
							|  |  |  | 	auto srcNode = getAINode(source.node); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(hero->canCastThisSpell(townPortal) && hero->mana >= hero->getSpellCost(townPortal)) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto towns = cb->getTownsInfo(false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			return cb->getPlayerRelations(hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(!towns.size()) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// TODO: Copy/Paste from TownPortalMechanics
 | 
					
						
							|  |  |  | 		auto skillLevel = hero->getSpellSchoolLevel(townPortal); | 
					
						
							|  |  |  | 		auto movementCost = GameConstants::BASE_MOVEMENT_COST * (skillLevel >= 3 ? 2 : 3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(hero->movement < movementCost) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(skillLevel < SecSkillLevel::ADVANCED) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			const CGTownInstance * nearestTown = *vstd::minElementByFun(towns, [&](const CGTownInstance * t) -> int | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				return hero->visitablePos().dist2dSQ(t->visitablePos()); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			towns = std::vector<const CGTownInstance *>{ nearestTown }; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for(const CGTownInstance * targetTown : towns) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if(targetTown->visitingHero) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(nodeOptional) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | #ifdef VCMI_TRACE_PATHFINDER
 | 
					
						
							|  |  |  | 				logAi->trace("Adding town portal node at %s", targetTown->name); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				AIPathNode * node = nodeOptional.get(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				node->theNodeBefore = source.node; | 
					
						
							| 
									
										
										
										
											2019-02-06 14:51:12 +02:00
										 |  |  | 				node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown)); | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | 				node->moveRemains = source.node->moveRemains; | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				neighbours.push_back(node); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	auto pos = destination.coord; | 
					
						
							|  |  |  | 	auto chains = nodes[pos.x][pos.y][pos.z][EPathfindingLayer::LAND]; | 
					
						
							| 
									
										
										
										
											2018-10-07 14:51:27 +03:00
										 |  |  | 	auto destinationNode = getAINode(destination.node); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for(const AIPathNode & node : chains) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto sameNode = node.chainMask == destinationNode->chainMask; | 
					
						
							|  |  |  | 		if(sameNode	|| node.action == CGPathNode::ENodeAction::UNKNOWN) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | 			if(node.cost < destinationNode->cost) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | #ifdef VCMI_TRACE_PATHFINDER
 | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 				logAi->trace( | 
					
						
							|  |  |  | 					"Block ineficient move %s:->%s, mask=%i, mp diff: %i", | 
					
						
							|  |  |  | 					source.coord.toString(), | 
					
						
							| 
									
										
										
										
											2018-10-10 16:07:28 +03:00
										 |  |  | 					destination.coord.toString(), | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 					destinationNode->chainMask, | 
					
						
							|  |  |  | 					node.moveRemains - destinationNode->moveRemains); | 
					
						
							| 
									
										
										
										
											2019-01-07 23:33:31 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 				return true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:20:46 +02:00
										 |  |  | bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const | 
					
						
							| 
									
										
										
										
											2018-12-29 15:55:20 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-14 13:20:46 +02:00
										 |  |  | 	const AIPathNode & node = nodes[pos.x][pos.y][pos.z][layer][0]; | 
					
						
							| 
									
										
										
										
											2018-12-29 15:55:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:20:46 +02:00
										 |  |  | 	return node.action != CGPathNode::ENodeAction::UNKNOWN; | 
					
						
							| 
									
										
										
										
											2018-12-29 15:55:20 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-28 15:03:40 +02:00
										 |  |  | std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	std::vector<AIPath> paths; | 
					
						
							| 
									
										
										
										
											2018-10-28 15:03:40 +02:00
										 |  |  | 	auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL]; | 
					
						
							| 
									
										
										
										
											2018-12-09 15:20:46 +02:00
										 |  |  | 	auto initialPos = hero->visitablePos(); | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for(const AIPathNode & node : chains) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if(node.action == CGPathNode::ENodeAction::UNKNOWN) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		AIPath path; | 
					
						
							|  |  |  | 		const AIPathNode * current = &node; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-09 15:20:46 +02:00
										 |  |  | 		while(current != nullptr && current->coord != initialPos) | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			AIPathNodeInfo pathNode; | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | 			pathNode.cost = current->cost; | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 			pathNode.turns = current->turns; | 
					
						
							|  |  |  | 			pathNode.danger = current->danger; | 
					
						
							|  |  |  | 			pathNode.coord = current->coord; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			path.nodes.push_back(pathNode); | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 			path.specialAction = current->specialAction; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 			current = getAINode(current->theNodeBefore); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-09 22:31:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 		paths.push_back(path); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return paths; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AIPath::AIPath() | 
					
						
							|  |  |  | 	: nodes({}) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int3 AIPath::firstTileToGet() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if(nodes.size()) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		return nodes.back().coord; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return int3(-1, -1, -1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint64_t AIPath::getPathDanger() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if(nodes.size()) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		return nodes.front().danger; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | float AIPath::movementCost() const | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	if(nodes.size()) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | 		return nodes.front().cost; | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// TODO: boost:optional?
 | 
					
						
							| 
									
										
										
										
											2019-01-15 08:52:55 +03:00
										 |  |  | 	return 0.0; | 
					
						
							| 
									
										
										
										
											2018-08-12 14:31:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint64_t AIPath::getTotalDanger(HeroPtr hero) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint64_t pathDanger = getPathDanger(); | 
					
						
							|  |  |  | 	uint64_t objDanger = evaluateDanger(nodes.front().coord, hero.get()); // bank danger is not checked by pathfinder
 | 
					
						
							|  |  |  | 	uint64_t danger = pathDanger > objDanger ? pathDanger : objDanger; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return danger; | 
					
						
							| 
									
										
										
										
											2018-10-10 16:07:28 +03:00
										 |  |  | } |