CRmgTemplateZone.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. /*
  2. * CRmgTemplateZone.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 "CRmgTemplateZone.h"
  12. #include "../mapping/CMapEditManager.h"
  13. #include "../mapping/CMap.h"
  14. #include "../VCMI_Lib.h"
  15. #include "../CTownHandler.h"
  16. class CMap;
  17. class CMapEditManager;
  18. CRmgTemplateZone::CTownInfo::CTownInfo() : townCount(0), castleCount(0), townDensity(0), castleDensity(0)
  19. {
  20. }
  21. int CRmgTemplateZone::CTownInfo::getTownCount() const
  22. {
  23. return townCount;
  24. }
  25. void CRmgTemplateZone::CTownInfo::setTownCount(int value)
  26. {
  27. if(value < 0)
  28. throw rmgException("Negative value for town count not allowed.");
  29. townCount = value;
  30. }
  31. int CRmgTemplateZone::CTownInfo::getCastleCount() const
  32. {
  33. return castleCount;
  34. }
  35. void CRmgTemplateZone::CTownInfo::setCastleCount(int value)
  36. {
  37. if(value < 0)
  38. throw rmgException("Negative value for castle count not allowed.");
  39. castleCount = value;
  40. }
  41. int CRmgTemplateZone::CTownInfo::getTownDensity() const
  42. {
  43. return townDensity;
  44. }
  45. void CRmgTemplateZone::CTownInfo::setTownDensity(int value)
  46. {
  47. if(value < 0)
  48. throw rmgException("Negative value for town density not allowed.");
  49. townDensity = value;
  50. }
  51. int CRmgTemplateZone::CTownInfo::getCastleDensity() const
  52. {
  53. return castleDensity;
  54. }
  55. void CRmgTemplateZone::CTownInfo::setCastleDensity(int value)
  56. {
  57. if(value < 0)
  58. throw rmgException("Negative value for castle density not allowed.");
  59. castleDensity = value;
  60. }
  61. CTileInfo::CTileInfo():nearestObjectDistance(INT_MAX), terrain(ETerrainType::WRONG)
  62. {
  63. occupied = ETileType::POSSIBLE; //all tiles are initially possible to place objects or passages
  64. }
  65. int CTileInfo::getNearestObjectDistance() const
  66. {
  67. return nearestObjectDistance;
  68. }
  69. void CTileInfo::setNearestObjectDistance(int value)
  70. {
  71. nearestObjectDistance = std::max(0, value); //never negative (or unitialized)
  72. }
  73. bool CTileInfo::shouldBeBlocked() const
  74. {
  75. return occupied == ETileType::BLOCKED;
  76. }
  77. bool CTileInfo::isBlocked() const
  78. {
  79. return occupied == ETileType::BLOCKED || occupied == ETileType::USED;
  80. }
  81. bool CTileInfo::isPossible() const
  82. {
  83. return occupied == ETileType::POSSIBLE;
  84. }
  85. bool CTileInfo::isFree() const
  86. {
  87. return occupied == ETileType::FREE;
  88. }
  89. void CTileInfo::setOccupied(ETileType::ETileType value)
  90. {
  91. occupied = value;
  92. }
  93. ETerrainType CTileInfo::getTerrainType() const
  94. {
  95. return terrain;
  96. }
  97. void CTileInfo::setTerrainType(ETerrainType value)
  98. {
  99. terrain = value;
  100. }
  101. CRmgTemplateZone::CRmgTemplateZone() : id(0), type(ETemplateZoneType::PLAYER_START), size(1),
  102. townsAreSameType(false), matchTerrainToTown(true)
  103. {
  104. townTypes = getDefaultTownTypes();
  105. terrainTypes = getDefaultTerrainTypes();
  106. }
  107. TRmgTemplateZoneId CRmgTemplateZone::getId() const
  108. {
  109. return id;
  110. }
  111. void CRmgTemplateZone::setId(TRmgTemplateZoneId value)
  112. {
  113. if(value <= 0)
  114. throw rmgException(boost::to_string(boost::format("Zone %d id should be greater than 0.") %id));
  115. id = value;
  116. }
  117. ETemplateZoneType::ETemplateZoneType CRmgTemplateZone::getType() const
  118. {
  119. return type;
  120. }
  121. void CRmgTemplateZone::setType(ETemplateZoneType::ETemplateZoneType value)
  122. {
  123. type = value;
  124. }
  125. int CRmgTemplateZone::getSize() const
  126. {
  127. return size;
  128. }
  129. void CRmgTemplateZone::setSize(int value)
  130. {
  131. if(value <= 0)
  132. throw rmgException(boost::to_string(boost::format("Zone %d size needs to be greater than 0.") % id));
  133. size = value;
  134. }
  135. boost::optional<int> CRmgTemplateZone::getOwner() const
  136. {
  137. return owner;
  138. }
  139. void CRmgTemplateZone::setOwner(boost::optional<int> value)
  140. {
  141. if(!(*value >= 0 && *value <= PlayerColor::PLAYER_LIMIT_I))
  142. throw rmgException(boost::to_string(boost::format ("Owner of zone %d has to be in range 0 to max player count.") %id));
  143. owner = value;
  144. }
  145. const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getPlayerTowns() const
  146. {
  147. return playerTowns;
  148. }
  149. void CRmgTemplateZone::setPlayerTowns(const CTownInfo & value)
  150. {
  151. playerTowns = value;
  152. }
  153. const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getNeutralTowns() const
  154. {
  155. return neutralTowns;
  156. }
  157. void CRmgTemplateZone::setNeutralTowns(const CTownInfo & value)
  158. {
  159. neutralTowns = value;
  160. }
  161. bool CRmgTemplateZone::getTownsAreSameType() const
  162. {
  163. return townsAreSameType;
  164. }
  165. void CRmgTemplateZone::setTownsAreSameType(bool value)
  166. {
  167. townsAreSameType = value;
  168. }
  169. const std::set<TFaction> & CRmgTemplateZone::getTownTypes() const
  170. {
  171. return townTypes;
  172. }
  173. void CRmgTemplateZone::setTownTypes(const std::set<TFaction> & value)
  174. {
  175. townTypes = value;
  176. }
  177. std::set<TFaction> CRmgTemplateZone::getDefaultTownTypes() const
  178. {
  179. std::set<TFaction> defaultTowns;
  180. auto towns = VLC->townh->getDefaultAllowed();
  181. for(int i = 0; i < towns.size(); ++i)
  182. {
  183. if(towns[i]) defaultTowns.insert(i);
  184. }
  185. return defaultTowns;
  186. }
  187. bool CRmgTemplateZone::getMatchTerrainToTown() const
  188. {
  189. return matchTerrainToTown;
  190. }
  191. void CRmgTemplateZone::setMatchTerrainToTown(bool value)
  192. {
  193. matchTerrainToTown = value;
  194. }
  195. const std::set<ETerrainType> & CRmgTemplateZone::getTerrainTypes() const
  196. {
  197. return terrainTypes;
  198. }
  199. void CRmgTemplateZone::setTerrainTypes(const std::set<ETerrainType> & value)
  200. {
  201. assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() &&
  202. value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end());
  203. terrainTypes = value;
  204. }
  205. std::set<ETerrainType> CRmgTemplateZone::getDefaultTerrainTypes() const
  206. {
  207. std::set<ETerrainType> terTypes;
  208. static const ETerrainType::EETerrainType allowedTerTypes[] = { ETerrainType::DIRT, ETerrainType::SAND, ETerrainType::GRASS, ETerrainType::SNOW,
  209. ETerrainType::SWAMP, ETerrainType::ROUGH, ETerrainType::SUBTERRANEAN, ETerrainType::LAVA };
  210. for(auto & allowedTerType : allowedTerTypes) terTypes.insert(allowedTerType);
  211. return terTypes;
  212. }
  213. boost::optional<TRmgTemplateZoneId> CRmgTemplateZone::getTerrainTypeLikeZone() const
  214. {
  215. return terrainTypeLikeZone;
  216. }
  217. void CRmgTemplateZone::setTerrainTypeLikeZone(boost::optional<TRmgTemplateZoneId> value)
  218. {
  219. terrainTypeLikeZone = value;
  220. }
  221. boost::optional<TRmgTemplateZoneId> CRmgTemplateZone::getTownTypeLikeZone() const
  222. {
  223. return townTypeLikeZone;
  224. }
  225. void CRmgTemplateZone::setTownTypeLikeZone(boost::optional<TRmgTemplateZoneId> value)
  226. {
  227. townTypeLikeZone = value;
  228. }
  229. void CRmgTemplateZone::addConnection(TRmgTemplateZoneId otherZone)
  230. {
  231. connections.push_back (otherZone);
  232. }
  233. std::vector<TRmgTemplateZoneId> CRmgTemplateZone::getConnections() const
  234. {
  235. return connections;
  236. }
  237. float3 CRmgTemplateZone::getCenter() const
  238. {
  239. return center;
  240. }
  241. void CRmgTemplateZone::setCenter(const float3 &f)
  242. {
  243. //limit boundaries to (0,1) square
  244. center = float3 (std::min(std::max(f.x, 0.f), 1.f), std::min(std::max(f.y, 0.f), 1.f), f.z);
  245. }
  246. bool CRmgTemplateZone::pointIsIn(int x, int y)
  247. {
  248. return true;
  249. }
  250. int3 CRmgTemplateZone::getPos() const
  251. {
  252. return pos;
  253. }
  254. void CRmgTemplateZone::setPos(const int3 &Pos)
  255. {
  256. pos = Pos;
  257. }
  258. void CRmgTemplateZone::addTile (const int3 &pos)
  259. {
  260. tileinfo.insert(pos);
  261. }
  262. std::set<int3> CRmgTemplateZone::getTileInfo () const
  263. {
  264. return tileinfo;
  265. }
  266. void CRmgTemplateZone::createConnections(CMapGenerator* gen)
  267. {
  268. for (auto connection : connections)
  269. {
  270. if (getId() > connection) //only one connection between each pair
  271. continue;
  272. int3 guardPos(-1,-1,-1);
  273. auto otherZoneTiles = gen->getZones()[connection]->getTileInfo();
  274. auto otherZoneCenter = gen->getZones()[connection]->getPos();
  275. for (auto tile : tileinfo)
  276. {
  277. gen->foreach_neighbour (tile, [&guardPos, tile, &otherZoneTiles](int3 &pos)
  278. {
  279. if (vstd::contains(otherZoneTiles, pos))
  280. guardPos = tile;
  281. });
  282. if (guardPos.valid())
  283. {
  284. gen->setOccupied (pos, ETileType::FREE); //TODO: place monster here
  285. //zones can make paths only in their own area
  286. this->crunchPath (gen, guardPos, this->getPos(), this->getId()); //make connection towards our zone center
  287. gen->getZones()[connection]->crunchPath (gen, guardPos, otherZoneCenter, connection); //make connection towards other zone center
  288. break; //we're done with this connection
  289. }
  290. }
  291. if (!guardPos.valid())
  292. logGlobal->warnStream() << boost::format ("Did not find connection between zones %d and %d") %getId() %connection;
  293. }
  294. }
  295. void CRmgTemplateZone::createBorder(CMapGenerator* gen)
  296. {
  297. for (auto tile : tileinfo)
  298. {
  299. gen->foreach_neighbour (tile, [this, gen](int3 &pos)
  300. {
  301. if (!vstd::contains(this->tileinfo, pos))
  302. {
  303. gen->foreach_neighbour (pos, [this, gen](int3 &pos)
  304. {
  305. if (gen->isPossible(pos))
  306. gen->setOccupied (pos, ETileType::BLOCKED);
  307. });
  308. }
  309. });
  310. }
  311. }
  312. bool CRmgTemplateZone::crunchPath (CMapGenerator* gen, int3 &src, int3 &dst, TRmgTemplateZoneId zone)
  313. {
  314. /*
  315. make shortest path with free tiles, reachning dst or closest already free tile. Avoid blocks.
  316. do not leave zone border
  317. */
  318. bool result = false;
  319. bool end = false;
  320. int3 currentPos = src;
  321. float distance = currentPos.dist2dSQ (dst);
  322. while (!end)
  323. {
  324. if (currentPos == dst)
  325. break;
  326. auto lastDistance = distance;
  327. gen->foreach_neighbour (currentPos, [this, gen, &currentPos, dst, &distance, &result, &end](int3 &pos)
  328. {
  329. if (!result) //not sure if lambda is worth it...
  330. {
  331. if (pos == dst)
  332. {
  333. result = true;
  334. end = true;
  335. }
  336. if (pos.dist2dSQ (dst) < distance)
  337. {
  338. if (!gen->isBlocked(pos))
  339. {
  340. if (vstd::contains (tileinfo, pos))
  341. {
  342. if (gen->isPossible(pos))
  343. {
  344. gen->setOccupied (pos, ETileType::FREE);
  345. currentPos = pos;
  346. distance = currentPos.dist2dSQ (dst);
  347. }
  348. else if (gen->isFree(pos))
  349. {
  350. end = true;
  351. result = true;
  352. }
  353. else
  354. throw rmgException(boost::to_string(boost::format("Tile %s of uknown type found on path") % pos()));
  355. }
  356. }
  357. }
  358. }
  359. });
  360. if (!(result || distance < lastDistance)) //we do not advance, use more avdnaced pathfinding algorithm?
  361. {
  362. logGlobal->warnStream() << boost::format ("No tile closer than %s found on path from %s to %s") %currentPos %src %dst;
  363. break;
  364. }
  365. }
  366. return result;
  367. }
  368. bool CRmgTemplateZone::fill(CMapGenerator* gen)
  369. {
  370. std::vector<CGObjectInstance*> required_objects;
  371. int townId = 0;
  372. if ((type == ETemplateZoneType::CPU_START) || (type == ETemplateZoneType::PLAYER_START))
  373. {
  374. logGlobal->infoStream() << "Preparing playing zone";
  375. int player_id = *owner - 1;
  376. auto & playerInfo = gen->map->players[player_id];
  377. if (playerInfo.canAnyonePlay())
  378. {
  379. PlayerColor player(player_id);
  380. auto town = new CGTownInstance();
  381. town->ID = Obj::TOWN;
  382. townId = gen->mapGenOptions->getPlayersSettings().find(player)->second.getStartingTown();
  383. if(townId == CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
  384. townId = *RandomGeneratorUtil::nextItem(VLC->townh->getAllowedFactions(), gen->rand); // all possible towns, skip neutral
  385. town->subID = townId;
  386. town->tempOwner = player;
  387. town->builtBuildings.insert(BuildingID::FORT);
  388. town->builtBuildings.insert(BuildingID::DEFAULT);
  389. placeObject(gen, town, getPos());
  390. logGlobal->traceStream() << "Placed object";
  391. logGlobal->traceStream() << "Fill player info " << player_id;
  392. auto & playerInfo = gen->map->players[player_id];
  393. // Update player info
  394. playerInfo.allowedFactions.clear();
  395. playerInfo.allowedFactions.insert(town->subID);
  396. playerInfo.hasMainTown = true;
  397. playerInfo.posOfMainTown = town->pos - int3(2, 0, 0);
  398. playerInfo.generateHeroAtMainTown = true;
  399. //required_objects.push_back(town);
  400. std::vector<Res::ERes> required_mines;
  401. required_mines.push_back(Res::ERes::WOOD);
  402. required_mines.push_back(Res::ERes::ORE);
  403. for(const auto res : required_mines)
  404. {
  405. auto mine = new CGMine();
  406. mine->ID = Obj::MINE;
  407. mine->subID = static_cast<si32>(res);
  408. mine->producedResource = res;
  409. mine->producedQuantity = mine->defaultResProduction();
  410. required_objects.push_back(mine);
  411. }
  412. }
  413. else
  414. {
  415. type = ETemplateZoneType::TREASURE;
  416. townId = *RandomGeneratorUtil::nextItem(VLC->townh->getAllowedFactions(), gen->rand);
  417. logGlobal->infoStream() << "Skipping this zone cause no player";
  418. }
  419. }
  420. else //no player
  421. {
  422. townId = *RandomGeneratorUtil::nextItem(VLC->townh->getAllowedFactions(), gen->rand);
  423. }
  424. //paint zone with matching terrain
  425. std::vector<int3> tiles;
  426. for (auto tile : tileinfo)
  427. {
  428. tiles.push_back (tile);
  429. }
  430. gen->editManager->getTerrainSelection().setSelection(tiles);
  431. gen->editManager->drawTerrain(VLC->townh->factions[townId]->nativeTerrain, &gen->rand);
  432. logGlobal->infoStream() << "Creating required objects";
  433. for(const auto &obj : required_objects)
  434. {
  435. int3 pos;
  436. logGlobal->traceStream() << "Looking for place";
  437. if ( ! findPlaceForObject(gen, obj, 3, pos))
  438. {
  439. logGlobal->errorStream() << boost::format("Failed to fill zone %d due to lack of space") %id;
  440. //TODO CLEANUP!
  441. return false;
  442. }
  443. logGlobal->traceStream() << "Place found";
  444. placeObject(gen, obj, pos);
  445. }
  446. std::vector<CGObjectInstance*> guarded_objects;
  447. static auto res_gen = gen->rand.getIntRange(Res::ERes::WOOD, Res::ERes::GOLD);
  448. const double res_mindist = 5;
  449. do {
  450. auto obj = new CGResource();
  451. auto restype = static_cast<Res::ERes>(res_gen());
  452. obj->ID = Obj::RESOURCE;
  453. obj->subID = static_cast<si32>(restype);
  454. obj->amount = 0;
  455. int3 pos;
  456. if ( ! findPlaceForObject(gen, obj, res_mindist, pos))
  457. {
  458. delete obj;
  459. break;
  460. }
  461. placeObject(gen, obj, pos);
  462. if ((restype != Res::ERes::WOOD) && (restype != Res::ERes::ORE))
  463. {
  464. guarded_objects.push_back(obj);
  465. }
  466. } while(true);
  467. for(const auto &obj : guarded_objects)
  468. {
  469. if ( ! guardObject(gen, obj, 500))
  470. {
  471. //TODO, DEL obj from map
  472. }
  473. }
  474. auto sel = gen->editManager->getTerrainSelection();
  475. sel.clearSelection();
  476. for (auto tile : tileinfo)
  477. {
  478. if (gen->shouldBeBlocked(tile)) //fill tiles that should be blocked with obstacles
  479. {
  480. auto obj = new CGObjectInstance();
  481. obj->ID = static_cast<Obj>(130);
  482. obj->subID = 0;
  483. placeObject(gen, obj, tile);
  484. }
  485. }
  486. //logGlobal->infoStream() << boost::format("Filling %d with ROCK") % sel.getSelectedItems().size();
  487. //gen->editManager->drawTerrain(ETerrainType::ROCK, &gen->gen);
  488. logGlobal->infoStream() << boost::format ("Zone %d filled successfully") %id;
  489. return true;
  490. }
  491. bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos)
  492. {
  493. //si32 min_dist = sqrt(tileinfo.size()/density);
  494. int best_distance = 0;
  495. bool result = false;
  496. si32 w = gen->map->width;
  497. si32 h = gen->map->height;
  498. auto ow = obj->getWidth();
  499. auto oh = obj->getHeight();
  500. //logGlobal->infoStream() << boost::format("Min dist for density %f is %d") % density % min_dist;
  501. for(auto tile : tileinfo)
  502. {
  503. auto &ti = gen->getTile(tile);
  504. auto dist = ti.getNearestObjectDistance();
  505. //avoid borders
  506. if ((tile.x < 3) || (w - tile.x < 3) || (tile.y < 3) || (h - tile.y < 3))
  507. continue;
  508. if (gen->isPossible(tile) && (dist >= min_dist) && (dist > best_distance))
  509. {
  510. bool allTilesAvailable = true;
  511. for (auto blockingTile : obj->getBlockedOffsets())
  512. {
  513. if (!gen->isPossible(pos + blockingTile))
  514. {
  515. allTilesAvailable = false; //if at least one tile is not possible, object can't be placed here
  516. break;
  517. }
  518. }
  519. if (allTilesAvailable)
  520. {
  521. best_distance = dist;
  522. pos = tile;
  523. result = true;
  524. }
  525. }
  526. }
  527. if (result)
  528. {
  529. gen->setOccupied(pos, ETileType::BLOCKED); //block that tile
  530. }
  531. return result;
  532. }
  533. void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
  534. {
  535. if (!gen->map->isInTheMap(pos))
  536. throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % object->id % object->pos()));
  537. object->pos = pos;
  538. if (object->isVisitable() && !gen->map->isInTheMap(object->visitablePos()))
  539. throw rmgException(boost::to_string(boost::format("Visitable tile %s of object %d at %s is outside the map") % object->visitablePos() % object->id % object->pos()));
  540. for (auto tile : object->getBlockedPos())
  541. {
  542. if (!gen->map->isInTheMap(tile))
  543. throw rmgException(boost::to_string(boost::format("Tile %s of object %d at %s is outside the map") % tile() % object->id % object->pos()));
  544. }
  545. auto templates = VLC->dobjinfo->pickCandidates(object->ID, object->subID, gen->map->getTile(pos).terType);
  546. if (templates.empty())
  547. throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") %object->ID %object->subID %pos));
  548. object->appearance = templates.front();
  549. gen->map->addBlockVisTiles(object);
  550. gen->editManager->insertObject(object, pos);
  551. logGlobal->traceStream() << boost::format ("Successfully inserted object (%d,%d) at pos %s") %object->ID %object->subID %pos();
  552. }
  553. void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
  554. {
  555. logGlobal->traceStream() << boost::format("Inserting object at %d %d") % pos.x % pos.y;
  556. checkAndPlaceObject (gen, object, pos);
  557. auto points = object->getBlockedPos();
  558. if (object->isVisitable())
  559. points.insert(pos + object->getVisitableOffset());
  560. points.insert(pos);
  561. for(auto const &p : points)
  562. {
  563. if (vstd::contains(tileinfo, pos + p))
  564. {
  565. gen->setOccupied(pos + p, ETileType::USED);
  566. }
  567. }
  568. for(auto tile : tileinfo)
  569. {
  570. si32 d = pos.dist2d(tile);
  571. gen->setNearestObjectDistance(tile, std::min(d, gen->getNearestObjectDistance(tile)));
  572. }
  573. }
  574. bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str)
  575. {
  576. logGlobal->traceStream() << boost::format("Guard object at %d %d") % object->pos.x % object->pos.y;
  577. int3 visitable = object->visitablePos();
  578. std::vector<int3> tiles;
  579. gen->foreach_neighbour(visitable, [&](int3& pos)
  580. {
  581. logGlobal->traceStream() << boost::format("Block at %d %d") % pos.x % pos.y;
  582. if (gen->isPossible(pos))
  583. {
  584. tiles.push_back(pos);
  585. gen->setOccupied(pos, ETileType::BLOCKED);
  586. };
  587. });
  588. if ( ! tiles.size())
  589. {
  590. logGlobal->infoStream() << "Failed";
  591. return false;
  592. }
  593. auto guard_tile = *RandomGeneratorUtil::nextItem(tiles, gen->rand);
  594. gen->setOccupied (guard_tile, ETileType::USED);
  595. auto guard = new CGCreature();
  596. guard->ID = Obj::RANDOM_MONSTER;
  597. guard->subID = 0;
  598. auto hlp = new CStackInstance();
  599. hlp->count = 10;
  600. //type will be set during initialization
  601. guard->putStack(SlotID(0), hlp);
  602. checkAndPlaceObject(gen, guard, guard_tile);
  603. return true;
  604. }