CRmgTemplateZone.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  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. //rearrange tiles in random order
  269. std::vector<int3> tiles(tileinfo.begin(), tileinfo.end());
  270. //TODO: hwo to use std::shuffle with our generator?
  271. //std::random_shuffle (tiles.begin(), tiles.end(), &gen->rand.nextInt);
  272. int i, n;
  273. n = (tiles.end() - tiles.begin());
  274. for (i=n-1; i>0; --i)
  275. {
  276. std::swap (tiles.begin()[i],tiles.begin()[gen->rand.nextInt(i+1)]);
  277. }
  278. for (auto connection : connections)
  279. {
  280. if (getId() > connection) //only one connection between each pair
  281. continue;
  282. int3 guardPos(-1,-1,-1);
  283. auto otherZoneTiles = gen->getZones()[connection]->getTileInfo();
  284. auto otherZoneCenter = gen->getZones()[connection]->getPos();
  285. for (auto tile : tiles)
  286. {
  287. gen->foreach_neighbour (tile, [&guardPos, tile, &otherZoneTiles](int3 &pos)
  288. {
  289. if (vstd::contains(otherZoneTiles, pos))
  290. guardPos = tile;
  291. });
  292. if (guardPos.valid())
  293. {
  294. gen->setOccupied (guardPos, ETileType::FREE); //TODO: place monster here
  295. //zones can make paths only in their own area
  296. this->crunchPath (gen, guardPos, this->getPos(), this->getId()); //make connection towards our zone center
  297. gen->getZones()[connection]->crunchPath (gen, guardPos, otherZoneCenter, connection); //make connection towards other zone center
  298. break; //we're done with this connection
  299. }
  300. }
  301. if (!guardPos.valid())
  302. {
  303. auto teleport1 = new CGTeleport;
  304. teleport1->ID = Obj::MONOLITH_TWO_WAY;
  305. teleport1->subID = gen->getNextMonlithIndex();
  306. auto teleport2 = new CGTeleport(*teleport1);
  307. addRequiredObject (teleport1);
  308. gen->getZones()[connection]->addRequiredObject(teleport2);
  309. }
  310. }
  311. }
  312. void CRmgTemplateZone::createBorder(CMapGenerator* gen)
  313. {
  314. for (auto tile : tileinfo)
  315. {
  316. gen->foreach_neighbour (tile, [this, gen](int3 &pos)
  317. {
  318. if (!vstd::contains(this->tileinfo, pos))
  319. {
  320. gen->foreach_neighbour (pos, [this, gen](int3 &pos)
  321. {
  322. if (gen->isPossible(pos))
  323. gen->setOccupied (pos, ETileType::BLOCKED);
  324. });
  325. }
  326. });
  327. }
  328. }
  329. bool CRmgTemplateZone::crunchPath (CMapGenerator* gen, int3 &src, int3 &dst, TRmgTemplateZoneId zone)
  330. {
  331. /*
  332. make shortest path with free tiles, reachning dst or closest already free tile. Avoid blocks.
  333. do not leave zone border
  334. */
  335. bool result = false;
  336. bool end = false;
  337. int3 currentPos = src;
  338. float distance = currentPos.dist2dSQ (dst);
  339. while (!end)
  340. {
  341. if (currentPos == dst)
  342. break;
  343. auto lastDistance = distance;
  344. gen->foreach_neighbour (currentPos, [this, gen, &currentPos, dst, &distance, &result, &end](int3 &pos)
  345. {
  346. if (!result) //not sure if lambda is worth it...
  347. {
  348. if (pos == dst)
  349. {
  350. result = true;
  351. end = true;
  352. }
  353. if (pos.dist2dSQ (dst) < distance)
  354. {
  355. if (!gen->isBlocked(pos))
  356. {
  357. if (vstd::contains (tileinfo, pos))
  358. {
  359. if (gen->isPossible(pos))
  360. {
  361. gen->setOccupied (pos, ETileType::FREE);
  362. currentPos = pos;
  363. distance = currentPos.dist2dSQ (dst);
  364. }
  365. else if (gen->isFree(pos))
  366. {
  367. end = true;
  368. result = true;
  369. }
  370. else
  371. throw rmgException(boost::to_string(boost::format("Tile %s of uknown type found on path") % pos()));
  372. }
  373. }
  374. }
  375. }
  376. });
  377. if (!(result || distance < lastDistance)) //we do not advance, use more avdnaced pathfinding algorithm?
  378. {
  379. logGlobal->warnStream() << boost::format ("No tile closer than %s found on path from %s to %s") %currentPos %src %dst;
  380. break;
  381. }
  382. }
  383. return result;
  384. }
  385. void CRmgTemplateZone::addRequiredObject(CGObjectInstance * obj)
  386. {
  387. requiredObjects.push_back(obj);
  388. }
  389. bool CRmgTemplateZone::fill(CMapGenerator* gen)
  390. {
  391. int townId = 0;
  392. if ((type == ETemplateZoneType::CPU_START) || (type == ETemplateZoneType::PLAYER_START))
  393. {
  394. logGlobal->infoStream() << "Preparing playing zone";
  395. int player_id = *owner - 1;
  396. auto & playerInfo = gen->map->players[player_id];
  397. if (playerInfo.canAnyonePlay())
  398. {
  399. PlayerColor player(player_id);
  400. auto town = new CGTownInstance();
  401. town->ID = Obj::TOWN;
  402. townId = gen->mapGenOptions->getPlayersSettings().find(player)->second.getStartingTown();
  403. if(townId == CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
  404. townId = *RandomGeneratorUtil::nextItem(VLC->townh->getAllowedFactions(), gen->rand); // all possible towns, skip neutral
  405. town->subID = townId;
  406. town->tempOwner = player;
  407. town->builtBuildings.insert(BuildingID::FORT);
  408. town->builtBuildings.insert(BuildingID::DEFAULT);
  409. placeObject(gen, town, getPos());
  410. logGlobal->traceStream() << "Placed object";
  411. logGlobal->traceStream() << "Fill player info " << player_id;
  412. auto & playerInfo = gen->map->players[player_id];
  413. // Update player info
  414. playerInfo.allowedFactions.clear();
  415. playerInfo.allowedFactions.insert(town->subID);
  416. playerInfo.hasMainTown = true;
  417. playerInfo.posOfMainTown = town->pos - int3(2, 0, 0);
  418. playerInfo.generateHeroAtMainTown = true;
  419. //requiredObjects.push_back(town);
  420. std::vector<Res::ERes> required_mines;
  421. required_mines.push_back(Res::ERes::WOOD);
  422. required_mines.push_back(Res::ERes::ORE);
  423. for(const auto res : required_mines)
  424. {
  425. auto mine = new CGMine();
  426. mine->ID = Obj::MINE;
  427. mine->subID = static_cast<si32>(res);
  428. mine->producedResource = res;
  429. mine->producedQuantity = mine->defaultResProduction();
  430. requiredObjects.push_back(mine);
  431. }
  432. }
  433. else
  434. {
  435. type = ETemplateZoneType::TREASURE;
  436. townId = *RandomGeneratorUtil::nextItem(VLC->townh->getAllowedFactions(), gen->rand);
  437. logGlobal->infoStream() << "Skipping this zone cause no player";
  438. }
  439. }
  440. else //no player
  441. {
  442. townId = *RandomGeneratorUtil::nextItem(VLC->townh->getAllowedFactions(), gen->rand);
  443. }
  444. //paint zone with matching terrain
  445. std::vector<int3> tiles;
  446. for (auto tile : tileinfo)
  447. {
  448. tiles.push_back (tile);
  449. }
  450. gen->editManager->getTerrainSelection().setSelection(tiles);
  451. gen->editManager->drawTerrain(VLC->townh->factions[townId]->nativeTerrain, &gen->rand);
  452. logGlobal->infoStream() << "Creating required objects";
  453. for(const auto &obj : requiredObjects)
  454. {
  455. int3 pos;
  456. logGlobal->traceStream() << "Looking for place";
  457. if ( ! findPlaceForObject(gen, obj, 3, pos))
  458. {
  459. logGlobal->errorStream() << boost::format("Failed to fill zone %d due to lack of space") %id;
  460. //TODO CLEANUP!
  461. return false;
  462. }
  463. logGlobal->traceStream() << "Place found";
  464. placeObject(gen, obj, pos);
  465. }
  466. std::vector<CGObjectInstance*> guarded_objects;
  467. static auto res_gen = gen->rand.getIntRange(Res::ERes::WOOD, Res::ERes::GOLD);
  468. const double res_mindist = 5;
  469. do {
  470. auto obj = new CGResource();
  471. auto restype = static_cast<Res::ERes>(res_gen());
  472. obj->ID = Obj::RESOURCE;
  473. obj->subID = static_cast<si32>(restype);
  474. obj->amount = 0;
  475. int3 pos;
  476. if ( ! findPlaceForObject(gen, obj, res_mindist, pos))
  477. {
  478. delete obj;
  479. break;
  480. }
  481. placeObject(gen, obj, pos);
  482. if ((restype != Res::ERes::WOOD) && (restype != Res::ERes::ORE))
  483. {
  484. guarded_objects.push_back(obj);
  485. }
  486. } while(true);
  487. for(const auto &obj : guarded_objects)
  488. {
  489. if ( ! guardObject(gen, obj, 500))
  490. {
  491. //TODO, DEL obj from map
  492. }
  493. }
  494. auto sel = gen->editManager->getTerrainSelection();
  495. sel.clearSelection();
  496. for (auto tile : tileinfo)
  497. {
  498. //test code - block all the map to show paths clearly
  499. //if (gen->isPossible(tile))
  500. // gen->setOccupied(tile, ETileType::BLOCKED);
  501. if (gen->shouldBeBlocked(tile)) //fill tiles that should be blocked with obstacles
  502. {
  503. auto obj = new CGObjectInstance();
  504. obj->ID = static_cast<Obj>(130);
  505. obj->subID = 0;
  506. placeObject(gen, obj, tile);
  507. }
  508. }
  509. //logGlobal->infoStream() << boost::format("Filling %d with ROCK") % sel.getSelectedItems().size();
  510. //gen->editManager->drawTerrain(ETerrainType::ROCK, &gen->gen);
  511. logGlobal->infoStream() << boost::format ("Zone %d filled successfully") %id;
  512. return true;
  513. }
  514. bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos)
  515. {
  516. //we need object apperance to deduce free tiles
  517. if (obj->appearance.id == Obj::NO_OBJ)
  518. {
  519. auto templates = VLC->dobjinfo->pickCandidates(obj->ID, obj->subID, gen->map->getTile(getPos()).terType);
  520. if (templates.empty())
  521. throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") %obj->ID %obj->subID %pos));
  522. obj->appearance = templates.front();
  523. }
  524. //si32 min_dist = sqrt(tileinfo.size()/density);
  525. int best_distance = 0;
  526. bool result = false;
  527. si32 w = gen->map->width;
  528. si32 h = gen->map->height;
  529. auto ow = obj->getWidth();
  530. auto oh = obj->getHeight();
  531. //logGlobal->infoStream() << boost::format("Min dist for density %f is %d") % density % min_dist;
  532. for(auto tile : tileinfo)
  533. {
  534. auto &ti = gen->getTile(tile);
  535. auto dist = ti.getNearestObjectDistance();
  536. //avoid borders
  537. if ((tile.x < 3) || (w - tile.x < 3) || (tile.y < 3) || (h - tile.y < 3))
  538. continue;
  539. if (gen->isPossible(tile) && (dist >= min_dist) && (dist > best_distance))
  540. {
  541. bool allTilesAvailable = true;
  542. for (auto blockingTile : obj->getBlockedOffsets())
  543. {
  544. int3 t = tile + blockingTile;
  545. if (!gen->map->isInTheMap(t) || !gen->isPossible(t))
  546. {
  547. allTilesAvailable = false; //if at least one tile is not possible, object can't be placed here
  548. break;
  549. }
  550. }
  551. if (allTilesAvailable)
  552. {
  553. best_distance = dist;
  554. pos = tile;
  555. result = true;
  556. }
  557. }
  558. }
  559. if (result)
  560. {
  561. gen->setOccupied(pos, ETileType::BLOCKED); //block that tile
  562. }
  563. return result;
  564. }
  565. void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
  566. {
  567. if (!gen->map->isInTheMap(pos))
  568. throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % object->id % object->pos()));
  569. object->pos = pos;
  570. if (object->isVisitable() && !gen->map->isInTheMap(object->visitablePos()))
  571. 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()));
  572. for (auto tile : object->getBlockedPos())
  573. {
  574. if (!gen->map->isInTheMap(tile))
  575. throw rmgException(boost::to_string(boost::format("Tile %s of object %d at %s is outside the map") % tile() % object->id % object->pos()));
  576. }
  577. if (object->appearance.id == Obj::NO_OBJ)
  578. {
  579. auto templates = VLC->dobjinfo->pickCandidates(object->ID, object->subID, gen->map->getTile(pos).terType);
  580. if (templates.empty())
  581. throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") %object->ID %object->subID %pos));
  582. object->appearance = templates.front();
  583. }
  584. gen->map->addBlockVisTiles(object);
  585. gen->editManager->insertObject(object, pos);
  586. logGlobal->traceStream() << boost::format ("Successfully inserted object (%d,%d) at pos %s") %object->ID %object->subID %pos();
  587. }
  588. void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
  589. {
  590. logGlobal->traceStream() << boost::format("Inserting object at %d %d") % pos.x % pos.y;
  591. checkAndPlaceObject (gen, object, pos);
  592. auto points = object->getBlockedPos();
  593. if (object->isVisitable())
  594. points.insert(pos + object->getVisitableOffset());
  595. points.insert(pos);
  596. for(auto const &p : points)
  597. {
  598. if (vstd::contains(tileinfo, pos + p))
  599. {
  600. gen->setOccupied(pos + p, ETileType::USED);
  601. }
  602. }
  603. for(auto tile : tileinfo)
  604. {
  605. si32 d = pos.dist2d(tile);
  606. gen->setNearestObjectDistance(tile, std::min(d, gen->getNearestObjectDistance(tile)));
  607. }
  608. }
  609. bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str)
  610. {
  611. logGlobal->traceStream() << boost::format("Guard object at %d %d") % object->pos.x % object->pos.y;
  612. int3 visitable = object->visitablePos();
  613. std::vector<int3> tiles;
  614. gen->foreach_neighbour(visitable, [&](int3& pos)
  615. {
  616. logGlobal->traceStream() << boost::format("Block at %d %d") % pos.x % pos.y;
  617. if (gen->isPossible(pos))
  618. {
  619. tiles.push_back(pos);
  620. gen->setOccupied(pos, ETileType::BLOCKED);
  621. };
  622. });
  623. if ( ! tiles.size())
  624. {
  625. logGlobal->infoStream() << "Failed";
  626. return false;
  627. }
  628. auto guard_tile = *RandomGeneratorUtil::nextItem(tiles, gen->rand);
  629. gen->setOccupied (guard_tile, ETileType::USED);
  630. auto guard = new CGCreature();
  631. guard->ID = Obj::RANDOM_MONSTER;
  632. guard->subID = 0;
  633. auto hlp = new CStackInstance();
  634. hlp->count = 10;
  635. //type will be set during initialization
  636. guard->putStack(SlotID(0), hlp);
  637. checkAndPlaceObject(gen, guard, guard_tile);
  638. return true;
  639. }