AIMovementAfterDestinationRule.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * AIMovementAfterDestinationRule.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 "AIMovementAfterDestinationRule.h"
  12. #include "../Actions/BattleAction.h"
  13. #include "../../Goals/Invalid.h"
  14. namespace AIPathfinding
  15. {
  16. class QuestAction : public ISpecialAction
  17. {
  18. public:
  19. QuestAction(QuestInfo questInfo)
  20. {
  21. }
  22. virtual bool canAct(const CGHeroInstance * hero) const override
  23. {
  24. return false;
  25. }
  26. virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override
  27. {
  28. return Goals::sptr(Goals::Invalid());
  29. }
  30. };
  31. AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
  32. CPlayerSpecificInfoCallback * cb,
  33. std::shared_ptr<AINodeStorage> nodeStorage)
  34. :cb(cb), nodeStorage(nodeStorage)
  35. {
  36. }
  37. void AIMovementAfterDestinationRule::process(
  38. const PathNodeInfo & source,
  39. CDestinationNodeInfo & destination,
  40. const PathfinderConfig * pathfinderConfig,
  41. CPathfinderHelper * pathfinderHelper) const
  42. {
  43. if(nodeStorage->isMovementIneficient(source, destination))
  44. {
  45. destination.blocked = true;
  46. return;
  47. }
  48. auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
  49. if(blocker == BlockingReason::NONE)
  50. return;
  51. if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject)
  52. {
  53. auto enemyHero = destination.nodeHero && destination.heroRelations == PlayerRelations::ENEMIES;
  54. if(!enemyHero && !isObjectRemovable(destination.nodeObject))
  55. {
  56. if(nodeStorage->getHero(destination.node) == destination.nodeHero)
  57. return;
  58. destination.blocked = true;
  59. }
  60. if(destination.nodeObject->ID == Obj::QUEST_GUARD || destination.nodeObject->ID == Obj::BORDERGUARD)
  61. {
  62. auto questObj = dynamic_cast<const IQuestObject *>(destination.nodeObject);
  63. auto nodeHero = pathfinderHelper->hero;
  64. if(!destination.nodeObject->wasVisited(nodeHero->tempOwner)
  65. || !questObj->checkQuest(nodeHero))
  66. {
  67. nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
  68. {
  69. auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord);
  70. node->specialAction.reset(new QuestAction(questInfo));
  71. });
  72. }
  73. }
  74. return;
  75. }
  76. if(blocker == BlockingReason::DESTINATION_VISIT)
  77. {
  78. return;
  79. }
  80. if(blocker == BlockingReason::DESTINATION_GUARDED)
  81. {
  82. auto srcGuardians = cb->getGuardingCreatures(source.coord);
  83. auto destGuardians = cb->getGuardingCreatures(destination.coord);
  84. if(destGuardians.empty())
  85. {
  86. destination.blocked = true;
  87. return;
  88. }
  89. vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool
  90. {
  91. return vstd::contains(srcGuardians, destGuard);
  92. });
  93. auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
  94. auto srcNode = nodeStorage->getAINode(source.node);
  95. if(guardsAlreadyBypassed && srcNode->actor->allowBattle)
  96. {
  97. #ifdef VCMI_TRACE_PATHFINDER
  98. logAi->trace(
  99. "Bypass guard at destination while moving %s -> %s",
  100. source.coord.toString(),
  101. destination.coord.toString());
  102. #endif
  103. return;
  104. }
  105. const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
  106. auto battleNodeOptional = nodeStorage->getOrCreateNode(
  107. destination.coord,
  108. destination.node->layer,
  109. destNode->actor->battleActor);
  110. if(!battleNodeOptional)
  111. {
  112. #ifdef VCMI_TRACE_PATHFINDER
  113. logAi->trace(
  114. "Can not allocate battle node while moving %s -> %s",
  115. source.coord.toString(),
  116. destination.coord.toString());
  117. #endif
  118. destination.blocked = true;
  119. return;
  120. }
  121. AIPathNode * battleNode = battleNodeOptional.get();
  122. if(battleNode->locked)
  123. {
  124. #ifdef VCMI_TRACE_PATHFINDER
  125. logAi->trace(
  126. "Block bypass guard at destination while moving %s -> %s",
  127. source.coord.toString(),
  128. destination.coord.toString());
  129. #endif
  130. destination.blocked = true;
  131. return;
  132. }
  133. auto hero = nodeStorage->getHero(source.node);
  134. auto danger = nodeStorage->evaluateDanger(destination.coord, hero);
  135. double actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
  136. double loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
  137. if(loss < actualArmyValue)
  138. {
  139. destination.node = battleNode;
  140. nodeStorage->commit(destination, source);
  141. battleNode->armyLoss += loss;
  142. vstd::amax(battleNode->danger, danger);
  143. battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
  144. if(source.nodeObject && isObjectRemovable(source.nodeObject))
  145. {
  146. battleNode->theNodeBefore = source.node;
  147. }
  148. #ifdef VCMI_TRACE_PATHFINDER
  149. logAi->trace(
  150. "Begin bypass guard at destination with danger %s while moving %s -> %s",
  151. std::to_string(danger),
  152. source.coord.toString(),
  153. destination.coord.toString());
  154. #endif
  155. return;
  156. }
  157. }
  158. destination.blocked = true;
  159. }
  160. }