2
0

ObstacleProxy.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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(temp);
  73. allObjects.emplace_back(*obj);
  74. rmg::Object * rmgObject = &allObjects.back();
  75. for(const auto & offset : obj->getBlockedOffsets())
  76. {
  77. rmgObject->setPosition(tile - offset);
  78. if(!isInTheMap(rmgObject->getPosition()))
  79. continue;
  80. if(!rmgObject->getArea().getSubarea([this](const int3 & t)
  81. {
  82. return !isInTheMap(t);
  83. }).empty())
  84. continue;
  85. if(isProhibited(rmgObject->getArea()))
  86. continue;
  87. int coverageBlocked = 0;
  88. int coveragePossible = 0;
  89. //do not use area intersection in optimization purposes
  90. for(const auto & t : rmgObject->getArea().getTilesVector())
  91. {
  92. auto coverage = verifyCoverage(t);
  93. if(coverage.first)
  94. ++coverageBlocked;
  95. if(coverage.second)
  96. ++coveragePossible;
  97. }
  98. int coverageOverlap = possibleObstacle.first - coverageBlocked - coveragePossible;
  99. int weight = possibleObstacle.first + coverageBlocked - coverageOverlap * possibleObstacle.first;
  100. assert(coverageOverlap >= 0);
  101. if(weight > maxWeight)
  102. {
  103. weightedObjects.clear();
  104. maxWeight = weight;
  105. weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
  106. if(weight > 0)
  107. break;
  108. }
  109. else if(weight == maxWeight)
  110. weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
  111. }
  112. }
  113. if(maxWeight > 0)
  114. break;
  115. }
  116. return maxWeight;
  117. }
  118. std::set<CGObjectInstance*> ObstacleProxy::createObstacles(CRandomGenerator & rand)
  119. {
  120. //reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left
  121. auto blockedTiles = blockedArea.getTilesVector();
  122. int tilePos = 0;
  123. std::set<CGObjectInstance*> objs;
  124. while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size())
  125. {
  126. auto tile = blockedArea.getTilesVector()[tilePos];
  127. std::list<rmg::Object> allObjects;
  128. std::vector<std::pair<rmg::Object*, int3>> weightedObjects;
  129. int maxWeight = getWeightedObjects(tile, rand, allObjects, weightedObjects);
  130. if(weightedObjects.empty())
  131. {
  132. tilePos += 1;
  133. continue;
  134. }
  135. auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, rand);
  136. objIter->first->setPosition(objIter->second);
  137. placeObject(*objIter->first, objs);
  138. blockedArea.subtract(objIter->first->getArea());
  139. tilePos = 0;
  140. postProcess(*objIter->first);
  141. if(maxWeight < 0)
  142. logGlobal->warn("Placed obstacle with negative weight at %s", objIter->second.toString());
  143. for(auto & o : allObjects)
  144. {
  145. if(&o != objIter->first)
  146. o.clear();
  147. }
  148. }
  149. return objs;
  150. }
  151. //FIXME: Only editor placer obstacles directly
  152. void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances)
  153. {
  154. manager->insertObjects(instances); //insert as one operation - for undo purposes
  155. }
  156. std::pair<bool, bool> ObstacleProxy::verifyCoverage(const int3 & t) const
  157. {
  158. return {blockedArea.contains(t), false};
  159. }
  160. void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
  161. {
  162. for (auto * instance : object.instances())
  163. {
  164. instances.insert(&instance->object());
  165. }
  166. }
  167. EditorObstaclePlacer::EditorObstaclePlacer(CMap* map):
  168. map(map)
  169. {
  170. }
  171. bool EditorObstaclePlacer::isInTheMap(const int3& tile)
  172. {
  173. return map->isInTheMap(tile);
  174. }
  175. std::set<CGObjectInstance*> EditorObstaclePlacer::placeObstacles(CRandomGenerator & rand)
  176. {
  177. auto obstacles = createObstacles(rand);
  178. finalInsertion(map->getEditManager(), obstacles);
  179. return obstacles;
  180. }
  181. VCMI_LIB_NAMESPACE_END