PathfindingRules.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*
  2. * PathfindingRules.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 "PathfindingRules.h"
  12. #include "CGPathNode.h"
  13. #include "CPathfinder.h"
  14. #include "INodeStorage.h"
  15. #include "PathfinderOptions.h"
  16. #include "../mapObjects/CGHeroInstance.h"
  17. #include "../mapObjects/MiscObjects.h"
  18. #include "../mapping/CMapDefines.h"
  19. VCMI_LIB_NAMESPACE_BEGIN
  20. void MovementCostRule::process(
  21. const PathNodeInfo & source,
  22. CDestinationNodeInfo & destination,
  23. const PathfinderConfig * pathfinderConfig,
  24. CPathfinderHelper * pathfinderHelper) const
  25. {
  26. float costAtNextTile = destination.cost;
  27. int turnAtNextTile = destination.turn;
  28. int moveAtNextTile = destination.movementLeft;
  29. int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
  30. int remains = moveAtNextTile - cost;
  31. int sourceLayerMaxMovePoints = pathfinderHelper->getMaxMovePoints(source.node->layer);
  32. if(remains < 0)
  33. {
  34. //occurs rarely, when hero with low movepoints tries to leave the road
  35. costAtNextTile += static_cast<float>(moveAtNextTile) / sourceLayerMaxMovePoints;//we spent all points of current turn
  36. pathfinderHelper->updateTurnInfo(++turnAtNextTile);
  37. int destinationLayerMaxMovePoints = pathfinderHelper->getMaxMovePoints(destination.node->layer);
  38. moveAtNextTile = destinationLayerMaxMovePoints;
  39. cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
  40. remains = moveAtNextTile - cost;
  41. }
  42. if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
  43. {
  44. /// FREE_SHIP_BOARDING bonus only remove additional penalty
  45. /// land <-> sail transition still cost movement points as normal movement
  46. remains = pathfinderHelper->movementPointsAfterEmbark(moveAtNextTile, cost, (destination.action == CGPathNode::DISEMBARK));
  47. cost = moveAtNextTile - remains;
  48. }
  49. costAtNextTile += static_cast<float>(cost) / sourceLayerMaxMovePoints;
  50. destination.cost = costAtNextTile;
  51. destination.turn = turnAtNextTile;
  52. destination.movementLeft = remains;
  53. if(destination.isBetterWay() &&
  54. ((source.node->turns == turnAtNextTile && remains) || pathfinderHelper->passOneTurnLimitCheck(source)))
  55. {
  56. pathfinderConfig->nodeStorage->commit(destination, source);
  57. return;
  58. }
  59. destination.blocked = true;
  60. }
  61. void PathfinderBlockingRule::process(
  62. const PathNodeInfo & source,
  63. CDestinationNodeInfo & destination,
  64. const PathfinderConfig * pathfinderConfig,
  65. CPathfinderHelper * pathfinderHelper) const
  66. {
  67. auto blockingReason = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
  68. destination.blocked = blockingReason != BlockingReason::NONE;
  69. }
  70. void DestinationActionRule::process(
  71. const PathNodeInfo & source,
  72. CDestinationNodeInfo & destination,
  73. const PathfinderConfig * pathfinderConfig,
  74. CPathfinderHelper * pathfinderHelper) const
  75. {
  76. if(destination.action != CGPathNode::ENodeAction::UNKNOWN)
  77. {
  78. #ifdef VCMI_TRACE_PATHFINDER
  79. logAi->trace("Accepted precalculated action at %s", destination.coord.toString());
  80. #endif
  81. return;
  82. }
  83. CGPathNode::ENodeAction action = CGPathNode::NORMAL;
  84. const auto * hero = pathfinderHelper->hero;
  85. switch(destination.node->layer)
  86. {
  87. case EPathfindingLayer::LAND:
  88. if(source.node->layer == EPathfindingLayer::SAIL)
  89. {
  90. // TODO: Handle dismebark into guarded areaa
  91. action = CGPathNode::DISEMBARK;
  92. break;
  93. }
  94. /// don't break - next case shared for both land and sail layers
  95. [[fallthrough]];
  96. case EPathfindingLayer::SAIL:
  97. if(destination.isNodeObjectVisitable())
  98. {
  99. auto objRel = destination.objectRelations;
  100. if(destination.nodeObject->ID == Obj::BOAT)
  101. action = CGPathNode::EMBARK;
  102. else if(destination.nodeHero)
  103. {
  104. if(destination.heroRelations == PlayerRelations::ENEMIES)
  105. action = CGPathNode::BATTLE;
  106. else
  107. action = CGPathNode::BLOCKING_VISIT;
  108. }
  109. else if(destination.nodeObject->ID == Obj::TOWN)
  110. {
  111. if(destination.nodeObject->passableFor(hero->tempOwner))
  112. action = CGPathNode::VISIT;
  113. else if(objRel == PlayerRelations::ENEMIES)
  114. action = CGPathNode::BATTLE;
  115. }
  116. else if(destination.nodeObject->ID == Obj::GARRISON || destination.nodeObject->ID == Obj::GARRISON2)
  117. {
  118. if(destination.nodeObject->passableFor(hero->tempOwner))
  119. {
  120. if(destination.guarded)
  121. action = CGPathNode::BATTLE;
  122. }
  123. else if(objRel == PlayerRelations::ENEMIES)
  124. action = CGPathNode::BATTLE;
  125. }
  126. else if(destination.nodeObject->ID == Obj::BORDER_GATE)
  127. {
  128. if(destination.nodeObject->passableFor(hero->tempOwner))
  129. {
  130. if(destination.guarded)
  131. action = CGPathNode::BATTLE;
  132. }
  133. else
  134. action = CGPathNode::BLOCKING_VISIT;
  135. }
  136. else if(destination.isGuardianTile)
  137. action = CGPathNode::BATTLE;
  138. else if(destination.nodeObject->blockVisit && !(pathfinderConfig->options.useCastleGate && destination.nodeObject->ID == Obj::TOWN))
  139. action = CGPathNode::BLOCKING_VISIT;
  140. if(action == CGPathNode::NORMAL)
  141. {
  142. if(destination.guarded)
  143. action = CGPathNode::BATTLE;
  144. else
  145. action = CGPathNode::VISIT;
  146. }
  147. }
  148. else if(destination.guarded)
  149. action = CGPathNode::BATTLE;
  150. break;
  151. }
  152. destination.action = action;
  153. }
  154. void MovementAfterDestinationRule::process(
  155. const PathNodeInfo & source,
  156. CDestinationNodeInfo & destination,
  157. const PathfinderConfig * config,
  158. CPathfinderHelper * pathfinderHelper) const
  159. {
  160. auto blocker = getBlockingReason(source, destination, config, pathfinderHelper);
  161. if(blocker == BlockingReason::DESTINATION_GUARDED && destination.action == CGPathNode::ENodeAction::BATTLE)
  162. {
  163. return; // allow bypass guarded tile but only in direction of guard, a bit UI related thing
  164. }
  165. destination.blocked = blocker != BlockingReason::NONE;
  166. }
  167. PathfinderBlockingRule::BlockingReason MovementAfterDestinationRule::getBlockingReason(
  168. const PathNodeInfo & source,
  169. const CDestinationNodeInfo & destination,
  170. const PathfinderConfig * config,
  171. const CPathfinderHelper * pathfinderHelper) const
  172. {
  173. switch(destination.action)
  174. {
  175. /// TODO: Investigate what kind of limitation is possible to apply on movement from visitable tiles
  176. /// Likely in many cases we don't need to add visitable tile to queue when hero doesn't fly
  177. case CGPathNode::VISIT:
  178. {
  179. /// For now we only add visitable tile into queue when it's teleporter that allow transit
  180. /// Movement from visitable tile when hero is standing on it is possible into any layer
  181. const auto * objTeleport = dynamic_cast<const CGTeleport *>(destination.nodeObject);
  182. if(pathfinderHelper->isAllowedTeleportEntrance(objTeleport))
  183. {
  184. /// For now we'll always allow transit over teleporters
  185. /// Transit over whirlpools only allowed when hero is protected
  186. return BlockingReason::NONE;
  187. }
  188. else if(destination.nodeObject->ID == Obj::GARRISON
  189. || destination.nodeObject->ID == Obj::GARRISON2
  190. || destination.nodeObject->ID == Obj::BORDER_GATE)
  191. {
  192. /// Transit via unguarded garrisons is always possible
  193. return BlockingReason::NONE;
  194. }
  195. return BlockingReason::DESTINATION_VISIT;
  196. }
  197. case CGPathNode::BLOCKING_VISIT:
  198. return destination.guarded
  199. ? BlockingReason::DESTINATION_GUARDED
  200. : BlockingReason::DESTINATION_BLOCKVIS;
  201. case CGPathNode::NORMAL:
  202. return BlockingReason::NONE;
  203. case CGPathNode::EMBARK:
  204. if(pathfinderHelper->options.useEmbarkAndDisembark)
  205. return BlockingReason::NONE;
  206. return BlockingReason::DESTINATION_BLOCKED;
  207. case CGPathNode::DISEMBARK:
  208. if(pathfinderHelper->options.useEmbarkAndDisembark)
  209. return destination.guarded ? BlockingReason::DESTINATION_GUARDED : BlockingReason::NONE;
  210. return BlockingReason::DESTINATION_BLOCKED;
  211. case CGPathNode::BATTLE:
  212. /// Movement after BATTLE action only possible from guarded tile to guardian tile
  213. if(destination.guarded)
  214. return BlockingReason::DESTINATION_GUARDED;
  215. break;
  216. }
  217. return BlockingReason::DESTINATION_BLOCKED;
  218. }
  219. PathfinderBlockingRule::BlockingReason MovementToDestinationRule::getBlockingReason(
  220. const PathNodeInfo & source,
  221. const CDestinationNodeInfo & destination,
  222. const PathfinderConfig * pathfinderConfig,
  223. const CPathfinderHelper * pathfinderHelper) const
  224. {
  225. if(destination.node->accessible == CGPathNode::BLOCKED)
  226. return BlockingReason::DESTINATION_BLOCKED;
  227. switch(destination.node->layer)
  228. {
  229. case EPathfindingLayer::LAND:
  230. if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord))
  231. return BlockingReason::DESTINATION_BLOCKED;
  232. if(source.guarded)
  233. {
  234. if(!(pathfinderConfig->options.originalMovementRules && source.node->layer == EPathfindingLayer::AIR) &&
  235. !destination.isGuardianTile) // Can step into tile of guard
  236. {
  237. return BlockingReason::SOURCE_GUARDED;
  238. }
  239. }
  240. break;
  241. case EPathfindingLayer::SAIL:
  242. if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord))
  243. return BlockingReason::DESTINATION_BLOCKED;
  244. if(source.guarded)
  245. {
  246. // Hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
  247. if(source.node->action != CGPathNode::EMBARK && !destination.isGuardianTile)
  248. return BlockingReason::SOURCE_GUARDED;
  249. }
  250. if(source.node->layer == EPathfindingLayer::LAND)
  251. {
  252. if(!destination.isNodeObjectVisitable())
  253. return BlockingReason::DESTINATION_BLOCKED;
  254. if(destination.nodeObject->ID != Obj::BOAT && !destination.nodeHero)
  255. return BlockingReason::DESTINATION_BLOCKED;
  256. }
  257. else if(destination.isNodeObjectVisitable() && destination.nodeObject->ID == Obj::BOAT)
  258. {
  259. /// Hero in boat can't visit empty boats
  260. return BlockingReason::DESTINATION_BLOCKED;
  261. }
  262. break;
  263. case EPathfindingLayer::WATER:
  264. if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord)
  265. || destination.node->accessible != CGPathNode::ACCESSIBLE)
  266. {
  267. return BlockingReason::DESTINATION_BLOCKED;
  268. }
  269. if(destination.guarded)
  270. return BlockingReason::DESTINATION_BLOCKED;
  271. break;
  272. }
  273. return BlockingReason::NONE;
  274. }
  275. void LayerTransitionRule::process(
  276. const PathNodeInfo & source,
  277. CDestinationNodeInfo & destination,
  278. const PathfinderConfig * pathfinderConfig,
  279. CPathfinderHelper * pathfinderHelper) const
  280. {
  281. if(source.node->layer == destination.node->layer)
  282. return;
  283. switch(source.node->layer)
  284. {
  285. case EPathfindingLayer::LAND:
  286. if(destination.node->layer == EPathfindingLayer::SAIL)
  287. {
  288. /// Cannot enter empty water tile from land -> it has to be visitable
  289. if(destination.node->accessible == CGPathNode::ACCESSIBLE)
  290. destination.blocked = true;
  291. }
  292. break;
  293. case EPathfindingLayer::SAIL:
  294. //tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast
  295. if((destination.node->accessible != CGPathNode::ACCESSIBLE && (destination.node->accessible != CGPathNode::BLOCKVIS || destination.tile->blocked))
  296. || destination.tile->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate
  297. {
  298. destination.blocked = true;
  299. }
  300. break;
  301. case EPathfindingLayer::AIR:
  302. if(pathfinderConfig->options.originalMovementRules)
  303. {
  304. if((source.node->accessible != CGPathNode::ACCESSIBLE &&
  305. source.node->accessible != CGPathNode::VISITABLE) &&
  306. (destination.node->accessible != CGPathNode::VISITABLE &&
  307. destination.node->accessible != CGPathNode::ACCESSIBLE))
  308. {
  309. destination.blocked = true;
  310. }
  311. }
  312. else if(destination.node->accessible != CGPathNode::ACCESSIBLE)
  313. {
  314. /// Hero that fly can only land on accessible tiles
  315. if(destination.nodeObject)
  316. destination.blocked = true;
  317. }
  318. break;
  319. case EPathfindingLayer::WATER:
  320. if(destination.node->accessible != CGPathNode::ACCESSIBLE && destination.node->accessible != CGPathNode::VISITABLE)
  321. {
  322. /// Hero that walking on water can transit to accessible and visitable tiles
  323. /// Though hero can't interact with blocking visit objects while standing on water
  324. destination.blocked = true;
  325. }
  326. break;
  327. }
  328. }
  329. VCMI_LIB_NAMESPACE_END