ObjectClusterizer.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * DangerHitMapAnalyzer.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 "ObjectClusterizer.h"
  12. #include "../Goals/ExecuteHeroChain.h"
  13. #include "../VCAI.h"
  14. #include "../Engine/Nullkiller.h"
  15. #include "lib/mapping/CMap.h" //for victory conditions
  16. extern boost::thread_specific_ptr<CCallback> cb;
  17. extern boost::thread_specific_ptr<VCAI> ai;
  18. void ObjectCluster::addObject(const CGObjectInstance * obj, const AIPath & path, float priority)
  19. {
  20. auto & info = objects[obj];
  21. if(info.priority < priority)
  22. {
  23. info.priority = priority;
  24. info.movementCost = path.movementCost() - path.firstNode().cost;
  25. info.danger = path.targetObjectDanger;
  26. info.turn = path.turn();
  27. }
  28. }
  29. const CGObjectInstance * ObjectCluster::calculateCenter() const
  30. {
  31. auto v = getObjects();
  32. auto tile = int3(0);
  33. float priority = 0;
  34. for(auto pair : objects)
  35. {
  36. auto newPoint = pair.first->visitablePos();
  37. float newPriority = std::pow(pair.second.priority, 4); // lets make high priority targets more weghtful
  38. int3 direction = newPoint - tile;
  39. float priorityRatio = newPriority / (priority + newPriority);
  40. tile += direction * priorityRatio;
  41. priority += newPriority;
  42. }
  43. auto closestPair = *vstd::minElementByFun(objects, [&](const std::pair<const CGObjectInstance *, ObjectInfo> & pair) -> int
  44. {
  45. return pair.first->visitablePos().dist2dSQ(tile);
  46. });
  47. return closestPair.first;
  48. }
  49. std::vector<const CGObjectInstance *> ObjectCluster::getObjects() const
  50. {
  51. std::vector<const CGObjectInstance *> result;
  52. for(auto pair : objects)
  53. {
  54. result.push_back(pair.first);
  55. }
  56. return result;
  57. }
  58. std::vector<const CGObjectInstance *> ObjectClusterizer::getNearbyObjects() const
  59. {
  60. return nearObjects.getObjects();
  61. }
  62. std::vector<std::shared_ptr<ObjectCluster>> ObjectClusterizer::getLockedClusters() const
  63. {
  64. std::vector<std::shared_ptr<ObjectCluster>> result;
  65. for(auto pair : blockedObjects)
  66. {
  67. result.push_back(pair.second);
  68. }
  69. return result;
  70. }
  71. const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) const
  72. {
  73. for(auto node = path.nodes.rbegin(); node != path.nodes.rend(); node++)
  74. {
  75. auto guardPos = cb->getGuardingCreaturePosition(node->coord);
  76. auto blockers = cb->getVisitableObjs(node->coord);
  77. if(guardPos.valid())
  78. {
  79. auto guard = cb->getTopObj(cb->getGuardingCreaturePosition(node->coord));
  80. if(guard)
  81. {
  82. blockers.insert(blockers.begin(), guard);
  83. }
  84. }
  85. if(blockers.empty())
  86. continue;
  87. auto blocker = blockers.front();
  88. if(blocker->ID == Obj::GARRISON
  89. || blocker->ID == Obj::MONSTER
  90. || blocker->ID == Obj::GARRISON2
  91. || blocker->ID == Obj::BORDERGUARD
  92. || blocker->ID == Obj::QUEST_GUARD
  93. || blocker->ID == Obj::BORDER_GATE)
  94. {
  95. return blocker;
  96. }
  97. }
  98. return nullptr;
  99. }
  100. bool shouldVisitObject(const CGObjectInstance * obj)
  101. {
  102. if(isObjectRemovable(obj))
  103. {
  104. return true;
  105. }
  106. const int3 pos = obj->visitablePos();
  107. if(obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->alreadyVisited, obj)
  108. || obj->wasVisited(ai->playerID))
  109. {
  110. return false;
  111. }
  112. auto playerRelations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);
  113. if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
  114. {
  115. return false;
  116. }
  117. //it may be hero visiting this obj
  118. //we don't try visiting object on which allied or owned hero stands
  119. // -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
  120. const CGObjectInstance * topObj = cb->getTopObj(pos);
  121. if(!topObj)
  122. return false; // partly visible obj but its visitable pos is not visible.
  123. if(topObj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, topObj->tempOwner) != PlayerRelations::ENEMIES)
  124. return false;
  125. else
  126. return true; //all of the following is met
  127. }
  128. void ObjectClusterizer::clusterize()
  129. {
  130. nearObjects.reset();
  131. farObjects.reset();
  132. blockedObjects.clear();
  133. Obj ignoreObjects[] = {
  134. Obj::MONSTER,
  135. Obj::SIGN,
  136. Obj::REDWOOD_OBSERVATORY,
  137. Obj::MONOLITH_TWO_WAY,
  138. Obj::MONOLITH_ONE_WAY_ENTRANCE,
  139. Obj::MONOLITH_ONE_WAY_EXIT,
  140. Obj::BUOY
  141. };
  142. logAi->debug("Begin object clusterization");
  143. for(const CGObjectInstance * obj : ai->visitableObjs)
  144. {
  145. if(!shouldVisitObject(obj))
  146. continue;
  147. auto paths = ai->ah->getPathsToTile(obj->visitablePos());
  148. if(paths.empty())
  149. continue;
  150. std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
  151. {
  152. return p1.movementCost() < p2.movementCost();
  153. });
  154. if(vstd::contains(ignoreObjects, obj->ID))
  155. {
  156. farObjects.addObject(obj, paths.front(), 0);
  157. continue;
  158. }
  159. bool added = false;
  160. bool directlyAccessible = false;
  161. for(auto & path : paths)
  162. {
  163. if(path.nodes.size() > 1)
  164. {
  165. auto blocker = getBlocker(path);
  166. if(blocker)
  167. {
  168. auto cluster = blockedObjects[blocker];
  169. if(!cluster)
  170. {
  171. cluster.reset(new ObjectCluster(blocker));
  172. blockedObjects[blocker] = cluster;
  173. }
  174. if(!vstd::contains(cluster->objects, obj))
  175. {
  176. float priority = ai->nullkiller->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
  177. cluster->addObject(obj, path, priority);
  178. added = true;
  179. }
  180. }
  181. }
  182. else
  183. {
  184. directlyAccessible = true;
  185. }
  186. }
  187. if(!added || directlyAccessible)
  188. {
  189. if(paths.front().turn() <= 2)
  190. nearObjects.addObject(obj, paths.front(), 0);
  191. else
  192. farObjects.addObject(obj, paths.front(), 0);
  193. }
  194. }
  195. logAi->trace("Near objects count: %i", nearObjects.objects.size());
  196. logAi->trace("Far objects count: %i", farObjects.objects.size());
  197. for(auto pair : blockedObjects)
  198. {
  199. logAi->trace("Cluster %s %s count: %i", pair.first->getObjectName(), pair.first->visitablePos().toString(), pair.second->objects.size());
  200. #if AI_TRACE_LEVEL >= 1
  201. for(auto obj : pair.second->getObjects())
  202. {
  203. logAi->trace("Object %s %s", obj->getObjectName(), obj->visitablePos().toString());
  204. }
  205. #endif
  206. }
  207. }