CPathfinder.cpp 36 KB

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