PathfindingRules.cpp 13 KB

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