2
0

SectorMap.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /*
  2. * SectorMap.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 "SectorMap.h"
  12. #include "VCAI.h"
  13. #include "../../CCallback.h"
  14. #include "../../lib/mapping/CMap.h"
  15. #include "../../lib/mapObjects/MapObjects.h"
  16. #include "../../lib/CPathfinder.h"
  17. #include "../../lib/CGameState.h"
  18. extern boost::thread_specific_ptr<CCallback> cb;
  19. extern boost::thread_specific_ptr<VCAI> ai;
  20. SectorMap::SectorMap()
  21. {
  22. update();
  23. }
  24. SectorMap::SectorMap(HeroPtr h)
  25. {
  26. update();
  27. makeParentBFS(h->visitablePos());
  28. }
  29. bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t)
  30. {
  31. if (t->blocked && !t->visitable)
  32. {
  33. sec = NOT_AVAILABLE;
  34. return true;
  35. }
  36. return false;
  37. }
  38. bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos)
  39. {
  40. return markIfBlocked(sec, pos, getTile(pos));
  41. }
  42. void SectorMap::update()
  43. {
  44. visibleTiles = cb->getAllVisibleTiles();
  45. auto shape = visibleTiles->shape();
  46. sector.resize(boost::extents[shape[0]][shape[1]][shape[2]]);
  47. clear();
  48. int curSector = 3; //0 is invisible, 1 is not explored
  49. CCallback * cbp = cb.get(); //optimization
  50. foreach_tile_pos([&](crint3 pos)
  51. {
  52. if (retrieveTile(pos) == NOT_CHECKED)
  53. {
  54. if (!markIfBlocked(retrieveTile(pos), pos))
  55. exploreNewSector(pos, curSector++, cbp);
  56. }
  57. });
  58. valid = true;
  59. }
  60. SectorMap::TSectorID & SectorMap::retrieveTileN(SectorMap::TSectorArray & a, const int3 & pos)
  61. {
  62. return a[pos.x][pos.y][pos.z];
  63. }
  64. const SectorMap::TSectorID & SectorMap::retrieveTileN(const SectorMap::TSectorArray & a, const int3 & pos)
  65. {
  66. return a[pos.x][pos.y][pos.z];
  67. }
  68. void SectorMap::clear()
  69. {
  70. //TODO: rotate to [z][x][y]
  71. auto fow = cb->getVisibilityMap();
  72. //TODO: any magic to automate this? will need array->array conversion
  73. //std::transform(fow.begin(), fow.end(), sector.begin(), [](const ui8 &f) -> unsigned short
  74. //{
  75. // return f; //type conversion
  76. //});
  77. auto width = fow.size();
  78. auto height = fow.front().size();
  79. auto depth = fow.front().front().size();
  80. for (size_t x = 0; x < width; x++)
  81. {
  82. for (size_t y = 0; y < height; y++)
  83. {
  84. for (size_t z = 0; z < depth; z++)
  85. sector[x][y][z] = fow[x][y][z];
  86. }
  87. }
  88. valid = false;
  89. }
  90. void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
  91. {
  92. Sector & s = infoOnSectors[num];
  93. s.id = num;
  94. s.water = getTile(pos)->isWater();
  95. std::queue<int3> toVisit;
  96. toVisit.push(pos);
  97. while (!toVisit.empty())
  98. {
  99. int3 curPos = toVisit.front();
  100. toVisit.pop();
  101. TSectorID & sec = retrieveTile(curPos);
  102. if (sec == NOT_CHECKED)
  103. {
  104. const TerrainTile * t = getTile(curPos);
  105. if (!markIfBlocked(sec, curPos, t))
  106. {
  107. if (t->isWater() == s.water) //sector is only-water or only-land
  108. {
  109. sec = num;
  110. s.tiles.push_back(curPos);
  111. foreach_neighbour(cbp, curPos, [&](CCallback * cbp, crint3 neighPos)
  112. {
  113. if (retrieveTile(neighPos) == NOT_CHECKED)
  114. {
  115. toVisit.push(neighPos);
  116. //parent[neighPos] = curPos;
  117. }
  118. const TerrainTile * nt = getTile(neighPos);
  119. if (nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water))
  120. {
  121. s.embarkmentPoints.push_back(neighPos);
  122. }
  123. });
  124. if (t->visitable)
  125. {
  126. auto obj = t->visitableObjects.front();
  127. if (cb->getObj(obj->id, false)) // FIXME: we have to filter invisible objcts like events, but probably TerrainTile shouldn't be used in SectorMap at all
  128. s.visitableObjs.push_back(obj);
  129. }
  130. }
  131. }
  132. }
  133. }
  134. vstd::removeDuplicates(s.embarkmentPoints);
  135. }
  136. void SectorMap::write(crstring fname)
  137. {
  138. std::ofstream out(fname);
  139. for (int k = 0; k < cb->getMapSize().z; k++)
  140. {
  141. for (int j = 0; j < cb->getMapSize().y; j++)
  142. {
  143. for (int i = 0; i < cb->getMapSize().x; i++)
  144. {
  145. out << (int)sector[i][j][k] << '\t';
  146. }
  147. out << std::endl;
  148. }
  149. out << std::endl;
  150. }
  151. }
  152. /*
  153. this functions returns one target tile or invalid tile. We will use it to poll possible destinations
  154. For ship construction etc, another function (goal?) is needed
  155. */
  156. int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
  157. {
  158. int3 ret(-1, -1, -1);
  159. int sourceSector = retrieveTile(h->visitablePos());
  160. int destinationSector = retrieveTile(dst);
  161. const Sector * src = &infoOnSectors[sourceSector];
  162. const Sector * dest = &infoOnSectors[destinationSector];
  163. if (sourceSector != destinationSector) //use ships, shipyards etc..
  164. {
  165. if (ai->isAccessibleForHero(dst, h)) //pathfinder can find a way using ships and gates if tile is not blocked by objects
  166. return dst;
  167. std::map<const Sector *, const Sector *> preds;
  168. std::queue<const Sector *> sectorQueue;
  169. sectorQueue.push(src);
  170. while (!sectorQueue.empty())
  171. {
  172. const Sector * s = sectorQueue.front();
  173. sectorQueue.pop();
  174. for (int3 ep : s->embarkmentPoints)
  175. {
  176. Sector * neigh = &infoOnSectors[retrieveTile(ep)];
  177. //preds[s].push_back(neigh);
  178. if (!preds[neigh])
  179. {
  180. preds[neigh] = s;
  181. sectorQueue.push(neigh);
  182. }
  183. }
  184. }
  185. if (!preds[dest])
  186. {
  187. //write("test.txt");
  188. return ret;
  189. //throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
  190. }
  191. std::vector<const Sector *> toTraverse;
  192. toTraverse.push_back(dest);
  193. while (toTraverse.back() != src)
  194. {
  195. toTraverse.push_back(preds[toTraverse.back()]);
  196. }
  197. if (preds[dest])
  198. {
  199. //TODO: would be nice to find sectors in loop
  200. const Sector * sectorToReach = toTraverse.at(toTraverse.size() - 2);
  201. if (!src->water && sectorToReach->water) //embark
  202. {
  203. //embark on ship -> look for an EP with a boat
  204. auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool
  205. {
  206. const TerrainTile * t = getTile(pos);
  207. if (t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
  208. {
  209. if (retrieveTile(pos) == sectorToReach->id)
  210. return true;
  211. }
  212. return false;
  213. });
  214. if (firstEP != src->embarkmentPoints.end())
  215. {
  216. return *firstEP;
  217. }
  218. else
  219. {
  220. //we need to find a shipyard with an access to the desired sector's EP
  221. //TODO what about Summon Boat spell?
  222. std::vector<const IShipyard *> shipyards;
  223. for (const CGTownInstance * t : cb->getTownsInfo())
  224. {
  225. if (t->hasBuilt(BuildingID::SHIPYARD))
  226. shipyards.push_back(t);
  227. }
  228. for (const CGObjectInstance * obj : ai->getFlaggedObjects())
  229. {
  230. if (obj->ID != Obj::TOWN) //towns were handled in the previous loop
  231. {
  232. if (const IShipyard * shipyard = IShipyard::castFrom(obj))
  233. shipyards.push_back(shipyard);
  234. }
  235. }
  236. shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool
  237. {
  238. return shipyard->shipyardStatus() != 0 || retrieveTile(shipyard->bestLocation()) != sectorToReach->id;
  239. }), shipyards.end());
  240. if (!shipyards.size())
  241. {
  242. //TODO consider possibility of building shipyard in a town
  243. return ret;
  244. //throw cannotFulfillGoalException("There is no known shipyard!");
  245. }
  246. //we have only shipyards that possibly can build ships onto the appropriate EP
  247. auto ownedGoodShipyard = boost::find_if(shipyards, [](const IShipyard * s) -> bool
  248. {
  249. return s->o->tempOwner == ai->playerID;
  250. });
  251. if (ownedGoodShipyard != shipyards.end())
  252. {
  253. const IShipyard * s = *ownedGoodShipyard;
  254. TResources shipCost;
  255. s->getBoatCost(shipCost);
  256. if (cb->getResourceAmount().canAfford(shipCost))
  257. {
  258. int3 ret = s->bestLocation();
  259. cb->buildBoat(s); //TODO: move actions elsewhere
  260. return ret;
  261. }
  262. else
  263. {
  264. //TODO gather res
  265. return ret;
  266. //throw cannotFulfillGoalException("Not enough resources to build a boat");
  267. }
  268. }
  269. else
  270. {
  271. //TODO pick best shipyard to take over
  272. return shipyards.front()->o->visitablePos();
  273. }
  274. }
  275. }
  276. else if (src->water && !sectorToReach->water)
  277. {
  278. //TODO
  279. //disembark
  280. return ret;
  281. }
  282. else //use subterranean gates - not needed since gates are now handled via Pathfinder
  283. {
  284. return ret;
  285. //throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
  286. }
  287. }
  288. else
  289. {
  290. return ret;
  291. //throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?");
  292. }
  293. }
  294. else //tiles are in same sector
  295. {
  296. return findFirstVisitableTile(h, dst);
  297. }
  298. }
  299. int3 SectorMap::findFirstVisitableTile(HeroPtr h, crint3 dst)
  300. {
  301. int3 ret(-1, -1, -1);
  302. int3 curtile = dst;
  303. while (curtile != h->visitablePos())
  304. {
  305. auto topObj = cb->getTopObj(curtile);
  306. if (topObj && topObj->ID == Obj::HERO && topObj != h.h)
  307. {
  308. if (cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
  309. {
  310. logAi->warn("Another allied hero stands in our way");
  311. return ret;
  312. }
  313. }
  314. if (ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable())
  315. {
  316. return curtile;
  317. }
  318. else
  319. {
  320. auto i = parent.find(curtile);
  321. if (i != parent.end())
  322. {
  323. assert(curtile != i->second);
  324. curtile = i->second;
  325. }
  326. else
  327. {
  328. return ret;
  329. //throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!");
  330. }
  331. }
  332. }
  333. return ret;
  334. }
  335. void SectorMap::makeParentBFS(crint3 source)
  336. {
  337. parent.clear();
  338. int mySector = retrieveTile(source);
  339. std::queue<int3> toVisit;
  340. toVisit.push(source);
  341. while (!toVisit.empty())
  342. {
  343. int3 curPos = toVisit.front();
  344. toVisit.pop();
  345. TSectorID & sec = retrieveTile(curPos);
  346. assert(sec == mySector); //consider only tiles from the same sector
  347. UNUSED(sec);
  348. foreach_neighbour(curPos, [&](crint3 neighPos)
  349. {
  350. if (retrieveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
  351. {
  352. if (cb->canMoveBetween(curPos, neighPos))
  353. {
  354. toVisit.push(neighPos);
  355. parent[neighPos] = curPos;
  356. }
  357. }
  358. });
  359. }
  360. }
  361. SectorMap::TSectorID & SectorMap::retrieveTile(crint3 pos)
  362. {
  363. return retrieveTileN(sector, pos);
  364. }
  365. TerrainTile * SectorMap::getTile(crint3 pos) const
  366. {
  367. //out of bounds access should be handled by boost::multi_array
  368. //still we cached this array to avoid any checks
  369. return visibleTiles->operator[](pos.x)[pos.y][pos.z];
  370. }
  371. std::vector<const CGObjectInstance *> SectorMap::getNearbyObjs(HeroPtr h, bool sectorsAround)
  372. {
  373. const Sector * heroSector = &infoOnSectors[retrieveTile(h->visitablePos())];
  374. if (sectorsAround)
  375. {
  376. std::vector<const CGObjectInstance *> ret;
  377. for (auto embarkPoint : heroSector->embarkmentPoints)
  378. {
  379. const Sector * embarkSector = &infoOnSectors[retrieveTile(embarkPoint)];
  380. range::copy(embarkSector->visitableObjs, std::back_inserter(ret));
  381. }
  382. return ret;
  383. }
  384. return heroSector->visitableObjs;
  385. }