ObstacleProxy.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*
  2. * ObstacleProxy.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 "ObstacleProxy.h"
  12. #include "../mapping/CMap.h"
  13. #include "../mapObjectConstructors/AObjectTypeHandler.h"
  14. #include "../mapObjectConstructors/CObjectClassesHandler.h"
  15. #include "../mapObjects/ObjectTemplate.h"
  16. VCMI_LIB_NAMESPACE_BEGIN
  17. void ObstacleProxy::collectPossibleObstacles(TerrainId terrain)
  18. {
  19. //get all possible obstacles for this terrain
  20. for(auto primaryID : VLC->objtypeh->knownObjects())
  21. {
  22. for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
  23. {
  24. auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
  25. if(handler->isStaticObject())
  26. {
  27. for(const auto & temp : handler->getTemplates())
  28. {
  29. if(temp->canBePlacedAt(terrain) && temp->getBlockMapOffset().valid())
  30. obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp);
  31. }
  32. }
  33. }
  34. }
  35. for(const auto & o : obstaclesBySize)
  36. {
  37. possibleObstacles.emplace_back(o);
  38. }
  39. boost::sort(possibleObstacles, [](const ObstaclePair &p1, const ObstaclePair &p2) -> bool
  40. {
  41. return p1.first > p2.first; //bigger obstacles first
  42. });
  43. }
  44. void ObstacleProxy::addBlockedTile(const int3& tile)
  45. {
  46. blockedArea.add(tile);
  47. }
  48. void ObstacleProxy::setBlockedArea(const rmg::Area& area)
  49. {
  50. blockedArea = area;
  51. }
  52. void ObstacleProxy::clearBlockedArea()
  53. {
  54. blockedArea.clear();
  55. }
  56. bool ObstacleProxy::isProhibited(const rmg::Area& objArea) const
  57. {
  58. return false;
  59. };
  60. int ObstacleProxy::getWeightedObjects(const int3 & tile, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects)
  61. {
  62. int maxWeight = std::numeric_limits<int>::min();
  63. for(auto & possibleObstacle : possibleObstacles)
  64. {
  65. if(!possibleObstacle.first)
  66. continue;
  67. auto shuffledObstacles = possibleObstacle.second;
  68. RandomGeneratorUtil::randomShuffle(shuffledObstacles, rand);
  69. for(const auto & temp : shuffledObstacles)
  70. {
  71. auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid);
  72. auto * obj = handler->create(nullptr, temp);
  73. allObjects.emplace_back(*obj);
  74. rmg::Object * rmgObject = &allObjects.back();
  75. for(const auto & offset : obj->getBlockedOffsets())
  76. {
  77. auto newPos = tile - offset;
  78. if(!isInTheMap(newPos))
  79. continue;
  80. rmgObject->setPosition(newPos);
  81. bool isInTheMapEntirely = true;
  82. for (const auto & t : rmgObject->getArea().getTiles())
  83. {
  84. if (!isInTheMap(t))
  85. {
  86. isInTheMapEntirely = false;
  87. break;
  88. }
  89. }
  90. if (!isInTheMapEntirely)
  91. {
  92. continue;
  93. }
  94. if(isProhibited(rmgObject->getArea()))
  95. continue;
  96. int coverageBlocked = 0;
  97. int coveragePossible = 0;
  98. //do not use area intersection in optimization purposes
  99. for(const auto & t : rmgObject->getArea().getTilesVector())
  100. {
  101. auto coverage = verifyCoverage(t);
  102. if(coverage.first)
  103. ++coverageBlocked;
  104. if(coverage.second)
  105. ++coveragePossible;
  106. }
  107. int coverageOverlap = possibleObstacle.first - coverageBlocked - coveragePossible;
  108. int weight = possibleObstacle.first + coverageBlocked - coverageOverlap * possibleObstacle.first;
  109. assert(coverageOverlap >= 0);
  110. if(weight > maxWeight)
  111. {
  112. weightedObjects.clear();
  113. maxWeight = weight;
  114. weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
  115. if(weight > 0)
  116. break;
  117. }
  118. else if(weight == maxWeight)
  119. weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
  120. }
  121. }
  122. if(maxWeight > 0)
  123. break;
  124. }
  125. return maxWeight;
  126. }
  127. std::set<CGObjectInstance*> ObstacleProxy::createObstacles(CRandomGenerator & rand)
  128. {
  129. //reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left
  130. auto blockedTiles = blockedArea.getTilesVector();
  131. int tilePos = 0;
  132. std::set<CGObjectInstance*> objs;
  133. while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size())
  134. {
  135. auto tile = blockedArea.getTilesVector()[tilePos];
  136. std::list<rmg::Object> allObjects;
  137. std::vector<std::pair<rmg::Object*, int3>> weightedObjects;
  138. int maxWeight = getWeightedObjects(tile, rand, allObjects, weightedObjects);
  139. if(weightedObjects.empty())
  140. {
  141. tilePos += 1;
  142. continue;
  143. }
  144. auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, rand);
  145. objIter->first->setPosition(objIter->second);
  146. placeObject(*objIter->first, objs);
  147. blockedArea.subtract(objIter->first->getArea());
  148. tilePos = 0;
  149. postProcess(*objIter->first);
  150. if(maxWeight < 0)
  151. logGlobal->warn("Placed obstacle with negative weight at %s", objIter->second.toString());
  152. for(auto & o : allObjects)
  153. {
  154. if(&o != objIter->first)
  155. o.clear();
  156. }
  157. }
  158. return objs;
  159. }
  160. //FIXME: Only editor placer obstacles directly
  161. void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances)
  162. {
  163. manager->insertObjects(instances); //insert as one operation - for undo purposes
  164. }
  165. std::pair<bool, bool> ObstacleProxy::verifyCoverage(const int3 & t) const
  166. {
  167. return {blockedArea.contains(t), false};
  168. }
  169. void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
  170. {
  171. for (auto * instance : object.instances())
  172. {
  173. instances.insert(&instance->object());
  174. }
  175. }
  176. EditorObstaclePlacer::EditorObstaclePlacer(CMap* map):
  177. map(map)
  178. {
  179. }
  180. bool EditorObstaclePlacer::isInTheMap(const int3& tile)
  181. {
  182. return map->isInTheMap(tile);
  183. }
  184. std::set<CGObjectInstance*> EditorObstaclePlacer::placeObstacles(CRandomGenerator & rand)
  185. {
  186. auto obstacles = createObstacles(rand);
  187. finalInsertion(map->getEditManager(), obstacles);
  188. return obstacles;
  189. }
  190. VCMI_LIB_NAMESPACE_END