ConnectionsPlacer.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * ConnectionsPlacer.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 "ConnectionsPlacer.h"
  12. #include "CMapGenerator.h"
  13. #include "RmgMap.h"
  14. #include "../mapping/CMap.h"
  15. #include "../mapping/CMapEditManager.h"
  16. #include "../mapObjects/CObjectClassesHandler.h"
  17. #include "RmgPath.h"
  18. #include "RmgObject.h"
  19. #include "ObjectManager.h"
  20. #include "Functions.h"
  21. #include "RoadPlacer.h"
  22. #include "TileInfo.h"
  23. #include "WaterAdopter.h"
  24. #include "WaterProxy.h"
  25. #include "TownPlacer.h"
  26. VCMI_LIB_NAMESPACE_BEGIN
  27. void ConnectionsPlacer::process()
  28. {
  29. collectNeighbourZones();
  30. for(auto & c : dConnections)
  31. {
  32. if(c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
  33. continue;
  34. if(vstd::contains(dCompleted, c))
  35. continue;
  36. selfSideDirectConnection(c);
  37. }
  38. createBorder(map, zone);
  39. for(auto & c : dConnections)
  40. {
  41. if(c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
  42. continue;
  43. if(vstd::contains(dCompleted, c))
  44. continue;
  45. selfSideIndirectConnection(c);
  46. }
  47. }
  48. void ConnectionsPlacer::init()
  49. {
  50. DEPENDENCY(WaterAdopter);
  51. DEPENDENCY(TownPlacer);
  52. POSTFUNCTION(RoadPlacer);
  53. POSTFUNCTION(ObjectManager);
  54. for(auto c : map.getMapGenOptions().getMapTemplate()->getConnections())
  55. addConnection(c);
  56. }
  57. void ConnectionsPlacer::addConnection(const rmg::ZoneConnection& connection)
  58. {
  59. dConnections.push_back(connection);
  60. }
  61. void ConnectionsPlacer::otherSideConnection(const rmg::ZoneConnection & connection)
  62. {
  63. dCompleted.push_back(connection);
  64. }
  65. void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & connection)
  66. {
  67. bool success = false;
  68. auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA());
  69. auto & otherZone = map.getZones().at(otherZoneId);
  70. //1. Try to make direct connection
  71. //Do if it's not prohibited by terrain settings
  72. const auto& terrains = VLC->terrainTypeHandler->terrains();
  73. bool directProhibited = vstd::contains(terrains[zone.getTerrainType()].prohibitTransitions, otherZone->getTerrainType())
  74. || vstd::contains(terrains[otherZone->getTerrainType()].prohibitTransitions, zone.getTerrainType());
  75. auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
  76. if(!directProhibited && directConnectionIterator != dNeighbourZones.end())
  77. {
  78. int3 guardPos(-1, -1, -1);
  79. int3 borderPos;
  80. while(!directConnectionIterator->second.empty())
  81. {
  82. borderPos = *RandomGeneratorUtil::nextItem(directConnectionIterator->second, generator.rand);
  83. guardPos = zone.areaPossible().nearest(borderPos);
  84. assert(borderPos != guardPos);
  85. auto safetyGap = rmg::Area({guardPos});
  86. safetyGap.unite(safetyGap.getBorderOutside());
  87. safetyGap.intersect(zone.areaPossible());
  88. if(!safetyGap.empty())
  89. {
  90. safetyGap.intersect(otherZone->areaPossible());
  91. if(safetyGap.empty())
  92. break; //successfull position
  93. }
  94. //failed position
  95. directConnectionIterator->second.erase(borderPos);
  96. guardPos = int3(-1, -1, -1);
  97. }
  98. if(guardPos.valid())
  99. {
  100. assert(zone.getModificator<ObjectManager>());
  101. auto & manager = *zone.getModificator<ObjectManager>();
  102. auto * monsterType = manager.chooseGuard(connection.getGuardStrength(), true);
  103. rmg::Area border(zone.getArea().getBorder());
  104. border.unite(otherZone->getArea().getBorder());
  105. auto costFunction = [&border](const int3 & s, const int3 & d)
  106. {
  107. return 1.f / (1.f + border.distanceSqr(d));
  108. };
  109. auto ourArea = zone.areaPossible() + zone.freePaths();
  110. auto theirArea = otherZone->areaPossible() + otherZone->freePaths();
  111. theirArea.add(guardPos);
  112. rmg::Path ourPath(ourArea), theirPath(theirArea);
  113. ourPath.connect(zone.freePaths());
  114. ourPath = ourPath.search(guardPos, true, costFunction);
  115. theirPath.connect(otherZone->freePaths());
  116. theirPath = theirPath.search(guardPos, true, costFunction);
  117. if(ourPath.valid() && theirPath.valid())
  118. {
  119. zone.connectPath(ourPath);
  120. otherZone->connectPath(theirPath);
  121. if(monsterType)
  122. {
  123. rmg::Object monster(*monsterType);
  124. monster.setPosition(guardPos);
  125. manager.placeObject(monster, false, true);
  126. }
  127. else
  128. {
  129. zone.areaPossible().erase(guardPos);
  130. zone.freePaths().add(guardPos);
  131. map.setOccupied(guardPos, ETileType::FREE);
  132. }
  133. assert(zone.getModificator<RoadPlacer>());
  134. zone.getModificator<RoadPlacer>()->addRoadNode(guardPos);
  135. assert(otherZone->getModificator<RoadPlacer>());
  136. otherZone->getModificator<RoadPlacer>()->addRoadNode(borderPos);
  137. assert(otherZone->getModificator<ConnectionsPlacer>());
  138. otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
  139. success = true;
  140. }
  141. }
  142. }
  143. //2. connect via water
  144. bool waterMode = map.getMapGenOptions().getWaterContent() != EWaterContent::NONE;
  145. if(waterMode && zone.isUnderground() == otherZone->isUnderground())
  146. {
  147. if(generator.getZoneWater() && generator.getZoneWater()->getModificator<WaterProxy>())
  148. {
  149. if(generator.getZoneWater()->getModificator<WaterProxy>()->waterKeepConnection(connection.getZoneA(), connection.getZoneB()))
  150. {
  151. assert(otherZone->getModificator<ConnectionsPlacer>());
  152. otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
  153. success = true;
  154. }
  155. }
  156. }
  157. if(success)
  158. dCompleted.push_back(connection);
  159. }
  160. void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & connection)
  161. {
  162. bool success = false;
  163. auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA());
  164. auto & otherZone = map.getZones().at(otherZoneId);
  165. //3. place subterrain gates
  166. if(zone.isUnderground() != otherZone->isUnderground())
  167. {
  168. int3 zShift(0, 0, zone.getPos().z - otherZone->getPos().z);
  169. auto commonArea = zone.areaPossible() * (otherZone->areaPossible() + zShift);
  170. if(!commonArea.empty())
  171. {
  172. assert(zone.getModificator<ObjectManager>());
  173. auto & manager = *zone.getModificator<ObjectManager>();
  174. assert(otherZone->getModificator<ObjectManager>());
  175. auto & managerOther = *otherZone->getModificator<ObjectManager>();
  176. auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
  177. auto gate1 = factory->create();
  178. auto gate2 = factory->create();
  179. rmg::Object rmgGate1(*gate1), rmgGate2(*gate2);
  180. rmgGate1.setTemplate(zone.getTerrainType());
  181. rmgGate2.setTemplate(otherZone->getTerrainType());
  182. bool guarded1 = manager.addGuard(rmgGate1, connection.getGuardStrength(), true);
  183. bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
  184. int minDist = 3;
  185. rmg::Path path2(otherZone->area());
  186. rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile)
  187. {
  188. auto ti = map.getTile(tile);
  189. float dist = ti.getNearestObjectDistance();
  190. if(dist < minDist)
  191. return -1.f;
  192. rmg::Area toPlace(rmgGate1.getArea() + rmgGate1.getAccessibleArea());
  193. toPlace.translate(-zShift);
  194. path2 = managerOther.placeAndConnectObject(toPlace, rmgGate2, minDist, guarded2, true, ObjectManager::OptimizeType::NONE);
  195. return path2.valid() ? 1.f : -1.f;
  196. }, guarded1, true, ObjectManager::OptimizeType::NONE);
  197. if(path1.valid() && path2.valid())
  198. {
  199. zone.connectPath(path1);
  200. otherZone->connectPath(path2);
  201. manager.placeObject(rmgGate1, guarded1, true);
  202. managerOther.placeObject(rmgGate2, guarded2, true);
  203. assert(otherZone->getModificator<ConnectionsPlacer>());
  204. otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
  205. success = true;
  206. }
  207. }
  208. }
  209. //4. place monoliths/portals
  210. if(!success)
  211. {
  212. auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, generator.getNextMonlithIndex());
  213. auto teleport1 = factory->create();
  214. auto teleport2 = factory->create();
  215. zone.getModificator<ObjectManager>()->addRequiredObject(teleport1, connection.getGuardStrength());
  216. otherZone->getModificator<ObjectManager>()->addRequiredObject(teleport2, connection.getGuardStrength());
  217. assert(otherZone->getModificator<ConnectionsPlacer>());
  218. otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
  219. success = true;
  220. }
  221. if(success)
  222. dCompleted.push_back(connection);
  223. }
  224. void ConnectionsPlacer::collectNeighbourZones()
  225. {
  226. auto border = zone.area().getBorderOutside();
  227. for(auto & i : border)
  228. {
  229. if(!map.isOnMap(i))
  230. continue;
  231. auto zid = map.getZoneID(i);
  232. assert(zid != zone.getId());
  233. dNeighbourZones[zid].insert(i);
  234. }
  235. }
  236. VCMI_LIB_NAMESPACE_END