CPathfinder.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. #include "StdInc.h"
  2. #include "CPathfinder.h"
  3. #include "CHeroHandler.h"
  4. #include "mapping/CMap.h"
  5. #include "CGameState.h"
  6. #include "mapObjects/CGHeroInstance.h"
  7. #include "GameConstants.h"
  8. #include "CStopWatch.h"
  9. /*
  10. * CPathfinder.cpp, part of VCMI engine
  11. *
  12. * Authors: listed in file AUTHORS in main folder
  13. *
  14. * License: GNU General Public License v2.0 or later
  15. * Full text of license available in license.txt file, in main folder
  16. *
  17. */
  18. CPathfinder::PathfinderOptions::PathfinderOptions()
  19. {
  20. useFlying = false;
  21. useWaterWalking = false;
  22. useEmbarkAndDisembark = true;
  23. useTeleportTwoWay = true;
  24. useTeleportOneWay = true;
  25. useTeleportOneWayRandom = false;
  26. useTeleportWhirlpool = false;
  27. }
  28. CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
  29. {
  30. assert(hero);
  31. assert(hero == getHero(hero->id));
  32. out.hero = hero;
  33. out.hpos = hero->getPosition(false);
  34. if(!gs->map->isInTheMap(out.hpos)/* || !gs->map->isInTheMap(dest)*/) //check input
  35. {
  36. logGlobal->errorStream() << "CGameState::calculatePaths: Hero outside the gs->map? How dare you...";
  37. throw std::runtime_error("Wrong checksum");
  38. }
  39. initializeGraph();
  40. if(hero->canFly())
  41. options.useFlying = true;
  42. if(hero->canWalkOnSea())
  43. options.useWaterWalking = true;
  44. if(CGWhirlpool::isProtected(hero))
  45. options.useTeleportWhirlpool = true;
  46. neighbours.reserve(16);
  47. }
  48. void CPathfinder::calculatePaths()
  49. {
  50. int maxMovePointsLand = hero->maxMovePoints(true);
  51. int maxMovePointsWater = hero->maxMovePoints(false);
  52. auto maxMovePoints = [&](CGPathNode *cp) -> int
  53. {
  54. return cp->land ? maxMovePointsLand : maxMovePointsWater;
  55. };
  56. auto isBetterWay = [&](int remains, int turn) -> bool
  57. {
  58. if(dp->turns == 0xff) //we haven't been here before
  59. return true;
  60. else if(dp->turns > turn)
  61. return true;
  62. else if(dp->turns >= turn && dp->moveRemains < remains) //this route is faster
  63. return true;
  64. return false;
  65. };
  66. //logGlobal->infoStream() << boost::format("Calculating paths for hero %s (adress %d) of player %d") % hero->name % hero % hero->tempOwner;
  67. //initial tile - set cost on 0 and add to the queue
  68. CGPathNode &initialNode = *getNode(out.hpos);
  69. initialNode.turns = 0;
  70. initialNode.moveRemains = hero->movement;
  71. mq.push_back(&initialNode);
  72. while(!mq.empty())
  73. {
  74. cp = mq.front();
  75. mq.pop_front();
  76. int movement = cp->moveRemains, turn = cp->turns;
  77. if(!movement)
  78. {
  79. movement = maxMovePoints(cp);
  80. turn++;
  81. }
  82. //add accessible neighbouring nodes to the queue
  83. addNeighbours(cp->coord);
  84. for(auto & neighbour : neighbours)
  85. {
  86. dp = getNode(neighbour);
  87. dt = &gs->map->getTile(neighbour);
  88. useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark
  89. if(!isMovementPossible())
  90. continue;
  91. int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement);
  92. int remains = movement - cost;
  93. if(useEmbarkCost)
  94. {
  95. remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1);
  96. cost = movement - remains;
  97. }
  98. int turnAtNextTile = turn;
  99. if(remains < 0)
  100. {
  101. //occurs rarely, when hero with low movepoints tries to leave the road
  102. turnAtNextTile++;
  103. int moveAtNextTile = maxMovePoints(cp);
  104. cost = gs->getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :(
  105. remains = moveAtNextTile - cost;
  106. }
  107. if(isBetterWay(remains, turnAtNextTile))
  108. {
  109. assert(dp != cp->theNodeBefore); //two tiles can't point to each other
  110. dp->moveRemains = remains;
  111. dp->turns = turnAtNextTile;
  112. dp->theNodeBefore = cp;
  113. if(checkDestinationTile())
  114. mq.push_back(dp);
  115. }
  116. } //neighbours loop
  117. //just add all passable teleport exits
  118. if(sTileObj)
  119. {
  120. addTeleportExits();
  121. for(auto & neighbour : neighbours)
  122. {
  123. dp = getNode(neighbour);
  124. if(isBetterWay(movement, turn))
  125. {
  126. dp->moveRemains = movement;
  127. dp->turns = turn;
  128. dp->theNodeBefore = cp;
  129. mq.push_back(dp);
  130. }
  131. }
  132. }
  133. } //queue loop
  134. }
  135. void CPathfinder::addNeighbours(const int3 &coord)
  136. {
  137. neighbours.clear();
  138. ct = &gs->map->getTile(coord);
  139. std::vector<int3> tiles;
  140. gs->getNeighbours(*ct, coord, tiles, boost::logic::indeterminate, !cp->land);
  141. sTileObj = ct->topVisitableObj(coord == CGHeroInstance::convertPosition(hero->pos, false));
  142. if(sTileObj)
  143. {
  144. for(int3 tile: tiles)
  145. {
  146. if(canMoveBetween(tile, sTileObj->visitablePos()))
  147. neighbours.push_back(tile);
  148. }
  149. }
  150. else
  151. vstd::concatenate(neighbours, tiles);
  152. }
  153. void CPathfinder::addTeleportExits(bool noTeleportExcludes)
  154. {
  155. assert(sTileObj);
  156. neighbours.clear();
  157. auto isAllowedTeleportEntrance = [&](const CGTeleport * obj) -> bool
  158. {
  159. if(!gs->isTeleportEntrancePassable(obj, hero->tempOwner))
  160. return false;
  161. if(noTeleportExcludes)
  162. return true;
  163. auto whirlpool = dynamic_cast<const CGWhirlpool *>(obj);
  164. if(whirlpool)
  165. {
  166. if(addTeleportWhirlpool(whirlpool))
  167. return true;
  168. }
  169. else if(addTeleportTwoWay(obj) || addTeleportOneWay(obj) || addTeleportOneWayRandom(obj))
  170. return true;
  171. return false;
  172. };
  173. const CGTeleport *sTileTeleport = dynamic_cast<const CGTeleport *>(sTileObj);
  174. if(isAllowedTeleportEntrance(sTileTeleport))
  175. {
  176. for(auto objId : gs->getTeleportChannelExits(sTileTeleport->channel, hero->tempOwner))
  177. {
  178. auto obj = getObj(objId);
  179. if(CGTeleport::isExitPassable(gs, hero, obj))
  180. neighbours.push_back(obj->visitablePos());
  181. }
  182. }
  183. }
  184. bool CPathfinder::isMovementPossible()
  185. {
  186. if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED)
  187. return false;
  188. Obj destTopVisObjID = dt->topVisitableId();
  189. if(cp->land != dp->land) //hero can traverse land<->sea only in special circumstances
  190. {
  191. if(cp->land) //from land to sea -> embark or assault hero on boat
  192. {
  193. if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable
  194. return false;
  195. if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land
  196. return false;
  197. if(destTopVisObjID == Obj::BOAT)
  198. useEmbarkCost = 1;
  199. }
  200. else //disembark
  201. {
  202. //can disembark only on coastal tiles
  203. if(!dt->isCoastal())
  204. return false;
  205. //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast
  206. if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked))
  207. || dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate
  208. return false;;
  209. useEmbarkCost = 2;
  210. }
  211. }
  212. if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard
  213. return false;
  214. return true;
  215. }
  216. bool CPathfinder::checkDestinationTile()
  217. {
  218. if(dp->accessible == CGPathNode::ACCESSIBLE)
  219. return true;
  220. if(dp->coord == CGHeroInstance::convertPosition(hero->pos, false))
  221. return true; // This one is tricky, we can ignore fact that tile is not ACCESSIBLE in case if it's our hero block it. Though this need investigation
  222. if(dp->accessible == CGPathNode::VISITABLE && CGTeleport::isTeleport(dt->topVisitableObj()))
  223. return true; // For now we'll always allow transit for teleporters
  224. if(useEmbarkCost && options.useEmbarkAndDisembark)
  225. return true;
  226. if(isDestinationGuarded() && !isSourceGuarded())
  227. return true; // Can step into a hostile tile once
  228. return false;
  229. }
  230. int3 CPathfinder::getSourceGuardPosition()
  231. {
  232. return gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z];
  233. }
  234. bool CPathfinder::isSourceGuarded()
  235. {
  236. //map can start with hero on guarded tile or teleport there using dimension door
  237. //so threat tile hero standing on like it's not guarded because it's should be possible to move out of here
  238. if(getSourceGuardPosition() != int3(-1, -1, -1)
  239. && cp->coord != hero->getPosition(false))
  240. {
  241. //special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
  242. if(cp->accessible != CGPathNode::VISITABLE
  243. || !cp->theNodeBefore->land
  244. || ct->topVisitableId() != Obj::BOAT)
  245. {
  246. return true;
  247. }
  248. }
  249. return false;
  250. }
  251. bool CPathfinder::isDestinationGuarded()
  252. {
  253. if(gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid()
  254. && dp->accessible == CGPathNode::BLOCKVIS)
  255. {
  256. return true;
  257. }
  258. return false;
  259. }
  260. bool CPathfinder::isDestinationGuardian()
  261. {
  262. return getSourceGuardPosition() == dp->coord;
  263. }
  264. void CPathfinder::initializeGraph()
  265. {
  266. int3 pos;
  267. CGPathNode ***graph = out.nodes;
  268. for(pos.x=0; pos.x < out.sizes.x; ++pos.x)
  269. {
  270. for(pos.y=0; pos.y < out.sizes.y; ++pos.y)
  271. {
  272. for(pos.z=0; pos.z < out.sizes.z; ++pos.z)
  273. {
  274. const TerrainTile *tinfo = &gs->map->getTile(pos);
  275. CGPathNode &node = graph[pos.x][pos.y][pos.z];
  276. node.accessible = evaluateAccessibility(pos, tinfo);
  277. node.turns = 0xff;
  278. node.moveRemains = 0;
  279. node.coord = pos;
  280. node.land = tinfo->terType != ETerrainType::WATER;
  281. node.theNodeBefore = nullptr;
  282. }
  283. }
  284. }
  285. }
  286. CGPathNode *CPathfinder::getNode(const int3 &coord)
  287. {
  288. return &out.nodes[coord.x][coord.y][coord.z];
  289. }
  290. CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 &pos, const TerrainTile *tinfo) const
  291. {
  292. CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE);
  293. if(tinfo->terType == ETerrainType::ROCK || !FoW[pos.x][pos.y][pos.z])
  294. return CGPathNode::BLOCKED;
  295. if(tinfo->visitable)
  296. {
  297. 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
  298. {
  299. return CGPathNode::BLOCKED;
  300. }
  301. else
  302. {
  303. for(const CGObjectInstance *obj : tinfo->visitableObjects)
  304. {
  305. if(obj->passableFor(hero->tempOwner))
  306. {
  307. ret = CGPathNode::ACCESSIBLE;
  308. }
  309. else if(obj->blockVisit)
  310. {
  311. return CGPathNode::BLOCKVIS;
  312. }
  313. else if(obj->ID != Obj::EVENT) //pathfinder should ignore placed events
  314. {
  315. ret = CGPathNode::VISITABLE;
  316. }
  317. }
  318. }
  319. }
  320. else if(gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z].valid()
  321. && !tinfo->blocked)
  322. {
  323. // Monster close by; blocked visit for battle.
  324. return CGPathNode::BLOCKVIS;
  325. }
  326. return ret;
  327. }
  328. bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const
  329. {
  330. return gs->checkForVisitableDir(a, b);
  331. }
  332. bool CPathfinder::addTeleportTwoWay(const CGTeleport * obj) const
  333. {
  334. return options.useTeleportTwoWay && gs->isTeleportChannelBidirectional(obj->channel, hero->tempOwner);
  335. }
  336. bool CPathfinder::addTeleportOneWay(const CGTeleport * obj) const
  337. {
  338. if(options.useTeleportOneWay && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
  339. {
  340. auto passableExits = CGTeleport::getPassableExits(gs, hero, gs->getTeleportChannelExits(obj->channel, hero->tempOwner));
  341. if(passableExits.size() == 1)
  342. return true;
  343. }
  344. return false;
  345. }
  346. bool CPathfinder::addTeleportOneWayRandom(const CGTeleport * obj) const
  347. {
  348. if(options.useTeleportOneWayRandom && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
  349. {
  350. auto passableExits = CGTeleport::getPassableExits(gs, hero, gs->getTeleportChannelExits(obj->channel, hero->tempOwner));
  351. if(passableExits.size() > 1)
  352. return true;
  353. }
  354. return false;
  355. }
  356. bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const
  357. {
  358. return options.useTeleportWhirlpool && obj;
  359. }
  360. CGPathNode::CGPathNode()
  361. :coord(-1,-1,-1)
  362. {
  363. accessible = NOT_SET;
  364. land = 0;
  365. moveRemains = 0;
  366. turns = 255;
  367. theNodeBefore = nullptr;
  368. }
  369. bool CGPathNode::reachable() const
  370. {
  371. return turns < 255;
  372. }
  373. int3 CGPath::startPos() const
  374. {
  375. return nodes[nodes.size()-1].coord;
  376. }
  377. int3 CGPath::endPos() const
  378. {
  379. return nodes[0].coord;
  380. }
  381. void CGPath::convert( ui8 mode )
  382. {
  383. if(mode==0)
  384. {
  385. for(auto & elem : nodes)
  386. {
  387. elem.coord = CGHeroInstance::convertPosition(elem.coord,true);
  388. }
  389. }
  390. }
  391. CPathsInfo::CPathsInfo( const int3 &Sizes )
  392. :sizes(Sizes)
  393. {
  394. hero = nullptr;
  395. nodes = new CGPathNode**[sizes.x];
  396. for(int i = 0; i < sizes.x; i++)
  397. {
  398. nodes[i] = new CGPathNode*[sizes.y];
  399. for(int j = 0; j < sizes.y; j++)
  400. {
  401. nodes[i][j] = new CGPathNode[sizes.z];
  402. }
  403. }
  404. }
  405. CPathsInfo::~CPathsInfo()
  406. {
  407. for(int i = 0; i < sizes.x; i++)
  408. {
  409. for(int j = 0; j < sizes.y; j++)
  410. {
  411. delete [] nodes[i][j];
  412. }
  413. delete [] nodes[i];
  414. }
  415. delete [] nodes;
  416. }
  417. const CGPathNode * CPathsInfo::getPathInfo( int3 tile ) const
  418. {
  419. boost::unique_lock<boost::mutex> pathLock(pathMx);
  420. if(tile.x >= sizes.x || tile.y >= sizes.y || tile.z >= sizes.z)
  421. return nullptr;
  422. return &nodes[tile.x][tile.y][tile.z];
  423. }
  424. bool CPathsInfo::getPath( const int3 &dst, CGPath &out ) const
  425. {
  426. boost::unique_lock<boost::mutex> pathLock(pathMx);
  427. out.nodes.clear();
  428. const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z];
  429. if(!curnode->theNodeBefore)
  430. return false;
  431. while(curnode)
  432. {
  433. CGPathNode cpn = *curnode;
  434. curnode = curnode->theNodeBefore;
  435. out.nodes.push_back(cpn);
  436. }
  437. return true;
  438. }
  439. int CPathsInfo::getDistance( int3 tile ) const
  440. {
  441. boost::unique_lock<boost::mutex> pathLock(pathMx);
  442. CGPath ret;
  443. if(getPath(tile, ret))
  444. return ret.nodes.size();
  445. else
  446. return 255;
  447. }