MapRenderer.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /*
  2. * MapRenderer.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 "MapRenderer.h"
  12. #include "MapRendererContext.h"
  13. #include "mapHandler.h"
  14. #include "../CGameInfo.h"
  15. #include "../render/CAnimation.h"
  16. #include "../render/Canvas.h"
  17. #include "../render/IImage.h"
  18. #include "../../CCallback.h"
  19. #include "../../lib/RiverHandler.h"
  20. #include "../../lib/RoadHandler.h"
  21. #include "../../lib/TerrainHandler.h"
  22. #include "../../lib/mapObjects/CGHeroInstance.h"
  23. #include "../../lib/mapping/CMap.h"
  24. #include "../../lib/CPathfinder.h"
  25. struct NeighborTilesInfo
  26. {
  27. bool d7, //789
  28. d8, //456
  29. d9, //123
  30. d4,
  31. d5,
  32. d6,
  33. d1,
  34. d2,
  35. d3;
  36. NeighborTilesInfo( const IMapRendererContext & context, const int3 & pos)
  37. {
  38. auto getTile = [&](int dx, int dy)->bool
  39. {
  40. if ( dx + pos.x < 0 || dx + pos.x >= context.getMapSize().x
  41. || dy + pos.y < 0 || dy + pos.y >= context.getMapSize().y)
  42. return false;
  43. //FIXME: please do not read settings for every tile...
  44. return context.isVisible( pos + int3(dx, dy, 0));
  45. };
  46. d7 = getTile(-1, -1); //789
  47. d8 = getTile( 0, -1); //456
  48. d9 = getTile(+1, -1); //123
  49. d4 = getTile(-1, 0);
  50. d5 = getTile( 0, 0);
  51. d6 = getTile(+1, 0);
  52. d1 = getTile(-1, +1);
  53. d2 = getTile( 0, +1);
  54. d3 = getTile(+1, +1);
  55. }
  56. bool areAllHidden() const
  57. {
  58. return !(d1 || d2 || d3 || d4 || d5 || d6 || d7 || d8 || d9);
  59. }
  60. int getBitmapID() const
  61. {
  62. //NOTE: some images have unused in VCMI pair (same blockmap but a bit different look)
  63. // 0-1, 2-3, 4-5, 11-13, 12-14
  64. static const int visBitmaps[256] = {
  65. -1, 34, 4, 4, 22, 23, 4, 4, 36, 36, 38, 38, 47, 47, 38, 38, //16
  66. 3, 25, 12, 12, 3, 25, 12, 12, 9, 9, 6, 6, 9, 9, 6, 6, //32
  67. 35, 39, 48, 48, 41, 43, 48, 48, 36, 36, 38, 38, 47, 47, 38, 38, //48
  68. 26, 49, 28, 28, 26, 49, 28, 28, 9, 9, 6, 6, 9, 9, 6, 6, //64
  69. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //80
  70. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //96
  71. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //112
  72. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //128
  73. 15, 17, 30, 30, 16, 19, 30, 30, 46, 46, 40, 40, 32, 32, 40, 40, //144
  74. 2, 25, 12, 12, 2, 25, 12, 12, 9, 9, 6, 6, 9, 9, 6, 6, //160
  75. 18, 42, 31, 31, 20, 21, 31, 31, 46, 46, 40, 40, 32, 32, 40, 40, //176
  76. 26, 49, 28, 28, 26, 49, 28, 28, 9, 9, 6, 6, 9, 9, 6, 6, //192
  77. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //208
  78. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //224
  79. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //240
  80. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10 //256
  81. };
  82. return visBitmaps[d1 + d2 * 2 + d3 * 4 + d4 * 8 + d6 * 16 + d7 * 32 + d8 * 64 + d9 * 128]; // >=0 -> partial hide, <0 - full hide
  83. }
  84. };
  85. MapObjectsSorter::MapObjectsSorter(const IMapRendererContext & context)
  86. : context(context)
  87. {
  88. }
  89. bool MapObjectsSorter::operator()(const ObjectInstanceID & left, const ObjectInstanceID & right) const
  90. {
  91. return (*this)(context.getObject(left), context.getObject(right));
  92. }
  93. bool MapObjectsSorter::operator()(const CGObjectInstance * left, const CGObjectInstance * right) const
  94. {
  95. //FIXME: remove mh access
  96. return CGI->mh->compareObjectBlitOrder(left, right);
  97. }
  98. MapTileStorage::MapTileStorage(size_t capacity)
  99. : animations(capacity)
  100. {
  101. }
  102. void MapTileStorage::load(size_t index, const std::string & filename)
  103. {
  104. auto & terrainAnimations = animations[index];
  105. for(auto & entry : terrainAnimations)
  106. {
  107. entry = std::make_unique<CAnimation>(filename);
  108. entry->preload();
  109. }
  110. for(size_t i = 0; i < terrainAnimations[0]->size(); ++i)
  111. {
  112. terrainAnimations[1]->getImage(i)->verticalFlip();
  113. terrainAnimations[3]->getImage(i)->verticalFlip();
  114. terrainAnimations[2]->getImage(i)->horizontalFlip();
  115. terrainAnimations[3]->getImage(i)->horizontalFlip();
  116. }
  117. }
  118. std::shared_ptr<IImage> MapTileStorage::find(size_t fileIndex, size_t rotationIndex, size_t imageIndex)
  119. {
  120. const auto & animation = animations[fileIndex][rotationIndex];
  121. return animation->getImage(imageIndex);
  122. }
  123. MapRendererTerrain::MapRendererTerrain()
  124. : storage(VLC->terrainTypeHandler->objects.size())
  125. {
  126. for(const auto & terrain : VLC->terrainTypeHandler->objects)
  127. storage.load(terrain->getIndex(), terrain->tilesFilename);
  128. }
  129. void MapRendererTerrain::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  130. {
  131. const TerrainTile & mapTile = context.getMapTile(coordinates);
  132. int32_t terrainIndex = mapTile.terType->getIndex();
  133. int32_t imageIndex = mapTile.terView;
  134. int32_t rotationIndex = mapTile.extTileFlags % 4;
  135. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  136. target.draw(image, Point(0, 0));
  137. }
  138. MapRendererRiver::MapRendererRiver()
  139. : storage(VLC->riverTypeHandler->objects.size())
  140. {
  141. for(const auto & river : VLC->riverTypeHandler->objects)
  142. storage.load(river->getIndex(), river->tilesFilename);
  143. }
  144. void MapRendererRiver::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  145. {
  146. const TerrainTile & mapTile = context.getMapTile(coordinates);
  147. if(mapTile.riverType->getId() != River::NO_RIVER)
  148. {
  149. int32_t terrainIndex = mapTile.riverType->getIndex();
  150. int32_t imageIndex = mapTile.riverDir;
  151. int32_t rotationIndex = (mapTile.extTileFlags >> 2) % 4;
  152. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  153. target.draw(image, Point(0, 0));
  154. }
  155. }
  156. MapRendererRoad::MapRendererRoad()
  157. : storage(VLC->roadTypeHandler->objects.size())
  158. {
  159. for(const auto & road : VLC->roadTypeHandler->objects)
  160. storage.load(road->getIndex(), road->tilesFilename);
  161. }
  162. void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  163. {
  164. const int3 coordinatesAbove = coordinates - int3(0, 1, 0);
  165. if(context.isInMap(coordinatesAbove))
  166. {
  167. const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove);
  168. if(mapTileAbove.roadType->getId() != Road::NO_ROAD)
  169. {
  170. int32_t terrainIndex = mapTileAbove.roadType->getIndex();
  171. int32_t imageIndex = mapTileAbove.roadDir;
  172. int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4;
  173. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  174. target.draw(image, Point(0, 0), Rect(0, 16, 32, 16));
  175. }
  176. }
  177. const TerrainTile & mapTile = context.getMapTile(coordinates);
  178. if(mapTile.roadType->getId() != Road::NO_ROAD)
  179. {
  180. int32_t terrainIndex = mapTile.roadType->getIndex();
  181. int32_t imageIndex = mapTile.roadDir;
  182. int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4;
  183. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  184. target.draw(image, Point(0, 16), Rect(0, 0, 32, 16));
  185. }
  186. }
  187. MapRendererBorder::MapRendererBorder()
  188. {
  189. animation = std::make_unique<CAnimation>("EDG");
  190. animation->preload();
  191. }
  192. size_t MapRendererBorder::getIndexForTile(const IMapRendererContext & context, const int3 & tile)
  193. {
  194. assert(!context.isInMap(tile));
  195. int3 size = context.getMapSize();
  196. if(tile.x < -1 || tile.x > size.x || tile.y < -1 || tile.y > size.y)
  197. return std::abs(tile.x) % 4 + 4 * (std::abs(tile.y) % 4);
  198. if(tile.x == -1 && tile.y == -1)
  199. return 16;
  200. if(tile.x == size.x && tile.y == -1)
  201. return 17;
  202. if(tile.x == size.x && tile.y == size.y)
  203. return 18;
  204. if(tile.x == -1 && tile.y == size.y)
  205. return 19;
  206. if(tile.y == -1)
  207. return 20 + (tile.x % 4);
  208. if(tile.x == size.x)
  209. return 24 + (tile.y % 4);
  210. if(tile.y == size.y)
  211. return 28 + (tile.x % 4);
  212. if(tile.x == -1)
  213. return 32 + (tile.y % 4);
  214. //else - visible area, no renderable border
  215. assert(0);
  216. return 0;
  217. }
  218. void MapRendererBorder::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  219. {
  220. const auto & image = animation->getImage(getIndexForTile(context, coordinates));
  221. target.draw(image, Point(0, 0));
  222. }
  223. MapRendererFow::MapRendererFow()
  224. {
  225. fogOfWarFullHide = std::make_unique<CAnimation>("TSHRC");
  226. fogOfWarFullHide->preload();
  227. fogOfWarPartialHide = std::make_unique<CAnimation>("TSHRE");
  228. fogOfWarPartialHide->preload();
  229. static const std::vector<int> rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27};
  230. size_t size = fogOfWarPartialHide->size(0); //group size after next rotation
  231. for(const int rotation : rotations)
  232. {
  233. fogOfWarPartialHide->duplicateImage(0, rotation, 0);
  234. auto image = fogOfWarPartialHide->getImage(size, 0);
  235. image->verticalFlip();
  236. size++;
  237. }
  238. }
  239. void MapRendererFow::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  240. {
  241. assert(!context.isVisible(coordinates));
  242. const NeighborTilesInfo neighborInfo(context, coordinates);
  243. int retBitmapID = neighborInfo.getBitmapID(); // >=0 -> partial hide, <0 - full hide
  244. if(retBitmapID < 0)
  245. {
  246. // generate a number that is predefined for each tile,
  247. // but appears random to break visible pattern in large areas of fow
  248. // current approach (use primes as magic numbers for formula) looks to be suitable
  249. size_t pseudorandomNumber = ((coordinates.x * 997) ^ (coordinates.y * 1009)) / 101;
  250. size_t imageIndex = pseudorandomNumber % fogOfWarFullHide->size();
  251. target.draw(fogOfWarFullHide->getImage(imageIndex), Point(0, 0));
  252. }
  253. else
  254. {
  255. target.draw(fogOfWarPartialHide->getImage(retBitmapID), Point(0, 0));
  256. }
  257. }
  258. std::shared_ptr<CAnimation> MapRendererObjects::getBaseAnimation(const CGObjectInstance* obj)
  259. {
  260. const auto & info = obj->appearance;
  261. //the only(?) invisible object
  262. if(info->id == Obj::EVENT)
  263. return std::shared_ptr<CAnimation>();
  264. if(info->animationFile.empty())
  265. {
  266. logGlobal->warn("Def name for obj (%d,%d) is empty!", info->id, info->subid);
  267. return std::shared_ptr<CAnimation>();
  268. }
  269. bool generateMovementGroups = (info->id == Obj::BOAT) || (info->id == Obj::HERO);
  270. //TODO: relocate to config file?
  271. // Boat appearance files only contain single, unanimated image
  272. // proper boat animations are actually in different file
  273. static const std::vector<std::string> boatAnimations = {
  274. "AB01_.def", "AB02_.def", "AB03_.def"
  275. };
  276. if (info->id == Obj::BOAT)
  277. return getAnimation(boatAnimations[info->subid], generateMovementGroups);
  278. return getAnimation(info->animationFile, generateMovementGroups);
  279. }
  280. std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const std::string & filename, bool generateMovementGroups)
  281. {
  282. if(animations.count(filename))
  283. return animations[filename];
  284. auto ret = std::make_shared<CAnimation>(filename);
  285. animations[filename] = ret;
  286. ret->preload();
  287. if(generateMovementGroups)
  288. {
  289. ret->createFlippedGroup(1, 13);
  290. ret->createFlippedGroup(2, 14);
  291. ret->createFlippedGroup(3, 15);
  292. ret->createFlippedGroup(6, 10);
  293. ret->createFlippedGroup(7, 11);
  294. ret->createFlippedGroup(8, 12);
  295. }
  296. return ret;
  297. }
  298. std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectInstance* obj)
  299. {
  300. //TODO: relocate to config file?
  301. static const std::vector<std::string> heroFlags = {
  302. "AF00", "AF01", "AF02", "AF03", "AF04", "AF05", "AF06", "AF07"
  303. };
  304. //TODO: relocate to config file?
  305. static const std::vector<std::vector<std::string>> boatFlags = {
  306. {"ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"},
  307. {"ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"},
  308. {"ABF03L", "ABF03G", "ABF03R", "ABF03D", "ABF03B", "ABF03P", "ABF03W", "ABF03K"}
  309. };
  310. if(obj->ID == Obj::HERO)
  311. {
  312. assert(dynamic_cast<const CGHeroInstance *>(obj) != nullptr);
  313. assert(obj->tempOwner.isValidPlayer());
  314. return getAnimation(heroFlags[obj->tempOwner.getNum()], true);
  315. }
  316. if(obj->ID == Obj::BOAT)
  317. {
  318. const auto * boat = dynamic_cast<const CGBoat *>(obj);
  319. assert(boat->subID < boatFlags.size());
  320. if (boat->hero)
  321. {
  322. assert(boat->hero->tempOwner.isValidPlayer());
  323. return getAnimation(boatFlags[boat->subID][boat->hero->tempOwner.getNum()], true);
  324. }
  325. }
  326. return nullptr;
  327. }
  328. std::shared_ptr<CAnimation> MapRendererObjects::getOverlayAnimation(const CGObjectInstance * obj)
  329. {
  330. if(obj->ID == Obj::BOAT)
  331. {
  332. //TODO: relocate to config file?
  333. // Boats have additional animation with waves around boat
  334. static const std::vector<std::string> boatAnimations = {
  335. "ABM01_.def", "ABM02_.def", "ABM03_.def"
  336. };
  337. const auto * boat = dynamic_cast<const CGBoat *>(obj);
  338. if (boat->hero)
  339. return getAnimation(boatAnimations[obj->subID], true);
  340. }
  341. return nullptr;
  342. }
  343. std::shared_ptr<IImage> MapRendererObjects::getImage(const IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation>& animation) const
  344. {
  345. if(!animation)
  346. return nullptr;
  347. size_t groupIndex = context.objectGroupIndex(obj->id);
  348. if(animation->size(groupIndex) == 0)
  349. return nullptr;
  350. size_t frameIndex = context.objectImageIndex(obj->id, animation->size(groupIndex));
  351. return animation->getImage(frameIndex, groupIndex);
  352. }
  353. void MapRendererObjects::renderImage(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage>& image)
  354. {
  355. if(!image)
  356. return;
  357. auto transparency = static_cast<uint8_t>(std::round(255 * context.objectTransparency(object->id)));
  358. if (transparency == 0)
  359. return;
  360. image->setAlpha(transparency);
  361. image->setFlagColor(object->tempOwner);
  362. Point offsetPixels = context.objectImageOffset(object->id, coordinates);
  363. if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y)
  364. {
  365. Point imagePos = image->dimensions() - offsetPixels - Point(32, 32);
  366. target.draw(image, Point(0, 0), Rect(imagePos, Point(32,32)));
  367. }
  368. }
  369. void MapRendererObjects::renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance* instance)
  370. {
  371. renderImage(context, target, coordinates, instance, getImage(context, instance, getBaseAnimation(instance)));
  372. renderImage(context, target, coordinates, instance, getImage(context, instance, getFlagAnimation(instance)));
  373. renderImage(context, target, coordinates, instance, getImage(context, instance, getOverlayAnimation(instance)));
  374. }
  375. void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  376. {
  377. for(const auto & objectID : context.getObjects(coordinates))
  378. {
  379. const auto * objectInstance = context.getObject(objectID);
  380. assert(objectInstance);
  381. if(!objectInstance)
  382. {
  383. logGlobal->error("Stray map object that isn't fading");
  384. continue;
  385. }
  386. renderObject(context, target, coordinates, objectInstance);
  387. }
  388. }
  389. void MapRendererDebugGrid::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  390. {
  391. if(context.showGrid())
  392. {
  393. target.drawLine(Point(0, 0), Point(0, 31), {128, 128, 128, 128}, {128, 128, 128, 128});
  394. target.drawLine(Point(0, 0), Point(31, 0), {128, 128, 128, 128}, {128, 128, 128, 128});
  395. }
  396. }
  397. MapRendererPath::MapRendererPath()
  398. : pathNodes(new CAnimation("ADAG"))
  399. {
  400. pathNodes->preload();
  401. }
  402. void MapRendererPath::renderImage(Canvas & target, bool reachableToday, size_t imageIndex)
  403. {
  404. const static size_t unreachableTodayOffset = 25;
  405. if(reachableToday)
  406. target.draw(pathNodes->getImage(imageIndex), Point(0, 0));
  407. else
  408. target.draw(pathNodes->getImage(imageIndex + unreachableTodayOffset), Point(0, 0));
  409. }
  410. void MapRendererPath::renderImageCross(Canvas & target, bool reachableToday, const int3 & curr)
  411. {
  412. renderImage(target, reachableToday, 0);
  413. }
  414. void MapRendererPath::renderImageArrow(Canvas & target, bool reachableToday, const int3 & curr, const int3 & prev, const int3 & next)
  415. {
  416. // Vector directions
  417. // 0 1 2
  418. // |
  419. // 3 - 4 - 5
  420. // |
  421. // 6 7 8
  422. //For example:
  423. // |
  424. // +->
  425. // is (directionToArrowIndex[7][5])
  426. //
  427. const static size_t directionToArrowIndex[9][9] = {
  428. {16, 17, 18, 7, 0, 19, 6, 5, 0 },
  429. {8, 9, 18, 7, 0, 19, 6, 0, 20},
  430. {8, 1, 10, 7, 0, 19, 0, 21, 20},
  431. {24, 17, 18, 15, 0, 0, 6, 5, 4 },
  432. {0, 0, 0, 0, 0, 0, 0, 0, 0 },
  433. {8, 1, 2, 0, 0, 11, 22, 21, 20},
  434. {24, 17, 0, 23, 0, 3, 14, 5, 4 },
  435. {24, 0, 2, 23, 0, 3, 22, 13, 4 },
  436. {0, 1, 2, 23, 0, 3, 22, 21, 12}
  437. };
  438. size_t enterDirection = (curr.x - next.x + 1) + 3 * (curr.y - next.y + 1);
  439. size_t leaveDirection = (prev.x - curr.x + 1) + 3 * (prev.y - curr.y + 1);
  440. size_t imageIndex = directionToArrowIndex[enterDirection][leaveDirection];
  441. renderImage(target, reachableToday, imageIndex);
  442. }
  443. void MapRendererPath::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  444. {
  445. const auto & functor = [&](const CGPathNode & node)
  446. {
  447. return node.coord == coordinates;
  448. };
  449. const auto * path = context.currentPath();
  450. if(!path)
  451. return;
  452. const auto & iter = boost::range::find_if(path->nodes, functor);
  453. if(iter == path->nodes.end())
  454. return;
  455. bool reachableToday = iter->turns == 0;
  456. if(iter == path->nodes.begin())
  457. renderImageCross(target, reachableToday, iter->coord);
  458. auto next = iter + 1;
  459. auto prev = iter - 1;
  460. // start of path - current hero location
  461. if(next == path->nodes.end())
  462. return;
  463. bool pathContinuous = iter->coord.areNeighbours(next->coord) && iter->coord.areNeighbours(prev->coord);
  464. bool embarking = iter->action == CGPathNode::EMBARK || iter->action == CGPathNode::DISEMBARK;
  465. if(pathContinuous && !embarking)
  466. renderImageArrow(target, reachableToday, iter->coord, prev->coord, next->coord);
  467. else
  468. renderImageCross(target, reachableToday, iter->coord);
  469. }
  470. void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  471. {
  472. if(!context.isInMap(coordinates))
  473. {
  474. rendererBorder.renderTile(context, target, coordinates);
  475. return;
  476. }
  477. const NeighborTilesInfo neighborInfo(context, coordinates);
  478. if(neighborInfo.areAllHidden())
  479. {
  480. rendererFow.renderTile(context, target, coordinates);
  481. }
  482. else
  483. {
  484. rendererTerrain.renderTile(context, target, coordinates);
  485. rendererRiver.renderTile(context, target, coordinates);
  486. rendererRoad.renderTile(context, target, coordinates);
  487. rendererObjects.renderTile(context, target, coordinates);
  488. rendererPath.renderTile(context, target, coordinates);
  489. if(!context.isVisible(coordinates))
  490. rendererFow.renderTile(context, target, coordinates);
  491. }
  492. rendererDebugGrid.renderTile(context, target, coordinates);
  493. }