ObjectClusterizer.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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. if(!isObjectPassable(blocker))
  96. return blocker;
  97. }
  98. }
  99. return nullptr;
  100. }
  101. bool shouldVisitObject(const CGObjectInstance * obj)
  102. {
  103. if(isObjectRemovable(obj))
  104. {
  105. return true;
  106. }
  107. const int3 pos = obj->visitablePos();
  108. if(obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->alreadyVisited, obj)
  109. || obj->wasVisited(ai->playerID))
  110. {
  111. return false;
  112. }
  113. auto playerRelations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);
  114. if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
  115. {
  116. return false;
  117. }
  118. //it may be hero visiting this obj
  119. //we don't try visiting object on which allied or owned hero stands
  120. // -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
  121. const CGObjectInstance * topObj = cb->getTopObj(pos);
  122. if(!topObj)
  123. return false; // partly visible obj but its visitable pos is not visible.
  124. if(topObj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, topObj->tempOwner) != PlayerRelations::ENEMIES)
  125. return false;
  126. else
  127. return true; //all of the following is met
  128. }
  129. void ObjectClusterizer::clusterize()
  130. {
  131. nearObjects.reset();
  132. farObjects.reset();
  133. blockedObjects.clear();
  134. Obj ignoreObjects[] = {
  135. Obj::MONSTER,
  136. Obj::SIGN,
  137. Obj::REDWOOD_OBSERVATORY,
  138. Obj::MONOLITH_TWO_WAY,
  139. Obj::MONOLITH_ONE_WAY_ENTRANCE,
  140. Obj::MONOLITH_ONE_WAY_EXIT,
  141. Obj::BUOY
  142. };
  143. logAi->debug("Begin object clusterization");
  144. for(const CGObjectInstance * obj : ai->visitableObjs)
  145. {
  146. if(!shouldVisitObject(obj))
  147. continue;
  148. auto paths = ai->ah->getPathsToTile(obj->visitablePos());
  149. if(paths.empty())
  150. continue;
  151. std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
  152. {
  153. return p1.movementCost() < p2.movementCost();
  154. });
  155. if(vstd::contains(ignoreObjects, obj->ID))
  156. {
  157. farObjects.addObject(obj, paths.front(), 0);
  158. continue;
  159. }
  160. bool added = false;
  161. bool directlyAccessible = false;
  162. for(auto & path : paths)
  163. {
  164. if(path.nodes.size() > 1)
  165. {
  166. auto blocker = getBlocker(path);
  167. if(blocker)
  168. {
  169. auto cluster = blockedObjects[blocker];
  170. if(!cluster)
  171. {
  172. cluster.reset(new ObjectCluster(blocker));
  173. blockedObjects[blocker] = cluster;
  174. }
  175. if(!vstd::contains(cluster->objects, obj))
  176. {
  177. float priority = ai->nullkiller->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
  178. cluster->addObject(obj, path, priority);
  179. added = true;
  180. }
  181. }
  182. }
  183. else
  184. {
  185. directlyAccessible = true;
  186. }
  187. }
  188. if(!added || directlyAccessible)
  189. {
  190. if(paths.front().turn() <= 2)
  191. nearObjects.addObject(obj, paths.front(), 0);
  192. else
  193. farObjects.addObject(obj, paths.front(), 0);
  194. }
  195. }
  196. logAi->trace("Near objects count: %i", nearObjects.objects.size());
  197. logAi->trace("Far objects count: %i", farObjects.objects.size());
  198. for(auto pair : blockedObjects)
  199. {
  200. logAi->trace("Cluster %s %s count: %i", pair.first->getObjectName(), pair.first->visitablePos().toString(), pair.second->objects.size());
  201. #if AI_TRACE_LEVEL >= 1
  202. for(auto obj : pair.second->getObjects())
  203. {
  204. logAi->trace("Object %s %s", obj->getObjectName(), obj->visitablePos().toString());
  205. }
  206. #endif
  207. }
  208. }