CRmgTemplateZone.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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) throw std::runtime_error("Negative value for town count not allowed.");
  28. townCount = value;
  29. }
  30. int CRmgTemplateZone::CTownInfo::getCastleCount() const
  31. {
  32. return castleCount;
  33. }
  34. void CRmgTemplateZone::CTownInfo::setCastleCount(int value)
  35. {
  36. if(value < 0) throw std::runtime_error("Negative value for castle count not allowed.");
  37. castleCount = value;
  38. }
  39. int CRmgTemplateZone::CTownInfo::getTownDensity() const
  40. {
  41. return townDensity;
  42. }
  43. void CRmgTemplateZone::CTownInfo::setTownDensity(int value)
  44. {
  45. if(value < 0) throw std::runtime_error("Negative value for town density not allowed.");
  46. townDensity = value;
  47. }
  48. int CRmgTemplateZone::CTownInfo::getCastleDensity() const
  49. {
  50. return castleDensity;
  51. }
  52. void CRmgTemplateZone::CTownInfo::setCastleDensity(int value)
  53. {
  54. if(value < 0) throw std::runtime_error("Negative value for castle density not allowed.");
  55. castleDensity = value;
  56. }
  57. CRmgTemplateZone::CTileInfo::CTileInfo():nearestObjectDistance(INT_MAX), obstacle(false), occupied(false), terrain(ETerrainType::WRONG)
  58. {
  59. }
  60. int CRmgTemplateZone::CTileInfo::getNearestObjectDistance() const
  61. {
  62. return nearestObjectDistance;
  63. }
  64. void CRmgTemplateZone::CTileInfo::setNearestObjectDistance(int value)
  65. {
  66. if(value < 0) throw std::runtime_error("Negative value for nearest object distance not allowed.");
  67. nearestObjectDistance = value;
  68. }
  69. bool CRmgTemplateZone::CTileInfo::isObstacle() const
  70. {
  71. return obstacle;
  72. }
  73. void CRmgTemplateZone::CTileInfo::setObstacle(bool value)
  74. {
  75. obstacle = value;
  76. }
  77. bool CRmgTemplateZone::CTileInfo::isOccupied() const
  78. {
  79. return occupied;
  80. }
  81. void CRmgTemplateZone::CTileInfo::setOccupied(bool value)
  82. {
  83. occupied = value;
  84. }
  85. ETerrainType CRmgTemplateZone::CTileInfo::getTerrainType() const
  86. {
  87. return terrain;
  88. }
  89. void CRmgTemplateZone::CTileInfo::setTerrainType(ETerrainType value)
  90. {
  91. terrain = value;
  92. }
  93. CRmgTemplateZone::CRmgTemplateZone() : id(0), type(ETemplateZoneType::PLAYER_START), size(1),
  94. townsAreSameType(false), matchTerrainToTown(true)
  95. {
  96. townTypes = getDefaultTownTypes();
  97. terrainTypes = getDefaultTerrainTypes();
  98. }
  99. TRmgTemplateZoneId CRmgTemplateZone::getId() const
  100. {
  101. return id;
  102. }
  103. void CRmgTemplateZone::setId(TRmgTemplateZoneId value)
  104. {
  105. if(value <= 0) throw std::runtime_error("Zone id should be greater than 0.");
  106. id = value;
  107. }
  108. ETemplateZoneType::ETemplateZoneType CRmgTemplateZone::getType() const
  109. {
  110. return type;
  111. }
  112. void CRmgTemplateZone::setType(ETemplateZoneType::ETemplateZoneType value)
  113. {
  114. type = value;
  115. }
  116. int CRmgTemplateZone::getSize() const
  117. {
  118. return size;
  119. }
  120. void CRmgTemplateZone::setSize(int value)
  121. {
  122. if(value <= 0) throw std::runtime_error("Zone size needs to be greater than 0.");
  123. size = value;
  124. }
  125. boost::optional<int> CRmgTemplateZone::getOwner() const
  126. {
  127. return owner;
  128. }
  129. void CRmgTemplateZone::setOwner(boost::optional<int> value)
  130. {
  131. if(!(*value >= 0 && *value <= PlayerColor::PLAYER_LIMIT_I)) throw std::runtime_error("Owner has to be in range 0 to max player count.");
  132. owner = value;
  133. }
  134. const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getPlayerTowns() const
  135. {
  136. return playerTowns;
  137. }
  138. void CRmgTemplateZone::setPlayerTowns(const CTownInfo & value)
  139. {
  140. playerTowns = value;
  141. }
  142. const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getNeutralTowns() const
  143. {
  144. return neutralTowns;
  145. }
  146. void CRmgTemplateZone::setNeutralTowns(const CTownInfo & value)
  147. {
  148. neutralTowns = value;
  149. }
  150. bool CRmgTemplateZone::getTownsAreSameType() const
  151. {
  152. return townsAreSameType;
  153. }
  154. void CRmgTemplateZone::setTownsAreSameType(bool value)
  155. {
  156. townsAreSameType = value;
  157. }
  158. const std::set<TFaction> & CRmgTemplateZone::getTownTypes() const
  159. {
  160. return townTypes;
  161. }
  162. void CRmgTemplateZone::setTownTypes(const std::set<TFaction> & value)
  163. {
  164. townTypes = value;
  165. }
  166. std::set<TFaction> CRmgTemplateZone::getDefaultTownTypes() const
  167. {
  168. std::set<TFaction> defaultTowns;
  169. auto towns = VLC->townh->getDefaultAllowed();
  170. for(int i = 0; i < towns.size(); ++i)
  171. {
  172. if(towns[i]) defaultTowns.insert(i);
  173. }
  174. return defaultTowns;
  175. }
  176. bool CRmgTemplateZone::getMatchTerrainToTown() const
  177. {
  178. return matchTerrainToTown;
  179. }
  180. void CRmgTemplateZone::setMatchTerrainToTown(bool value)
  181. {
  182. matchTerrainToTown = value;
  183. }
  184. const std::set<ETerrainType> & CRmgTemplateZone::getTerrainTypes() const
  185. {
  186. return terrainTypes;
  187. }
  188. void CRmgTemplateZone::setTerrainTypes(const std::set<ETerrainType> & value)
  189. {
  190. assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() &&
  191. value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end());
  192. terrainTypes = value;
  193. }
  194. std::set<ETerrainType> CRmgTemplateZone::getDefaultTerrainTypes() const
  195. {
  196. std::set<ETerrainType> terTypes;
  197. static const ETerrainType::EETerrainType allowedTerTypes[] = { ETerrainType::DIRT, ETerrainType::SAND, ETerrainType::GRASS, ETerrainType::SNOW,
  198. ETerrainType::SWAMP, ETerrainType::ROUGH, ETerrainType::SUBTERRANEAN, ETerrainType::LAVA };
  199. for(auto & allowedTerType : allowedTerTypes) terTypes.insert(allowedTerType);
  200. return terTypes;
  201. }
  202. boost::optional<TRmgTemplateZoneId> CRmgTemplateZone::getTerrainTypeLikeZone() const
  203. {
  204. return terrainTypeLikeZone;
  205. }
  206. void CRmgTemplateZone::setTerrainTypeLikeZone(boost::optional<TRmgTemplateZoneId> value)
  207. {
  208. terrainTypeLikeZone = value;
  209. }
  210. boost::optional<TRmgTemplateZoneId> CRmgTemplateZone::getTownTypeLikeZone() const
  211. {
  212. return townTypeLikeZone;
  213. }
  214. void CRmgTemplateZone::setTownTypeLikeZone(boost::optional<TRmgTemplateZoneId> value)
  215. {
  216. townTypeLikeZone = value;
  217. }
  218. bool CRmgTemplateZone::pointIsIn(int x, int y)
  219. {
  220. int i, j;
  221. bool c = false;
  222. int nvert = shape.size();
  223. for (i = 0, j = nvert-1; i < nvert; j = i++) {
  224. if ( ((shape[i].y>y) != (shape[j].y>y)) &&
  225. (x < (shape[j].x-shape[i].x) * (y-shape[i].y) / (shape[j].y-shape[i].y) + shape[i].x) )
  226. c = !c;
  227. }
  228. return c;
  229. }
  230. void CRmgTemplateZone::setShape(std::vector<int3> shape)
  231. {
  232. int z = -1;
  233. si32 minx = INT_MAX;
  234. si32 maxx = -1;
  235. si32 miny = INT_MAX;
  236. si32 maxy = -1;
  237. for(auto &point : shape)
  238. {
  239. if (z == -1)
  240. z = point.z;
  241. if (point.z != z)
  242. throw std::runtime_error("Zone shape points should lie on same z.");
  243. minx = std::min(minx, point.x);
  244. maxx = std::max(maxx, point.x);
  245. miny = std::min(miny, point.y);
  246. maxy = std::max(maxy, point.y);
  247. }
  248. this->shape = shape;
  249. for(int x = minx; x <= maxx; ++x)
  250. {
  251. for(int y = miny; y <= maxy; ++y)
  252. {
  253. if (pointIsIn(x, y))
  254. {
  255. tileinfo[int3(x,y,z)] = CTileInfo();
  256. }
  257. }
  258. }
  259. }
  260. int3 CRmgTemplateZone::getCenter()
  261. {
  262. si32 cx = 0;
  263. si32 cy = 0;
  264. si32 area = 0;
  265. si32 sz = shape.size();
  266. //include last->first too
  267. for(si32 i = 0, j = sz-1; i < sz; j = i++) {
  268. si32 sf = (shape[i].x * shape[j].y - shape[j].x * shape[i].y);
  269. cx += (shape[i].x + shape[j].x) * sf;
  270. cy += (shape[i].y + shape[j].y) * sf;
  271. area += sf;
  272. }
  273. area /= 2;
  274. return int3(std::abs(cx/area/6), std::abs(cy/area/6), shape[0].z);
  275. }
  276. bool CRmgTemplateZone::fill(CMapGenerator* gen)
  277. {
  278. std::vector<CGObjectInstance*> required_objects;
  279. if ((type == ETemplateZoneType::CPU_START) || (type == ETemplateZoneType::PLAYER_START))
  280. {
  281. logGlobal->infoStream() << "Preparing playing zone";
  282. int player_id = *owner - 1;
  283. auto & playerInfo = gen->map->players[player_id];
  284. if (playerInfo.canAnyonePlay())
  285. {
  286. PlayerColor player(player_id);
  287. auto town = new CGTownInstance();
  288. town->ID = Obj::TOWN;
  289. int townId = gen->mapGenOptions->getPlayersSettings().find(player)->second.getStartingTown();
  290. if(townId == CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
  291. townId = gen->rand.nextInt (VLC->townh->factions.size()); // all possible towns
  292. town->subID = townId;
  293. town->tempOwner = player;
  294. town->builtBuildings.insert(BuildingID::FORT);
  295. town->builtBuildings.insert(BuildingID::DEFAULT);
  296. placeObject(gen, town, getCenter());
  297. logGlobal->infoStream() << "Placed object";
  298. logGlobal->infoStream() << "Fill player info " << player_id;
  299. auto & playerInfo = gen->map->players[player_id];
  300. // Update player info
  301. playerInfo.allowedFactions.clear();
  302. playerInfo.allowedFactions.insert(town->subID);
  303. playerInfo.hasMainTown = true;
  304. playerInfo.posOfMainTown = town->pos - int3(2, 0, 0);
  305. playerInfo.generateHeroAtMainTown = true;
  306. //required_objects.push_back(town);
  307. std::vector<Res::ERes> required_mines;
  308. required_mines.push_back(Res::ERes::WOOD);
  309. required_mines.push_back(Res::ERes::ORE);
  310. for(const auto res : required_mines)
  311. {
  312. auto mine = new CGMine();
  313. mine->ID = Obj::MINE;
  314. mine->subID = static_cast<si32>(res);
  315. mine->producedResource = res;
  316. mine->producedQuantity = mine->defaultResProduction();
  317. required_objects.push_back(mine);
  318. }
  319. }
  320. else
  321. {
  322. type = ETemplateZoneType::TREASURE;
  323. logGlobal->infoStream() << "Skipping this zone cause no player";
  324. }
  325. }
  326. logGlobal->infoStream() << "Creating required objects";
  327. for(const auto &obj : required_objects)
  328. {
  329. int3 pos;
  330. logGlobal->infoStream() << "Looking for place";
  331. if ( ! findPlaceForObject(gen, obj, 3, pos))
  332. {
  333. logGlobal->errorStream() << "Failed to fill zone due to lack of space";
  334. //TODO CLEANUP!
  335. return false;
  336. }
  337. logGlobal->infoStream() << "Place found";
  338. placeObject(gen, obj, pos);
  339. logGlobal->infoStream() << "Placed object";
  340. }
  341. std::vector<CGObjectInstance*> guarded_objects;
  342. static auto res_gen = gen->rand.getIntRange(Res::ERes::WOOD, Res::ERes::GOLD);
  343. const double res_mindist = 5;
  344. do {
  345. auto obj = new CGResource();
  346. auto restype = static_cast<Res::ERes>(res_gen());
  347. obj->ID = Obj::RESOURCE;
  348. obj->subID = static_cast<si32>(restype);
  349. obj->amount = 0;
  350. int3 pos;
  351. if ( ! findPlaceForObject(gen, obj, res_mindist, pos))
  352. {
  353. delete obj;
  354. break;
  355. }
  356. placeObject(gen, obj, pos);
  357. if ((restype != Res::ERes::WOOD) && (restype != Res::ERes::ORE))
  358. {
  359. guarded_objects.push_back(obj);
  360. }
  361. } while(true);
  362. for(const auto &obj : guarded_objects)
  363. {
  364. if ( ! guardObject(gen, obj, 500))
  365. {
  366. //TODO, DEL obj from map
  367. }
  368. }
  369. auto sel = gen->editManager->getTerrainSelection();
  370. sel.clearSelection();
  371. for(auto it = tileinfo.begin(); it != tileinfo.end(); ++it)
  372. {
  373. if (it->second.isObstacle())
  374. {
  375. auto obj = new CGObjectInstance();
  376. obj->ID = static_cast<Obj>(130);
  377. obj->subID = 0;
  378. placeObject(gen, obj, it->first);
  379. }
  380. }
  381. logGlobal->infoStream() << boost::format("Filling %d with ROCK") % sel.getSelectedItems().size();
  382. //gen->editManager->drawTerrain(ETerrainType::ROCK, &gen->gen);
  383. logGlobal->infoStream() << "Zone filled successfully";
  384. return true;
  385. }
  386. bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos)
  387. {
  388. //si32 min_dist = sqrt(tileinfo.size()/density);
  389. int best_distance = 0;
  390. bool result = false;
  391. si32 w = gen->map->width;
  392. si32 h = gen->map->height;
  393. auto ow = obj->getWidth();
  394. auto oh = obj->getHeight();
  395. //logGlobal->infoStream() << boost::format("Min dist for density %f is %d") % density % min_dist;
  396. for(auto it = tileinfo.begin(); it != tileinfo.end(); ++it)
  397. {
  398. auto &ti = it->second;
  399. auto p = it->first;
  400. auto dist = ti.getNearestObjectDistance();
  401. //avoid borders
  402. if ((p.x < 3) || (w - p.x < 3) || (p.y < 3) || (h - p.y < 3))
  403. continue;
  404. if (!ti.isOccupied() && !ti.isObstacle() && (dist >= min_dist) && (dist > best_distance))
  405. {
  406. best_distance = dist;
  407. pos = p;
  408. result = true;
  409. }
  410. }
  411. return result;
  412. }
  413. void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
  414. {
  415. logGlobal->infoStream() << boost::format("Insert object at %d %d") % pos.x % pos.y;
  416. object->pos = pos;
  417. gen->editManager->insertObject(object, pos);
  418. logGlobal->infoStream() << "Inserted object";
  419. auto points = object->getBlockedPos();
  420. if (object->isVisitable())
  421. points.emplace(pos + object->getVisitableOffset());
  422. points.emplace(pos);
  423. for(auto const &p : points)
  424. {
  425. if (tileinfo.find(pos + p) != tileinfo.end())
  426. {
  427. tileinfo[pos + p].setOccupied(true);
  428. }
  429. }
  430. for(auto it = tileinfo.begin(); it != tileinfo.end(); ++it)
  431. {
  432. si32 d = pos.dist2d(it->first);
  433. it->second.setNearestObjectDistance(std::min(d, it->second.getNearestObjectDistance()));
  434. }
  435. }
  436. bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str)
  437. {
  438. logGlobal->infoStream() << boost::format("Guard object at %d %d") % object->pos.x % object->pos.y;
  439. int3 visitable = object->pos + object->getVisitableOffset();
  440. std::vector<int3> tiles;
  441. for(int i = -1; i < 2; ++i)
  442. {
  443. for(int j = -1; j < 2; ++j)
  444. {
  445. auto it = tileinfo.find(visitable + int3(i, j, 0));
  446. if (it != tileinfo.end())
  447. {
  448. logGlobal->infoStream() << boost::format("Block at %d %d") % it->first.x % it->first.y;
  449. if ( ! it->second.isOccupied() && ! it->second.isObstacle())
  450. {
  451. tiles.push_back(it->first);
  452. it->second.setObstacle(true);
  453. }
  454. }
  455. }
  456. }
  457. if ( ! tiles.size())
  458. {
  459. logGlobal->infoStream() << "Failed";
  460. return false;
  461. }
  462. auto guard_tile = *std::next(tiles.begin(), gen->rand.nextInt(tiles.size()));
  463. tileinfo[guard_tile].setObstacle(false);
  464. auto guard = new CGCreature();
  465. guard->ID = Obj::RANDOM_MONSTER;
  466. guard->subID = 0;
  467. auto hlp = new CStackInstance();
  468. hlp->count = 10;
  469. //type will be set during initialization
  470. guard->putStack(SlotID(0), hlp);
  471. guard->pos = guard_tile;
  472. gen->editManager->insertObject(guard, guard->pos);
  473. return true;
  474. }