CPathfinder.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399
  1. /*
  2. * CPathfinder.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CPathfinder.h"
  12. #include "CHeroHandler.h"
  13. #include "mapping/CMap.h"
  14. #include "CGameState.h"
  15. #include "mapObjects/CGHeroInstance.h"
  16. #include "GameConstants.h"
  17. #include "CStopWatch.h"
  18. #include "CConfigHandler.h"
  19. #include "CPlayerState.h"
  20. #include "PathfinderUtil.h"
  21. bool canSeeObj(const CGObjectInstance * obj)
  22. {
  23. /// Pathfinder should ignore placed events
  24. return obj != nullptr && obj->ID != Obj::EVENT;
  25. }
  26. void NodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero)
  27. {
  28. //TODO: fix this code duplication with AINodeStorage::initialize, problem is to keep `resetTile` inline
  29. int3 pos;
  30. const int3 sizes = gs->getMapSize();
  31. const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap;
  32. const PlayerColor player = hero->tempOwner;
  33. //make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
  34. const bool useFlying = options.useFlying;
  35. const bool useWaterWalking = options.useWaterWalking;
  36. for(pos.x=0; pos.x < sizes.x; ++pos.x)
  37. {
  38. for(pos.y=0; pos.y < sizes.y; ++pos.y)
  39. {
  40. for(pos.z=0; pos.z < sizes.z; ++pos.z)
  41. {
  42. const TerrainTile * tile = &gs->map->getTile(pos);
  43. switch(tile->terType)
  44. {
  45. case ETerrainType::ROCK:
  46. break;
  47. case ETerrainType::WATER:
  48. resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
  49. if(useFlying)
  50. resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
  51. if(useWaterWalking)
  52. resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
  53. break;
  54. default:
  55. resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
  56. if(useFlying)
  57. resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
  58. break;
  59. }
  60. }
  61. }
  62. }
  63. }
  64. std::vector<CGPathNode *> NodeStorage::calculateNeighbours(
  65. const PathNodeInfo & source,
  66. const PathfinderConfig * pathfinderConfig,
  67. const CPathfinderHelper * pathfinderHelper)
  68. {
  69. std::vector<CGPathNode *> neighbours;
  70. neighbours.reserve(16);
  71. auto accessibleNeighbourTiles = pathfinderHelper->getNeighbourTiles(source);
  72. for(auto & neighbour : accessibleNeighbourTiles)
  73. {
  74. for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1))
  75. {
  76. auto node = getNode(neighbour, i);
  77. if(node->accessible == CGPathNode::NOT_SET)
  78. continue;
  79. neighbours.push_back(node);
  80. }
  81. }
  82. return neighbours;
  83. }
  84. std::vector<CGPathNode *> NodeStorage::calculateTeleportations(
  85. const PathNodeInfo & source,
  86. const PathfinderConfig * pathfinderConfig,
  87. const CPathfinderHelper * pathfinderHelper)
  88. {
  89. std::vector<CGPathNode *> neighbours;
  90. if(!source.isNodeObjectVisitable())
  91. return neighbours;
  92. auto accessibleExits = pathfinderHelper->getTeleportExits(source);
  93. for(auto & neighbour : accessibleExits)
  94. {
  95. auto node = getNode(neighbour, source.node->layer);
  96. neighbours.push_back(node);
  97. }
  98. return neighbours;
  99. }
  100. std::vector<int3> CPathfinderHelper::getNeighbourTiles(const PathNodeInfo & source) const
  101. {
  102. std::vector<int3> neighbourTiles;
  103. neighbourTiles.reserve(16);
  104. getNeighbours(
  105. *source.tile,
  106. source.node->coord,
  107. neighbourTiles,
  108. boost::logic::indeterminate,
  109. source.node->layer == EPathfindingLayer::SAIL);
  110. if(source.isNodeObjectVisitable())
  111. {
  112. vstd::erase_if(neighbourTiles, [&](int3 tile) -> bool
  113. {
  114. return !canMoveBetween(tile, source.nodeObject->visitablePos());
  115. });
  116. }
  117. return neighbourTiles;
  118. }
  119. NodeStorage::NodeStorage(CPathsInfo & pathsInfo, const CGHeroInstance * hero)
  120. :out(pathsInfo)
  121. {
  122. out.hero = hero;
  123. out.hpos = hero->getPosition(false);
  124. }
  125. void NodeStorage::resetTile(
  126. const int3 & tile,
  127. EPathfindingLayer layer,
  128. CGPathNode::EAccessibility accessibility)
  129. {
  130. getNode(tile, layer)->update(tile, layer, accessibility);
  131. }
  132. CGPathNode * NodeStorage::getInitialNode()
  133. {
  134. auto initialNode = getNode(out.hpos, out.hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND);
  135. initialNode->turns = 0;
  136. initialNode->moveRemains = out.hero->movement;
  137. initialNode->setCost(0.0);
  138. return initialNode;
  139. }
  140. void NodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
  141. {
  142. assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other
  143. destination.node->setCost(destination.cost);
  144. destination.node->moveRemains = destination.movementLeft;
  145. destination.node->turns = destination.turn;
  146. destination.node->theNodeBefore = source.node;
  147. destination.node->action = destination.action;
  148. }
  149. PathfinderOptions::PathfinderOptions()
  150. {
  151. useFlying = settings["pathfinder"]["layers"]["flying"].Bool();
  152. useWaterWalking = settings["pathfinder"]["layers"]["waterWalking"].Bool();
  153. useEmbarkAndDisembark = settings["pathfinder"]["layers"]["sailing"].Bool();
  154. useTeleportTwoWay = settings["pathfinder"]["teleports"]["twoWay"].Bool();
  155. useTeleportOneWay = settings["pathfinder"]["teleports"]["oneWay"].Bool();
  156. useTeleportOneWayRandom = settings["pathfinder"]["teleports"]["oneWayRandom"].Bool();
  157. useTeleportWhirlpool = settings["pathfinder"]["teleports"]["whirlpool"].Bool();
  158. useCastleGate = settings["pathfinder"]["teleports"]["castleGate"].Bool();
  159. lightweightFlyingMode = settings["pathfinder"]["lightweightFlyingMode"].Bool();
  160. oneTurnSpecialLayersLimit = settings["pathfinder"]["oneTurnSpecialLayersLimit"].Bool();
  161. originalMovementRules = settings["pathfinder"]["originalMovementRules"].Bool();
  162. }
  163. void MovementCostRule::process(
  164. const PathNodeInfo & source,
  165. CDestinationNodeInfo & destination,
  166. const PathfinderConfig * pathfinderConfig,
  167. CPathfinderHelper * pathfinderHelper) const
  168. {
  169. float costAtNextTile = destination.cost;
  170. int turnAtNextTile = destination.turn;
  171. int moveAtNextTile = destination.movementLeft;
  172. int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
  173. int remains = moveAtNextTile - cost;
  174. int maxMovePoints = pathfinderHelper->getMaxMovePoints(destination.node->layer);
  175. if(remains < 0)
  176. {
  177. //occurs rarely, when hero with low movepoints tries to leave the road
  178. costAtNextTile += static_cast<float>(moveAtNextTile) / maxMovePoints;//we spent all points of current turn
  179. pathfinderHelper->updateTurnInfo(++turnAtNextTile);
  180. maxMovePoints = pathfinderHelper->getMaxMovePoints(destination.node->layer);
  181. moveAtNextTile = maxMovePoints;
  182. cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
  183. remains = moveAtNextTile - cost;
  184. }
  185. if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
  186. {
  187. /// FREE_SHIP_BOARDING bonus only remove additional penalty
  188. /// land <-> sail transition still cost movement points as normal movement
  189. remains = pathfinderHelper->movementPointsAfterEmbark(moveAtNextTile, cost, (destination.action == CGPathNode::DISEMBARK));
  190. cost = moveAtNextTile - remains;
  191. }
  192. costAtNextTile += static_cast<float>(cost) / maxMovePoints;
  193. destination.cost = costAtNextTile;
  194. destination.turn = turnAtNextTile;
  195. destination.movementLeft = remains;
  196. if(destination.isBetterWay() &&
  197. ((source.node->turns == turnAtNextTile && remains) || pathfinderHelper->passOneTurnLimitCheck(source)))
  198. {
  199. pathfinderConfig->nodeStorage->commit(destination, source);
  200. return;
  201. }
  202. destination.blocked = true;
  203. }
  204. PathfinderConfig::PathfinderConfig(
  205. std::shared_ptr<INodeStorage> nodeStorage,
  206. std::vector<std::shared_ptr<IPathfindingRule>> rules)
  207. : nodeStorage(nodeStorage), rules(rules), options()
  208. {
  209. }
  210. CPathfinder::CPathfinder(
  211. CPathsInfo & _out,
  212. CGameState * _gs,
  213. const CGHeroInstance * _hero)
  214. : CPathfinder(
  215. _gs,
  216. _hero,
  217. std::make_shared<PathfinderConfig>(
  218. std::make_shared<NodeStorage>(_out, _hero),
  219. std::vector<std::shared_ptr<IPathfindingRule>>{
  220. std::make_shared<LayerTransitionRule>(),
  221. std::make_shared<DestinationActionRule>(),
  222. std::make_shared<MovementToDestinationRule>(),
  223. std::make_shared<MovementCostRule>(),
  224. std::make_shared<MovementAfterDestinationRule>()
  225. }))
  226. {
  227. }
  228. CPathfinder::CPathfinder(
  229. CGameState * _gs,
  230. const CGHeroInstance * _hero,
  231. std::shared_ptr<PathfinderConfig> config)
  232. : CGameInfoCallback(_gs, boost::optional<PlayerColor>())
  233. , hero(_hero)
  234. , patrolTiles({})
  235. , config(config)
  236. , source()
  237. , destination()
  238. {
  239. assert(hero);
  240. assert(hero == getHero(hero->id));
  241. hlp = make_unique<CPathfinderHelper>(_gs, hero, config->options);
  242. initializePatrol();
  243. initializeGraph();
  244. }
  245. void CPathfinder::push(CGPathNode * node)
  246. {
  247. if(node && !node->inPQ)
  248. {
  249. node->inPQ = true;
  250. node->pq = &this->pq;
  251. auto handle = pq.push(node);
  252. node->pqHandle = handle;
  253. }
  254. }
  255. CGPathNode * CPathfinder::topAndPop()
  256. {
  257. auto node = pq.top();
  258. pq.pop();
  259. node->inPQ = false;
  260. node->pq = nullptr;
  261. return node;
  262. }
  263. void CPathfinder::calculatePaths()
  264. {
  265. //logGlobal->info("Calculating paths for hero %s (adress %d) of player %d", hero->name, hero , hero->tempOwner);
  266. //initial tile - set cost on 0 and add to the queue
  267. CGPathNode * initialNode = config->nodeStorage->getInitialNode();
  268. if(!isInTheMap(initialNode->coord)/* || !gs->map->isInTheMap(dest)*/) //check input
  269. {
  270. logGlobal->error("CGameState::calculatePaths: Hero outside the gs->map? How dare you...");
  271. throw std::runtime_error("Wrong checksum");
  272. }
  273. if(isHeroPatrolLocked())
  274. return;
  275. push(initialNode);
  276. while(!pq.empty())
  277. {
  278. auto node = topAndPop();
  279. auto excludeOurHero = node->coord == initialNode->coord;
  280. source.setNode(gs, node, excludeOurHero);
  281. source.node->locked = true;
  282. int movement = source.node->moveRemains;
  283. uint8_t turn = source.node->turns;
  284. float cost = source.node->getCost();
  285. hlp->updateTurnInfo(turn);
  286. if(!movement)
  287. {
  288. hlp->updateTurnInfo(++turn);
  289. movement = hlp->getMaxMovePoints(source.node->layer);
  290. if(!hlp->passOneTurnLimitCheck(source))
  291. continue;
  292. }
  293. source.guarded = isSourceGuarded();
  294. if(source.nodeObject)
  295. source.objectRelations = gs->getPlayerRelations(hero->tempOwner, source.nodeObject->tempOwner);
  296. //add accessible neighbouring nodes to the queue
  297. auto neighbourNodes = config->nodeStorage->calculateNeighbours(source, config.get(), hlp.get());
  298. for(CGPathNode * neighbour : neighbourNodes)
  299. {
  300. if(neighbour->locked)
  301. continue;
  302. if(!isPatrolMovementAllowed(neighbour->coord))
  303. continue;
  304. if(!hlp->isLayerAvailable(neighbour->layer))
  305. continue;
  306. destination.setNode(gs, neighbour);
  307. /// Check transition without tile accessability rules
  308. if(source.node->layer != neighbour->layer && !isLayerTransitionPossible())
  309. continue;
  310. destination.turn = turn;
  311. destination.movementLeft = movement;
  312. destination.cost = cost;
  313. destination.guarded = isDestinationGuarded();
  314. destination.isGuardianTile = destination.guarded && isDestinationGuardian();
  315. if(destination.nodeObject)
  316. destination.objectRelations = gs->getPlayerRelations(hero->tempOwner, destination.nodeObject->tempOwner);
  317. for(auto rule : config->rules)
  318. {
  319. rule->process(source, destination, config.get(), hlp.get());
  320. if(destination.blocked)
  321. break;
  322. }
  323. if(!destination.blocked)
  324. push(destination.node);
  325. } //neighbours loop
  326. //just add all passable teleport exits
  327. /// For now we disable teleports usage for patrol movement
  328. /// VCAI not aware about patrol and may stuck while attempt to use teleport
  329. if(patrolState == PATROL_RADIUS)
  330. continue;
  331. auto teleportationNodes = config->nodeStorage->calculateTeleportations(source, config.get(), hlp.get());
  332. for(CGPathNode * teleportNode : teleportationNodes)
  333. {
  334. if(teleportNode->locked)
  335. continue;
  336. /// TODO: We may consider use invisible exits on FoW border in future
  337. /// Useful for AI when at least one tile around exit is visible and passable
  338. /// Objects are usually visible on FoW border anyway so it's not cheating.
  339. ///
  340. /// For now it's disabled as it's will cause crashes in movement code.
  341. if(teleportNode->accessible == CGPathNode::BLOCKED)
  342. continue;
  343. destination.setNode(gs, teleportNode);
  344. destination.turn = turn;
  345. destination.movementLeft = movement;
  346. destination.cost = cost;
  347. if(destination.isBetterWay())
  348. {
  349. destination.action = getTeleportDestAction();
  350. config->nodeStorage->commit(destination, source);
  351. if(destination.node->action == CGPathNode::TELEPORT_NORMAL)
  352. push(destination.node);
  353. }
  354. }
  355. } //queue loop
  356. }
  357. std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(TeleportChannelID channelID) const
  358. {
  359. std::vector<int3> allowedExits;
  360. for(auto objId : getTeleportChannelExits(channelID, hero->tempOwner))
  361. {
  362. auto obj = getObj(objId);
  363. if(dynamic_cast<const CGWhirlpool *>(obj))
  364. {
  365. auto pos = obj->getBlockedPos();
  366. for(auto p : pos)
  367. {
  368. if(gs->map->getTile(p).topVisitableId() == obj->ID)
  369. allowedExits.push_back(p);
  370. }
  371. }
  372. else if(CGTeleport::isExitPassable(gs, hero, obj))
  373. allowedExits.push_back(obj->visitablePos());
  374. }
  375. return allowedExits;
  376. }
  377. std::vector<int3> CPathfinderHelper::getCastleGates(const PathNodeInfo & source) const
  378. {
  379. std::vector<int3> allowedExits;
  380. auto towns = getPlayer(hero->tempOwner)->towns;
  381. for(const auto & town : towns)
  382. {
  383. if(town->id != source.nodeObject->id && town->visitingHero == nullptr
  384. && town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO))
  385. {
  386. allowedExits.push_back(town->visitablePos());
  387. }
  388. }
  389. return allowedExits;
  390. }
  391. std::vector<int3> CPathfinderHelper::getTeleportExits(const PathNodeInfo & source) const
  392. {
  393. std::vector<int3> teleportationExits;
  394. const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
  395. if(isAllowedTeleportEntrance(objTeleport))
  396. {
  397. for(auto exit : getAllowedTeleportChannelExits(objTeleport->channel))
  398. {
  399. teleportationExits.push_back(exit);
  400. }
  401. }
  402. else if(options.useCastleGate
  403. && (source.nodeObject->ID == Obj::TOWN && source.nodeObject->subID == ETownType::INFERNO
  404. && source.objectRelations != PlayerRelations::ENEMIES))
  405. {
  406. /// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo
  407. /// This may be handy if we allow to use teleportation to friendly towns
  408. for(auto exit : getCastleGates(source))
  409. {
  410. teleportationExits.push_back(exit);
  411. }
  412. }
  413. return teleportationExits;
  414. }
  415. bool CPathfinder::isHeroPatrolLocked() const
  416. {
  417. return patrolState == PATROL_LOCKED;
  418. }
  419. bool CPathfinder::isPatrolMovementAllowed(const int3 & dst) const
  420. {
  421. if(patrolState == PATROL_RADIUS)
  422. {
  423. if(!vstd::contains(patrolTiles, dst))
  424. return false;
  425. }
  426. return true;
  427. }
  428. bool CPathfinder::isLayerTransitionPossible() const
  429. {
  430. ELayer destLayer = destination.node->layer;
  431. /// No layer transition allowed when previous node action is BATTLE
  432. if(source.node->action == CGPathNode::BATTLE)
  433. return false;
  434. switch(source.node->layer)
  435. {
  436. case ELayer::LAND:
  437. if(destLayer == ELayer::AIR)
  438. {
  439. if(!config->options.lightweightFlyingMode || isSourceInitialPosition())
  440. return true;
  441. }
  442. else if(destLayer == ELayer::SAIL)
  443. {
  444. if(destination.tile->isWater())
  445. return true;
  446. }
  447. else
  448. return true;
  449. break;
  450. case ELayer::SAIL:
  451. if(destLayer == ELayer::LAND && !destination.tile->isWater())
  452. return true;
  453. break;
  454. case ELayer::AIR:
  455. if(destLayer == ELayer::LAND)
  456. return true;
  457. break;
  458. case ELayer::WATER:
  459. if(destLayer == ELayer::LAND)
  460. return true;
  461. break;
  462. }
  463. return false;
  464. }
  465. void LayerTransitionRule::process(
  466. const PathNodeInfo & source,
  467. CDestinationNodeInfo & destination,
  468. const PathfinderConfig * pathfinderConfig,
  469. CPathfinderHelper * pathfinderHelper) const
  470. {
  471. if(source.node->layer == destination.node->layer)
  472. return;
  473. switch(source.node->layer)
  474. {
  475. case EPathfindingLayer::LAND:
  476. if(destination.node->layer == EPathfindingLayer::SAIL)
  477. {
  478. /// Cannot enter empty water tile from land -> it has to be visitable
  479. if(destination.node->accessible == CGPathNode::ACCESSIBLE)
  480. destination.blocked = true;
  481. }
  482. break;
  483. case EPathfindingLayer::SAIL:
  484. //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast
  485. if((destination.node->accessible != CGPathNode::ACCESSIBLE && (destination.node->accessible != CGPathNode::BLOCKVIS || destination.tile->blocked))
  486. || destination.tile->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate
  487. {
  488. destination.blocked = true;
  489. }
  490. break;
  491. case EPathfindingLayer::AIR:
  492. if(pathfinderConfig->options.originalMovementRules)
  493. {
  494. if((source.node->accessible != CGPathNode::ACCESSIBLE &&
  495. source.node->accessible != CGPathNode::VISITABLE) &&
  496. (destination.node->accessible != CGPathNode::VISITABLE &&
  497. destination.node->accessible != CGPathNode::ACCESSIBLE))
  498. {
  499. destination.blocked = true;
  500. }
  501. }
  502. else if(source.node->accessible != CGPathNode::ACCESSIBLE && destination.node->accessible != CGPathNode::ACCESSIBLE)
  503. {
  504. /// Hero that fly can only land on accessible tiles
  505. destination.blocked = true;
  506. }
  507. break;
  508. case EPathfindingLayer::WATER:
  509. if(destination.node->accessible != CGPathNode::ACCESSIBLE && destination.node->accessible != CGPathNode::VISITABLE)
  510. {
  511. /// Hero that walking on water can transit to accessible and visitable tiles
  512. /// Though hero can't interact with blocking visit objects while standing on water
  513. destination.blocked = true;
  514. }
  515. break;
  516. }
  517. }
  518. PathfinderBlockingRule::BlockingReason MovementToDestinationRule::getBlockingReason(
  519. const PathNodeInfo & source,
  520. const CDestinationNodeInfo & destination,
  521. const PathfinderConfig * pathfinderConfig,
  522. const CPathfinderHelper * pathfinderHelper) const
  523. {
  524. if(destination.node->accessible == CGPathNode::BLOCKED)
  525. return BlockingReason::DESTINATION_BLOCKED;
  526. switch(destination.node->layer)
  527. {
  528. case EPathfindingLayer::LAND:
  529. if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord))
  530. return BlockingReason::DESTINATION_BLOCKED;
  531. if(source.guarded)
  532. {
  533. if(!(pathfinderConfig->options.originalMovementRules && source.node->layer == EPathfindingLayer::AIR) &&
  534. !destination.isGuardianTile) // Can step into tile of guard
  535. {
  536. return BlockingReason::SOURCE_GUARDED;
  537. }
  538. }
  539. break;
  540. case EPathfindingLayer::SAIL:
  541. if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord))
  542. return BlockingReason::DESTINATION_BLOCKED;
  543. if(source.guarded)
  544. {
  545. // Hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
  546. if(source.node->action != CGPathNode::EMBARK && !destination.isGuardianTile)
  547. return BlockingReason::SOURCE_GUARDED;
  548. }
  549. if(source.node->layer == EPathfindingLayer::LAND)
  550. {
  551. if(!destination.isNodeObjectVisitable())
  552. return BlockingReason::DESTINATION_BLOCKED;
  553. if(destination.nodeObject->ID != Obj::BOAT && destination.nodeObject->ID != Obj::HERO)
  554. return BlockingReason::DESTINATION_BLOCKED;
  555. }
  556. else if(destination.isNodeObjectVisitable() && destination.nodeObject->ID == Obj::BOAT)
  557. {
  558. /// Hero in boat can't visit empty boats
  559. return BlockingReason::DESTINATION_BLOCKED;
  560. }
  561. break;
  562. case EPathfindingLayer::WATER:
  563. if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord)
  564. || destination.node->accessible != CGPathNode::ACCESSIBLE)
  565. {
  566. return BlockingReason::DESTINATION_BLOCKED;
  567. }
  568. if(destination.guarded)
  569. return BlockingReason::DESTINATION_BLOCKED;
  570. break;
  571. }
  572. return BlockingReason::NONE;
  573. }
  574. void MovementAfterDestinationRule::process(
  575. const PathNodeInfo & source,
  576. CDestinationNodeInfo & destination,
  577. const PathfinderConfig * config,
  578. CPathfinderHelper * pathfinderHelper) const
  579. {
  580. auto blocker = getBlockingReason(source, destination, config, pathfinderHelper);
  581. if(blocker == BlockingReason::DESTINATION_GUARDED && destination.action == CGPathNode::ENodeAction::BATTLE)
  582. {
  583. return; // allow bypass guarded tile but only in direction of guard, a bit UI related thing
  584. }
  585. destination.blocked = blocker != BlockingReason::NONE;
  586. }
  587. PathfinderBlockingRule::BlockingReason MovementAfterDestinationRule::getBlockingReason(
  588. const PathNodeInfo & source,
  589. const CDestinationNodeInfo & destination,
  590. const PathfinderConfig * config,
  591. const CPathfinderHelper * pathfinderHelper) const
  592. {
  593. switch(destination.action)
  594. {
  595. /// TODO: Investigate what kind of limitation is possible to apply on movement from visitable tiles
  596. /// Likely in many cases we don't need to add visitable tile to queue when hero don't fly
  597. case CGPathNode::VISIT:
  598. {
  599. /// For now we only add visitable tile into queue when it's teleporter that allow transit
  600. /// Movement from visitable tile when hero is standing on it is possible into any layer
  601. const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(destination.nodeObject);
  602. if(pathfinderHelper->isAllowedTeleportEntrance(objTeleport))
  603. {
  604. /// For now we'll always allow transit over teleporters
  605. /// Transit over whirlpools only allowed when hero protected
  606. return BlockingReason::NONE;
  607. }
  608. else if(destination.nodeObject->ID == Obj::GARRISON
  609. || destination.nodeObject->ID == Obj::GARRISON2
  610. || destination.nodeObject->ID == Obj::BORDER_GATE)
  611. {
  612. /// Transit via unguarded garrisons is always possible
  613. return BlockingReason::NONE;
  614. }
  615. return BlockingReason::DESTINATION_VISIT;
  616. }
  617. case CGPathNode::BLOCKING_VISIT:
  618. return destination.guarded
  619. ? BlockingReason::DESTINATION_GUARDED
  620. : BlockingReason::DESTINATION_BLOCKVIS;
  621. case CGPathNode::NORMAL:
  622. return BlockingReason::NONE;
  623. case CGPathNode::EMBARK:
  624. if(pathfinderHelper->options.useEmbarkAndDisembark)
  625. return BlockingReason::NONE;
  626. return BlockingReason::DESTINATION_BLOCKED;
  627. case CGPathNode::DISEMBARK:
  628. if(pathfinderHelper->options.useEmbarkAndDisembark)
  629. return destination.guarded ? BlockingReason::DESTINATION_GUARDED : BlockingReason::NONE;
  630. return BlockingReason::DESTINATION_BLOCKED;
  631. case CGPathNode::BATTLE:
  632. /// Movement after BATTLE action only possible from guarded tile to guardian tile
  633. if(destination.guarded)
  634. return BlockingReason::DESTINATION_GUARDED;
  635. break;
  636. }
  637. return BlockingReason::DESTINATION_BLOCKED;
  638. }
  639. void DestinationActionRule::process(
  640. const PathNodeInfo & source,
  641. CDestinationNodeInfo & destination,
  642. const PathfinderConfig * pathfinderConfig,
  643. CPathfinderHelper * pathfinderHelper) const
  644. {
  645. if(destination.action != CGPathNode::ENodeAction::UNKNOWN)
  646. {
  647. #ifdef VCMI_TRACE_PATHFINDER
  648. logAi->trace("Accepted precalculated action at %s", destination.coord.toString());
  649. #endif
  650. return;
  651. }
  652. CGPathNode::ENodeAction action = CGPathNode::NORMAL;
  653. auto hero = pathfinderHelper->hero;
  654. switch(destination.node->layer)
  655. {
  656. case EPathfindingLayer::LAND:
  657. if(source.node->layer == EPathfindingLayer::SAIL)
  658. {
  659. // TODO: Handle dismebark into guarded areaa
  660. action = CGPathNode::DISEMBARK;
  661. break;
  662. }
  663. /// don't break - next case shared for both land and sail layers
  664. FALLTHROUGH
  665. case EPathfindingLayer::SAIL:
  666. if(destination.isNodeObjectVisitable())
  667. {
  668. auto objRel = destination.objectRelations;
  669. if(destination.nodeObject->ID == Obj::BOAT)
  670. action = CGPathNode::EMBARK;
  671. else if(destination.nodeObject->ID == Obj::HERO)
  672. {
  673. if(objRel == PlayerRelations::ENEMIES)
  674. action = CGPathNode::BATTLE;
  675. else
  676. action = CGPathNode::BLOCKING_VISIT;
  677. }
  678. else if(destination.nodeObject->ID == Obj::TOWN)
  679. {
  680. if(destination.nodeObject->passableFor(hero->tempOwner))
  681. action = CGPathNode::VISIT;
  682. else if(objRel == PlayerRelations::ENEMIES)
  683. action = CGPathNode::BATTLE;
  684. }
  685. else if(destination.nodeObject->ID == Obj::GARRISON || destination.nodeObject->ID == Obj::GARRISON2)
  686. {
  687. if(destination.nodeObject->passableFor(hero->tempOwner))
  688. {
  689. if(destination.guarded)
  690. action = CGPathNode::BATTLE;
  691. }
  692. else if(objRel == PlayerRelations::ENEMIES)
  693. action = CGPathNode::BATTLE;
  694. }
  695. else if(destination.nodeObject->ID == Obj::BORDER_GATE)
  696. {
  697. if(destination.nodeObject->passableFor(hero->tempOwner))
  698. {
  699. if(destination.guarded)
  700. action = CGPathNode::BATTLE;
  701. }
  702. else
  703. action = CGPathNode::BLOCKING_VISIT;
  704. }
  705. else if(destination.isGuardianTile)
  706. action = CGPathNode::BATTLE;
  707. else if(destination.nodeObject->blockVisit && !(pathfinderConfig->options.useCastleGate && destination.nodeObject->ID == Obj::TOWN))
  708. action = CGPathNode::BLOCKING_VISIT;
  709. if(action == CGPathNode::NORMAL)
  710. {
  711. if(destination.guarded)
  712. action = CGPathNode::BATTLE;
  713. else
  714. action = CGPathNode::VISIT;
  715. }
  716. }
  717. else if(destination.guarded)
  718. action = CGPathNode::BATTLE;
  719. break;
  720. }
  721. destination.action = action;
  722. }
  723. CGPathNode::ENodeAction CPathfinder::getTeleportDestAction() const
  724. {
  725. CGPathNode::ENodeAction action = CGPathNode::TELEPORT_NORMAL;
  726. if(destination.isNodeObjectVisitable() && destination.nodeObject->ID == Obj::HERO)
  727. {
  728. auto objRel = getPlayerRelations(destination.nodeObject->tempOwner, hero->tempOwner);
  729. if(objRel == PlayerRelations::ENEMIES)
  730. action = CGPathNode::TELEPORT_BATTLE;
  731. else
  732. action = CGPathNode::TELEPORT_BLOCKING_VISIT;
  733. }
  734. return action;
  735. }
  736. bool CPathfinder::isSourceInitialPosition() const
  737. {
  738. return source.node->coord == config->nodeStorage->getInitialNode()->coord;
  739. }
  740. bool CPathfinder::isSourceGuarded() const
  741. {
  742. /// Hero can move from guarded tile if movement started on that tile
  743. /// It's possible at least in these cases:
  744. /// - Map start with hero on guarded tile
  745. /// - Dimention door used
  746. /// TODO: check what happen when there is several guards
  747. if(gs->guardingCreaturePosition(source.node->coord).valid() && !isSourceInitialPosition())
  748. {
  749. return true;
  750. }
  751. return false;
  752. }
  753. bool CPathfinder::isDestinationGuarded() const
  754. {
  755. /// isDestinationGuarded is exception needed for garrisons.
  756. /// When monster standing behind garrison it's visitable and guarded at the same time.
  757. return gs->guardingCreaturePosition(destination.node->coord).valid();
  758. }
  759. bool CPathfinder::isDestinationGuardian() const
  760. {
  761. return gs->guardingCreaturePosition(source.node->coord) == destination.node->coord;
  762. }
  763. void CPathfinder::initializePatrol()
  764. {
  765. auto state = PATROL_NONE;
  766. if(hero->patrol.patrolling && !getPlayer(hero->tempOwner)->human)
  767. {
  768. if(hero->patrol.patrolRadius)
  769. {
  770. state = PATROL_RADIUS;
  771. gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadius, boost::optional<PlayerColor>(), 0, int3::DIST_MANHATTAN);
  772. }
  773. else
  774. state = PATROL_LOCKED;
  775. }
  776. patrolState = state;
  777. }
  778. void CPathfinder::initializeGraph()
  779. {
  780. INodeStorage * nodeStorage = config->nodeStorage.get();
  781. nodeStorage->initialize(config->options, gs, hero);
  782. }
  783. bool CPathfinderHelper::canMoveBetween(const int3 & a, const int3 & b) const
  784. {
  785. return gs->checkForVisitableDir(a, b);
  786. }
  787. bool CPathfinderHelper::isAllowedTeleportEntrance(const CGTeleport * obj) const
  788. {
  789. if(!obj || !isTeleportEntrancePassable(obj, hero->tempOwner))
  790. return false;
  791. auto whirlpool = dynamic_cast<const CGWhirlpool *>(obj);
  792. if(whirlpool)
  793. {
  794. if(addTeleportWhirlpool(whirlpool))
  795. return true;
  796. }
  797. else if(addTeleportTwoWay(obj) || addTeleportOneWay(obj) || addTeleportOneWayRandom(obj))
  798. return true;
  799. return false;
  800. }
  801. bool CPathfinderHelper::addTeleportTwoWay(const CGTeleport * obj) const
  802. {
  803. return options.useTeleportTwoWay && isTeleportChannelBidirectional(obj->channel, hero->tempOwner);
  804. }
  805. bool CPathfinderHelper::addTeleportOneWay(const CGTeleport * obj) const
  806. {
  807. if(options.useTeleportOneWay && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
  808. {
  809. auto passableExits = CGTeleport::getPassableExits(gs, hero, getTeleportChannelExits(obj->channel, hero->tempOwner));
  810. if(passableExits.size() == 1)
  811. return true;
  812. }
  813. return false;
  814. }
  815. bool CPathfinderHelper::addTeleportOneWayRandom(const CGTeleport * obj) const
  816. {
  817. if(options.useTeleportOneWayRandom && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
  818. {
  819. auto passableExits = CGTeleport::getPassableExits(gs, hero, getTeleportChannelExits(obj->channel, hero->tempOwner));
  820. if(passableExits.size() > 1)
  821. return true;
  822. }
  823. return false;
  824. }
  825. bool CPathfinderHelper::addTeleportWhirlpool(const CGWhirlpool * obj) const
  826. {
  827. return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
  828. }
  829. int CPathfinderHelper::movementPointsAfterEmbark(int movement, int basicCost, bool disembark) const
  830. {
  831. return hero->movementPointsAfterEmbark(movement, basicCost, disembark, getTurnInfo());
  832. }
  833. bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const
  834. {
  835. if(!options.oneTurnSpecialLayersLimit)
  836. return true;
  837. if(source.node->layer == EPathfindingLayer::WATER)
  838. return false;
  839. if(source.node->layer == EPathfindingLayer::AIR)
  840. {
  841. if(options.originalMovementRules && source.node->accessible == CGPathNode::ACCESSIBLE)
  842. return true;
  843. else
  844. return false;
  845. }
  846. return true;
  847. }
  848. TurnInfo::BonusCache::BonusCache(TBonusListPtr bl)
  849. {
  850. noTerrainPenalty.reserve(ETerrainType::ROCK);
  851. for(int i = 0; i < ETerrainType::ROCK; i++)
  852. {
  853. noTerrainPenalty.push_back(static_cast<bool>(
  854. bl->getFirst(Selector::type(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype(i)))));
  855. }
  856. freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type(Bonus::FREE_SHIP_BOARDING)));
  857. flyingMovement = static_cast<bool>(bl->getFirst(Selector::type(Bonus::FLYING_MOVEMENT)));
  858. flyingMovementVal = bl->valOfBonuses(Selector::type(Bonus::FLYING_MOVEMENT));
  859. waterWalking = static_cast<bool>(bl->getFirst(Selector::type(Bonus::WATER_WALKING)));
  860. waterWalkingVal = bl->valOfBonuses(Selector::type(Bonus::WATER_WALKING));
  861. }
  862. TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn)
  863. : hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1)
  864. {
  865. bonuses = hero->getAllBonuses(Selector::days(turn), Selector::all, nullptr, "");
  866. bonusCache = make_unique<BonusCache>(bonuses);
  867. nativeTerrain = hero->getNativeTerrain();
  868. }
  869. bool TurnInfo::isLayerAvailable(const EPathfindingLayer layer) const
  870. {
  871. switch(layer)
  872. {
  873. case EPathfindingLayer::AIR:
  874. if(!hasBonusOfType(Bonus::FLYING_MOVEMENT))
  875. return false;
  876. break;
  877. case EPathfindingLayer::WATER:
  878. if(!hasBonusOfType(Bonus::WATER_WALKING))
  879. return false;
  880. break;
  881. }
  882. return true;
  883. }
  884. bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const
  885. {
  886. switch(type)
  887. {
  888. case Bonus::FREE_SHIP_BOARDING:
  889. return bonusCache->freeShipBoarding;
  890. case Bonus::FLYING_MOVEMENT:
  891. return bonusCache->flyingMovement;
  892. case Bonus::WATER_WALKING:
  893. return bonusCache->waterWalking;
  894. case Bonus::NO_TERRAIN_PENALTY:
  895. return bonusCache->noTerrainPenalty[subtype];
  896. }
  897. return static_cast<bool>(
  898. bonuses->getFirst(Selector::type(type).And(Selector::subtype(subtype))));
  899. }
  900. int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const
  901. {
  902. switch(type)
  903. {
  904. case Bonus::FLYING_MOVEMENT:
  905. return bonusCache->flyingMovementVal;
  906. case Bonus::WATER_WALKING:
  907. return bonusCache->waterWalkingVal;
  908. }
  909. return bonuses->valOfBonuses(Selector::type(type).And(Selector::subtype(subtype)));
  910. }
  911. int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
  912. {
  913. if(maxMovePointsLand == -1)
  914. maxMovePointsLand = hero->maxMovePointsCached(true, this);
  915. if(maxMovePointsWater == -1)
  916. maxMovePointsWater = hero->maxMovePointsCached(false, this);
  917. return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
  918. }
  919. CPathfinderHelper::CPathfinderHelper(CGameState * gs, const CGHeroInstance * Hero, const PathfinderOptions & Options)
  920. : CGameInfoCallback(gs, boost::optional<PlayerColor>()), turn(-1), hero(Hero), options(Options)
  921. {
  922. turnsInfo.reserve(16);
  923. updateTurnInfo();
  924. }
  925. CPathfinderHelper::~CPathfinderHelper()
  926. {
  927. for(auto ti : turnsInfo)
  928. delete ti;
  929. }
  930. void CPathfinderHelper::updateTurnInfo(const int Turn)
  931. {
  932. if(turn != Turn)
  933. {
  934. turn = Turn;
  935. if(turn >= turnsInfo.size())
  936. {
  937. auto ti = new TurnInfo(hero, turn);
  938. turnsInfo.push_back(ti);
  939. }
  940. }
  941. }
  942. bool CPathfinderHelper::isLayerAvailable(const EPathfindingLayer layer) const
  943. {
  944. switch(layer)
  945. {
  946. case EPathfindingLayer::AIR:
  947. if(!options.useFlying)
  948. return false;
  949. break;
  950. case EPathfindingLayer::WATER:
  951. if(!options.useWaterWalking)
  952. return false;
  953. break;
  954. }
  955. return turnsInfo[turn]->isLayerAvailable(layer);
  956. }
  957. const TurnInfo * CPathfinderHelper::getTurnInfo() const
  958. {
  959. return turnsInfo[turn];
  960. }
  961. bool CPathfinderHelper::hasBonusOfType(const Bonus::BonusType type, const int subtype) const
  962. {
  963. return turnsInfo[turn]->hasBonusOfType(type, subtype);
  964. }
  965. int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const
  966. {
  967. return turnsInfo[turn]->getMaxMovePoints(layer);
  968. }
  969. void CPathfinderHelper::getNeighbours(
  970. const TerrainTile & srct,
  971. const int3 & tile,
  972. std::vector<int3> & vec,
  973. const boost::logic::tribool & onLand,
  974. const bool limitCoastSailing) const
  975. {
  976. CMap * map = gs->map;
  977. static const int3 dirs[] = {
  978. int3(-1, +1, +0), int3(0, +1, +0), int3(+1, +1, +0),
  979. int3(-1, +0, +0), /* source pos */ int3(+1, +0, +0),
  980. int3(-1, -1, +0), int3(0, -1, +0), int3(+1, -1, +0)
  981. };
  982. for(auto & dir : dirs)
  983. {
  984. const int3 hlp = tile + dir;
  985. if(!map->isInTheMap(hlp))
  986. continue;
  987. const TerrainTile & hlpt = map->getTile(hlp);
  988. if(hlpt.terType == ETerrainType::ROCK)
  989. continue;
  990. // //we cannot visit things from blocked tiles
  991. // if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
  992. // {
  993. // continue;
  994. // }
  995. /// Following condition let us avoid diagonal movement over coast when sailing
  996. if(srct.terType == ETerrainType::WATER && limitCoastSailing && hlpt.terType == ETerrainType::WATER && dir.x && dir.y) //diagonal move through water
  997. {
  998. int3 hlp1 = tile,
  999. hlp2 = tile;
  1000. hlp1.x += dir.x;
  1001. hlp2.y += dir.y;
  1002. if(map->getTile(hlp1).terType != ETerrainType::WATER || map->getTile(hlp2).terType != ETerrainType::WATER)
  1003. continue;
  1004. }
  1005. if(indeterminate(onLand) || onLand == (hlpt.terType != ETerrainType::WATER))
  1006. {
  1007. vec.push_back(hlp);
  1008. }
  1009. }
  1010. }
  1011. int CPathfinderHelper::getMovementCost(
  1012. const int3 & src,
  1013. const int3 & dst,
  1014. const TerrainTile * ct,
  1015. const TerrainTile * dt,
  1016. const int remainingMovePoints,
  1017. const bool checkLast) const
  1018. {
  1019. if(src == dst) //same tile
  1020. return 0;
  1021. auto ti = getTurnInfo();
  1022. if(ct == nullptr || dt == nullptr)
  1023. {
  1024. ct = hero->cb->getTile(src);
  1025. dt = hero->cb->getTile(dst);
  1026. }
  1027. /// TODO: by the original game rules hero shouldn't be affected by terrain penalty while flying.
  1028. /// Also flying movement only has penalty when player moving over blocked tiles.
  1029. /// So if you only have base flying with 40% penalty you can still ignore terrain penalty while having zero flying penalty.
  1030. int ret = hero->getTileCost(*dt, *ct, ti);
  1031. /// Unfortunately this can't be implemented yet as server don't know when player flying and when he's not.
  1032. /// Difference in cost calculation on client and server is much worse than incorrect cost.
  1033. /// So this one is waiting till server going to use pathfinder rules for path validation.
  1034. if(dt->blocked && ti->hasBonusOfType(Bonus::FLYING_MOVEMENT))
  1035. {
  1036. ret *= (100.0 + ti->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0;
  1037. }
  1038. else if(dt->terType == ETerrainType::WATER)
  1039. {
  1040. if(hero->boat && ct->hasFavorableWinds() && dt->hasFavorableWinds())
  1041. ret *= 0.666;
  1042. else if(!hero->boat && ti->hasBonusOfType(Bonus::WATER_WALKING))
  1043. {
  1044. ret *= (100.0 + ti->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
  1045. }
  1046. }
  1047. if(src.x != dst.x && src.y != dst.y) //it's diagonal move
  1048. {
  1049. int old = ret;
  1050. ret *= 1.414213;
  1051. //diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
  1052. if(ret > remainingMovePoints && remainingMovePoints >= old)
  1053. {
  1054. return remainingMovePoints;
  1055. }
  1056. }
  1057. /// TODO: This part need rework in order to work properly with flying and water walking
  1058. /// Currently it's only work properly for normal movement or sailing
  1059. int left = remainingMovePoints-ret;
  1060. if(checkLast && left > 0 && remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points
  1061. {
  1062. std::vector<int3> vec;
  1063. vec.reserve(8); //optimization
  1064. getNeighbours(*dt, dst, vec, ct->terType != ETerrainType::WATER, true);
  1065. for(auto & elem : vec)
  1066. {
  1067. int fcost = getMovementCost(dst, elem, nullptr, nullptr, left, false);
  1068. if(fcost <= left)
  1069. {
  1070. return ret;
  1071. }
  1072. }
  1073. ret = remainingMovePoints;
  1074. }
  1075. return ret;
  1076. }
  1077. int3 CGPath::startPos() const
  1078. {
  1079. return nodes[nodes.size()-1].coord;
  1080. }
  1081. int3 CGPath::endPos() const
  1082. {
  1083. return nodes[0].coord;
  1084. }
  1085. void CGPath::convert(ui8 mode)
  1086. {
  1087. if(mode==0)
  1088. {
  1089. for(auto & elem : nodes)
  1090. {
  1091. elem.coord = CGHeroInstance::convertPosition(elem.coord,true);
  1092. }
  1093. }
  1094. }
  1095. CPathsInfo::CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_)
  1096. : sizes(Sizes), hero(hero_)
  1097. {
  1098. nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][ELayer::NUM_LAYERS]);
  1099. }
  1100. CPathsInfo::~CPathsInfo() = default;
  1101. const CGPathNode * CPathsInfo::getPathInfo(const int3 & tile) const
  1102. {
  1103. assert(vstd::iswithin(tile.x, 0, sizes.x));
  1104. assert(vstd::iswithin(tile.y, 0, sizes.y));
  1105. assert(vstd::iswithin(tile.z, 0, sizes.z));
  1106. return getNode(tile);
  1107. }
  1108. bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const
  1109. {
  1110. out.nodes.clear();
  1111. const CGPathNode * curnode = getNode(dst);
  1112. if(!curnode->theNodeBefore)
  1113. return false;
  1114. while(curnode)
  1115. {
  1116. const CGPathNode cpn = * curnode;
  1117. curnode = curnode->theNodeBefore;
  1118. out.nodes.push_back(cpn);
  1119. }
  1120. return true;
  1121. }
  1122. const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
  1123. {
  1124. auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND];
  1125. if(landNode->reachable())
  1126. return landNode;
  1127. else
  1128. return &nodes[coord.x][coord.y][coord.z][ELayer::SAIL];
  1129. }
  1130. PathNodeInfo::PathNodeInfo()
  1131. : node(nullptr), nodeObject(nullptr), tile(nullptr), coord(-1, -1, -1), guarded(false)
  1132. {
  1133. }
  1134. void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject)
  1135. {
  1136. node = n;
  1137. if(coord != node->coord)
  1138. {
  1139. assert(node->coord.valid());
  1140. coord = node->coord;
  1141. tile = gs->getTile(coord);
  1142. nodeObject = tile->topVisitableObj(excludeTopObject);
  1143. }
  1144. guarded = false;
  1145. }
  1146. CDestinationNodeInfo::CDestinationNodeInfo()
  1147. : PathNodeInfo(),
  1148. blocked(false),
  1149. action(CGPathNode::ENodeAction::UNKNOWN)
  1150. {
  1151. }
  1152. void CDestinationNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject)
  1153. {
  1154. PathNodeInfo::setNode(gs, n, excludeTopObject);
  1155. blocked = false;
  1156. action = CGPathNode::ENodeAction::UNKNOWN;
  1157. }
  1158. bool CDestinationNodeInfo::isBetterWay() const
  1159. {
  1160. if(node->turns == 0xff) //we haven't been here before
  1161. return true;
  1162. else
  1163. return cost < node->getCost(); //this route is faster
  1164. }
  1165. bool PathNodeInfo::isNodeObjectVisitable() const
  1166. {
  1167. /// Hero can't visit objects while walking on water or flying
  1168. return canSeeObj(nodeObject) && (node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL);
  1169. }