No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

map.cpp 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. /*
  2. * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. * THE SOFTWARE.
  21. */
  22. #include "map.h"
  23. #include "game.h"
  24. #include "localplayer.h"
  25. #include "tile.h"
  26. #include "item.h"
  27. #include "missile.h"
  28. #include "statictext.h"
  29. #include "mapview.h"
  30. #include "minimap.h"
  31. #include <framework/core/eventdispatcher.h>
  32. #include <framework/core/application.h>
  33. Map g_map;
  34. TilePtr Map::m_nulltile;
  35. void Map::init()
  36. {
  37. resetAwareRange();
  38. m_animationFlags |= Animation_Show;
  39. }
  40. void Map::terminate()
  41. {
  42. clean();
  43. }
  44. void Map::addMapView(const MapViewPtr& mapView)
  45. {
  46. m_mapViews.push_back(mapView);
  47. }
  48. void Map::removeMapView(const MapViewPtr& mapView)
  49. {
  50. auto it = std::find(m_mapViews.begin(), m_mapViews.end(), mapView);
  51. if(it != m_mapViews.end())
  52. m_mapViews.erase(it);
  53. }
  54. void Map::notificateTileUpdate(const Position& pos)
  55. {
  56. if(!pos.isMapPosition())
  57. return;
  58. for(const MapViewPtr& mapView : m_mapViews)
  59. mapView->onTileUpdate(pos);
  60. g_minimap.updateTile(pos, getTile(pos));
  61. }
  62. void Map::clean()
  63. {
  64. cleanDynamicThings();
  65. for(int i=0;i<=Otc::MAX_Z;++i)
  66. m_tileBlocks[i].clear();
  67. m_waypoints.clear();
  68. g_towns.clear();
  69. g_houses.clear();
  70. g_creatures.clearSpawns();
  71. m_tilesRect = Rect(65534, 65534, 0, 0);
  72. }
  73. void Map::cleanDynamicThings()
  74. {
  75. for(const auto& pair : m_knownCreatures) {
  76. const CreaturePtr& creature = pair.second;
  77. removeThing(creature);
  78. }
  79. m_knownCreatures.clear();
  80. for(int i=0;i<=Otc::MAX_Z;++i)
  81. m_floorMissiles[i].clear();
  82. cleanTexts();
  83. }
  84. void Map::cleanTexts()
  85. {
  86. m_animatedTexts.clear();
  87. m_staticTexts.clear();
  88. }
  89. void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
  90. {
  91. if(!thing)
  92. return;
  93. if(thing->isItem() || thing->isCreature() || thing->isEffect()) {
  94. const TilePtr& tile = getOrCreateTile(pos);
  95. if(tile)
  96. tile->addThing(thing, stackPos);
  97. } else {
  98. if(thing->isMissile()) {
  99. m_floorMissiles[pos.z].push_back(thing->static_self_cast<Missile>());
  100. } else if(thing->isAnimatedText()) {
  101. // this code will stack animated texts of the same color
  102. AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>();
  103. AnimatedTextPtr prevAnimatedText;
  104. bool merged = false;
  105. for(auto other : m_animatedTexts) {
  106. if(other->getPosition() == pos) {
  107. prevAnimatedText = other;
  108. if(other->merge(animatedText)) {
  109. merged = true;
  110. break;
  111. }
  112. }
  113. }
  114. if(!merged) {
  115. if(prevAnimatedText) {
  116. Point offset = prevAnimatedText->getOffset();
  117. float t = prevAnimatedText->getTimer().ticksElapsed();
  118. if(t < Otc::ANIMATED_TEXT_DURATION / 4.0) { // didnt move 12 pixels
  119. int y = 12 - 48 * t / (float)Otc::ANIMATED_TEXT_DURATION;
  120. offset += Point(0, y);
  121. }
  122. offset.y = std::min<int>(offset.y, 12);
  123. animatedText->setOffset(offset);
  124. }
  125. m_animatedTexts.push_back(animatedText);
  126. }
  127. } else if(thing->isStaticText()) {
  128. StaticTextPtr staticText = thing->static_self_cast<StaticText>();
  129. bool mustAdd = true;
  130. for(auto other : m_staticTexts) {
  131. // try to combine messages
  132. if(other->getPosition() == pos && other->addMessage(staticText->getName(), staticText->getMessageMode(), staticText->getFirstMessage())) {
  133. mustAdd = false;
  134. break;
  135. }
  136. }
  137. if(mustAdd)
  138. m_staticTexts.push_back(staticText);
  139. else
  140. return;
  141. }
  142. thing->setPosition(pos);
  143. thing->onAppear();
  144. }
  145. notificateTileUpdate(pos);
  146. }
  147. ThingPtr Map::getThing(const Position& pos, int stackPos)
  148. {
  149. if(TilePtr tile = getTile(pos))
  150. return tile->getThing(stackPos);
  151. return nullptr;
  152. }
  153. bool Map::removeThing(const ThingPtr& thing)
  154. {
  155. if(!thing)
  156. return false;
  157. bool ret = false;
  158. if(thing->isMissile()) {
  159. MissilePtr missile = thing->static_self_cast<Missile>();
  160. int z = missile->getPosition().z;
  161. auto it = std::find(m_floorMissiles[z].begin(), m_floorMissiles[z].end(), missile);
  162. if(it != m_floorMissiles[z].end()) {
  163. m_floorMissiles[z].erase(it);
  164. ret = true;
  165. }
  166. } else if(thing->isAnimatedText()) {
  167. AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>();
  168. auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText);
  169. if(it != m_animatedTexts.end()) {
  170. m_animatedTexts.erase(it);
  171. ret = true;
  172. }
  173. } else if(thing->isStaticText()) {
  174. StaticTextPtr staticText = thing->static_self_cast<StaticText>();
  175. auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText);
  176. if(it != m_staticTexts.end()) {
  177. m_staticTexts.erase(it);
  178. ret = true;
  179. }
  180. } else if(const TilePtr& tile = thing->getTile())
  181. ret = tile->removeThing(thing);
  182. notificateTileUpdate(thing->getPosition());
  183. return ret;
  184. }
  185. bool Map::removeThingByPos(const Position& pos, int stackPos)
  186. {
  187. if(TilePtr tile = getTile(pos))
  188. return removeThing(tile->getThing(stackPos));
  189. return false;
  190. }
  191. void Map::colorizeThing(const ThingPtr& thing, const Color& color)
  192. {
  193. if(!thing)
  194. return;
  195. if(thing->isItem())
  196. thing->static_self_cast<Item>()->setColor(color);
  197. else if(thing->isCreature()) {
  198. const TilePtr& tile = thing->getTile();
  199. assert(tile);
  200. const ThingPtr& topThing = tile->getTopThing();
  201. assert(topThing);
  202. topThing->static_self_cast<Item>()->setColor(color);
  203. }
  204. }
  205. void Map::removeThingColor(const ThingPtr& thing)
  206. {
  207. if(!thing)
  208. return;
  209. if(thing->isItem())
  210. thing->static_self_cast<Item>()->setColor(Color::alpha);
  211. else if(thing->isCreature()) {
  212. const TilePtr& tile = thing->getTile();
  213. assert(tile);
  214. const ThingPtr& topThing = tile->getTopThing();
  215. assert(topThing);
  216. topThing->static_self_cast<Item>()->setColor(Color::alpha);
  217. }
  218. }
  219. StaticTextPtr Map::getStaticText(const Position& pos)
  220. {
  221. for(auto staticText : m_staticTexts) {
  222. // try to combine messages
  223. if(staticText->getPosition() == pos)
  224. return staticText;
  225. }
  226. return nullptr;
  227. }
  228. const TilePtr& Map::createTile(const Position& pos)
  229. {
  230. if(!pos.isMapPosition())
  231. return m_nulltile;
  232. if(pos.x < m_tilesRect.left())
  233. m_tilesRect.setLeft(pos.x);
  234. if(pos.y < m_tilesRect.top())
  235. m_tilesRect.setTop(pos.y);
  236. if(pos.x > m_tilesRect.right())
  237. m_tilesRect.setRight(pos.x);
  238. if(pos.y > m_tilesRect.bottom())
  239. m_tilesRect.setBottom(pos.y);
  240. TileBlock& block = m_tileBlocks[pos.z][getBlockIndex(pos)];
  241. return block.create(pos);
  242. }
  243. template <typename... Items>
  244. const TilePtr& Map::createTileEx(const Position& pos, const Items&... items)
  245. {
  246. if(!pos.isValid())
  247. return m_nulltile;
  248. const TilePtr& tile = getOrCreateTile(pos);
  249. auto vec = {items...};
  250. for(auto it : vec)
  251. addThing(it, pos);
  252. return tile;
  253. }
  254. const TilePtr& Map::getOrCreateTile(const Position& pos)
  255. {
  256. if(!pos.isMapPosition())
  257. return m_nulltile;
  258. if(pos.x < m_tilesRect.left())
  259. m_tilesRect.setLeft(pos.x);
  260. if(pos.y < m_tilesRect.top())
  261. m_tilesRect.setTop(pos.y);
  262. if(pos.x > m_tilesRect.right())
  263. m_tilesRect.setRight(pos.x);
  264. if(pos.y > m_tilesRect.bottom())
  265. m_tilesRect.setBottom(pos.y);
  266. TileBlock& block = m_tileBlocks[pos.z][getBlockIndex(pos)];
  267. return block.getOrCreate(pos);
  268. }
  269. const TilePtr& Map::getTile(const Position& pos)
  270. {
  271. if(!pos.isMapPosition())
  272. return m_nulltile;
  273. auto it = m_tileBlocks[pos.z].find(getBlockIndex(pos));
  274. if(it != m_tileBlocks[pos.z].end())
  275. return it->second.get(pos);
  276. return m_nulltile;
  277. }
  278. const TileList Map::getTiles(int floor/* = -1*/)
  279. {
  280. TileList tiles;
  281. if(floor > Otc::MAX_Z) {
  282. return tiles;
  283. }
  284. else if(floor < 0) {
  285. // Search all floors
  286. for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
  287. for(const auto& pair : m_tileBlocks[z]) {
  288. const TileBlock& block = pair.second;
  289. for(const TilePtr& tile : block.getTiles()) {
  290. if(tile != nullptr)
  291. tiles.push_back(tile);
  292. }
  293. }
  294. }
  295. }
  296. else {
  297. for(const auto& pair : m_tileBlocks[floor]) {
  298. const TileBlock& block = pair.second;
  299. for(const TilePtr& tile : block.getTiles()) {
  300. if(tile != nullptr)
  301. tiles.push_back(tile);
  302. }
  303. }
  304. }
  305. return tiles;
  306. }
  307. void Map::cleanTile(const Position& pos)
  308. {
  309. if(!pos.isMapPosition())
  310. return;
  311. auto it = m_tileBlocks[pos.z].find(getBlockIndex(pos));
  312. if(it != m_tileBlocks[pos.z].end()) {
  313. TileBlock& block = it->second;
  314. if(const TilePtr& tile = block.get(pos)) {
  315. tile->clean();
  316. if(tile->canErase())
  317. block.remove(pos);
  318. notificateTileUpdate(pos);
  319. }
  320. }
  321. for(auto it = m_staticTexts.begin();it != m_staticTexts.end();) {
  322. const StaticTextPtr& staticText = *it;
  323. if(staticText->getPosition() == pos && staticText->getMessageMode() == Otc::MessageNone)
  324. it = m_staticTexts.erase(it);
  325. else
  326. ++it;
  327. }
  328. }
  329. void Map::setShowZone(tileflags_t zone, bool show)
  330. {
  331. if(show)
  332. m_zoneFlags |= (uint32)zone;
  333. else
  334. m_zoneFlags &= ~(uint32)zone;
  335. }
  336. void Map::setShowZones(bool show)
  337. {
  338. if(!show)
  339. m_zoneFlags = 0;
  340. else if(m_zoneFlags == 0)
  341. m_zoneFlags = TILESTATE_HOUSE | TILESTATE_PROTECTIONZONE;
  342. }
  343. void Map::setZoneColor(tileflags_t zone, const Color& color)
  344. {
  345. if((m_zoneFlags & zone) == zone)
  346. m_zoneColors[zone] = color;
  347. }
  348. Color Map::getZoneColor(tileflags_t flag)
  349. {
  350. auto it = m_zoneColors.find(flag);
  351. if(it == m_zoneColors.end())
  352. return Color::alpha;
  353. return it->second;
  354. }
  355. void Map::setForceShowAnimations(bool force)
  356. {
  357. if(force) {
  358. if(!(m_animationFlags & Animation_Force))
  359. m_animationFlags |= Animation_Force;
  360. } else
  361. m_animationFlags &= ~Animation_Force;
  362. }
  363. bool Map::isForcingAnimations()
  364. {
  365. return (m_animationFlags & Animation_Force) == Animation_Force;
  366. }
  367. bool Map::isShowingAnimations()
  368. {
  369. return (m_animationFlags & Animation_Show) == Animation_Show;
  370. }
  371. void Map::setShowAnimations(bool show)
  372. {
  373. if(show) {
  374. if(!(m_animationFlags & Animation_Show))
  375. m_animationFlags |= Animation_Show;
  376. } else
  377. m_animationFlags &= ~Animation_Show;
  378. }
  379. void Map::beginGhostMode(float opacity)
  380. {
  381. g_painter->setOpacity(opacity);
  382. }
  383. void Map::endGhostMode()
  384. {
  385. g_painter->resetOpacity();
  386. }
  387. std::map<Position, ItemPtr> Map::findItemsById(uint16 clientId, uint32 max)
  388. {
  389. std::map<Position, ItemPtr> ret;
  390. uint32 count = 0;
  391. for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
  392. for(const auto& pair : m_tileBlocks[z]) {
  393. const TileBlock& block = pair.second;
  394. for(const TilePtr& tile : block.getTiles()) {
  395. if(unlikely(!tile || tile->isEmpty()))
  396. continue;
  397. for(const ItemPtr& item : tile->getItems()) {
  398. if(item->getId() == clientId) {
  399. ret.insert(std::make_pair(tile->getPosition(), item));
  400. if(++count >= max)
  401. break;
  402. }
  403. }
  404. }
  405. }
  406. }
  407. return ret;
  408. }
  409. void Map::addCreature(const CreaturePtr& creature)
  410. {
  411. m_knownCreatures[creature->getId()] = creature;
  412. }
  413. CreaturePtr Map::getCreatureById(uint32 id)
  414. {
  415. auto it = m_knownCreatures.find(id);
  416. if(it == m_knownCreatures.end())
  417. return nullptr;
  418. return it->second;
  419. }
  420. void Map::removeCreatureById(uint32 id)
  421. {
  422. if(id == 0)
  423. return;
  424. auto it = m_knownCreatures.find(id);
  425. if(it != m_knownCreatures.end())
  426. m_knownCreatures.erase(it);
  427. }
  428. void Map::removeUnawareThings()
  429. {
  430. // remove creatures from tiles that we are not aware of anymore
  431. for(const auto& pair : m_knownCreatures) {
  432. const CreaturePtr& creature = pair.second;
  433. if(!isAwareOfPosition(creature->getPosition()))
  434. removeThing(creature);
  435. }
  436. // remove static texts from tiles that we are not aware anymore
  437. for(auto it = m_staticTexts.begin(); it != m_staticTexts.end();) {
  438. const StaticTextPtr& staticText = *it;
  439. if(staticText->getMessageMode() == Otc::MessageNone && !isAwareOfPosition(staticText->getPosition()))
  440. it = m_staticTexts.erase(it);
  441. else
  442. ++it;
  443. }
  444. if(!g_game.getFeature(Otc::GameKeepUnawareTiles)) {
  445. // remove tiles that we are not aware anymore
  446. for(int z = 0; z <= Otc::MAX_Z; ++z) {
  447. std::unordered_map<uint, TileBlock>& tileBlocks = m_tileBlocks[z];
  448. for(auto it = tileBlocks.begin(); it != tileBlocks.end();) {
  449. TileBlock& block = (*it).second;
  450. bool blockEmpty = true;
  451. for(const TilePtr& tile : block.getTiles()) {
  452. if(!tile)
  453. continue;
  454. const Position& pos = tile->getPosition();
  455. if(!isAwareOfPosition(pos))
  456. block.remove(pos);
  457. else
  458. blockEmpty = false;
  459. }
  460. if(blockEmpty)
  461. it = tileBlocks.erase(it);
  462. else
  463. ++it;
  464. }
  465. }
  466. }
  467. }
  468. void Map::setCentralPosition(const Position& centralPosition)
  469. {
  470. if(m_centralPosition == centralPosition)
  471. return;
  472. m_centralPosition = centralPosition;
  473. removeUnawareThings();
  474. // this fixes local player position when the local player is removed from the map,
  475. // the local player is removed from the map when there are too many creatures on his tile,
  476. // so there is no enough stackpos to the server send him
  477. g_dispatcher.addEvent([this] {
  478. LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
  479. if(!localPlayer || localPlayer->getPosition() == m_centralPosition)
  480. return;
  481. TilePtr tile = localPlayer->getTile();
  482. if(tile && tile->hasThing(localPlayer))
  483. return;
  484. Position oldPos = localPlayer->getPosition();
  485. Position pos = m_centralPosition;
  486. if(oldPos != pos) {
  487. if(!localPlayer->isRemoved())
  488. localPlayer->onDisappear();
  489. localPlayer->setPosition(pos);
  490. localPlayer->onAppear();
  491. g_logger.debug("forced player position update");
  492. }
  493. });
  494. for(const MapViewPtr& mapView : m_mapViews)
  495. mapView->onMapCenterChange(centralPosition);
  496. }
  497. std::vector<CreaturePtr> Map::getSightSpectators(const Position& centerPos, bool multiFloor)
  498. {
  499. return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left - 1, m_awareRange.right - 2, m_awareRange.top - 1, m_awareRange.bottom - 2);
  500. }
  501. std::vector<CreaturePtr> Map::getSpectators(const Position& centerPos, bool multiFloor)
  502. {
  503. return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left, m_awareRange.right, m_awareRange.top, m_awareRange.bottom);
  504. }
  505. std::vector<CreaturePtr> Map::getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange)
  506. {
  507. return getSpectatorsInRangeEx(centerPos, multiFloor, xRange, xRange, yRange, yRange);
  508. }
  509. std::vector<CreaturePtr> Map::getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange)
  510. {
  511. int minZRange = 0;
  512. int maxZRange = 0;
  513. std::vector<CreaturePtr> creatures;
  514. if(multiFloor) {
  515. minZRange = 0;
  516. maxZRange = Otc::MAX_Z;
  517. }
  518. //TODO: optimize
  519. //TODO: get creatures from other floors corretly
  520. //TODO: delivery creatures in distance order
  521. for(int iz=-minZRange; iz<=maxZRange; ++iz) {
  522. for(int iy=-minYRange; iy<=maxYRange; ++iy) {
  523. for(int ix=-minXRange; ix<=maxXRange; ++ix) {
  524. TilePtr tile = getTile(centerPos.translated(ix,iy,iz));
  525. if(!tile)
  526. continue;
  527. auto tileCreatures = tile->getCreatures();
  528. creatures.insert(creatures.end(), tileCreatures.rbegin(), tileCreatures.rend());
  529. }
  530. }
  531. }
  532. return creatures;
  533. }
  534. bool Map::isLookPossible(const Position& pos)
  535. {
  536. TilePtr tile = getTile(pos);
  537. return tile && tile->isLookPossible();
  538. }
  539. bool Map::isCovered(const Position& pos, int firstFloor)
  540. {
  541. // check for tiles on top of the postion
  542. Position tilePos = pos;
  543. while(tilePos.coveredUp() && tilePos.z >= firstFloor) {
  544. TilePtr tile = getTile(tilePos);
  545. // the below tile is covered when the above tile has a full ground
  546. if(tile && tile->isFullGround())
  547. return true;
  548. }
  549. return false;
  550. }
  551. bool Map::isCompletelyCovered(const Position& pos, int firstFloor)
  552. {
  553. const TilePtr& checkTile = getTile(pos);
  554. Position tilePos = pos;
  555. while(tilePos.coveredUp() && tilePos.z >= firstFloor) {
  556. bool covered = true;
  557. bool done = false;
  558. // check in 2x2 range tiles that has no transparent pixels
  559. for(int x=0;x<2 && !done;++x) {
  560. for(int y=0;y<2 && !done;++y) {
  561. const TilePtr& tile = getTile(tilePos.translated(-x, -y));
  562. if(!tile || !tile->isFullyOpaque()) {
  563. covered = false;
  564. done = true;
  565. } else if(x==0 && y==0 && (!checkTile || checkTile->isSingleDimension())) {
  566. done = true;
  567. }
  568. }
  569. }
  570. if(covered)
  571. return true;
  572. }
  573. return false;
  574. }
  575. bool Map::isAwareOfPosition(const Position& pos)
  576. {
  577. if(pos.z < getFirstAwareFloor() || pos.z > getLastAwareFloor())
  578. return false;
  579. Position groundedPos = pos;
  580. while(groundedPos.z != m_centralPosition.z) {
  581. if(groundedPos.z > m_centralPosition.z) {
  582. if(groundedPos.x == 65535 || groundedPos.y == 65535) // When pos == 65535,65535,15 we cant go up to 65536,65536,14
  583. break;
  584. groundedPos.coveredUp();
  585. }
  586. else {
  587. if(groundedPos.x == 0 || groundedPos.y == 0) // When pos == 0,0,0 we cant go down to -1,-1,1
  588. break;
  589. groundedPos.coveredDown();
  590. }
  591. }
  592. return m_centralPosition.isInRange(groundedPos, m_awareRange.left,
  593. m_awareRange.right,
  594. m_awareRange.top,
  595. m_awareRange.bottom);
  596. }
  597. void Map::setAwareRange(const AwareRange& range)
  598. {
  599. m_awareRange = range;
  600. removeUnawareThings();
  601. }
  602. void Map::resetAwareRange()
  603. {
  604. AwareRange range;
  605. range.left = 8;
  606. range.top = 6;
  607. range.bottom = 7;
  608. range.right = 9;
  609. setAwareRange(range);
  610. }
  611. int Map::getFirstAwareFloor()
  612. {
  613. if(m_centralPosition.z > Otc::SEA_FLOOR)
  614. return m_centralPosition.z-Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
  615. else
  616. return 0;
  617. }
  618. int Map::getLastAwareFloor()
  619. {
  620. if(m_centralPosition.z > Otc::SEA_FLOOR)
  621. return std::min<int>(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
  622. else
  623. return Otc::SEA_FLOOR;
  624. }
  625. std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const Position& startPos, const Position& goalPos, int maxComplexity, int flags)
  626. {
  627. // pathfinding using A* search algorithm
  628. // as described in http://en.wikipedia.org/wiki/A*_search_algorithm
  629. struct Node {
  630. Node(const Position& pos) : cost(0), totalCost(0), pos(pos), prev(nullptr), dir(Otc::InvalidDirection) { }
  631. float cost;
  632. float totalCost;
  633. Position pos;
  634. Node *prev;
  635. Otc::Direction dir;
  636. };
  637. struct LessNode : std::binary_function<std::pair<Node*, float>, std::pair<Node*, float>, bool> {
  638. bool operator()(std::pair<Node*, float> a, std::pair<Node*, float> b) const {
  639. return b.second < a.second;
  640. }
  641. };
  642. std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> ret;
  643. std::vector<Otc::Direction>& dirs = std::get<0>(ret);
  644. Otc::PathFindResult& result = std::get<1>(ret);
  645. result = Otc::PathFindResultNoWay;
  646. if(startPos == goalPos) {
  647. result = Otc::PathFindResultSamePosition;
  648. return ret;
  649. }
  650. if(startPos.z != goalPos.z) {
  651. result = Otc::PathFindResultImpossible;
  652. return ret;
  653. }
  654. // check the goal pos is walkable
  655. if(g_map.isAwareOfPosition(goalPos)) {
  656. const TilePtr goalTile = getTile(goalPos);
  657. if(!goalTile || !goalTile->isWalkable()) {
  658. return ret;
  659. }
  660. }
  661. else {
  662. const MinimapTile& goalTile = g_minimap.getTile(goalPos);
  663. if(goalTile.hasFlag(MinimapTileNotWalkable)) {
  664. return ret;
  665. }
  666. }
  667. std::unordered_map<Position, Node*, PositionHasher> nodes;
  668. std::priority_queue<std::pair<Node*, float>, std::vector<std::pair<Node*, float>>, LessNode> searchList;
  669. Node *currentNode = new Node(startPos);
  670. currentNode->pos = startPos;
  671. nodes[startPos] = currentNode;
  672. Node *foundNode = nullptr;
  673. while(currentNode) {
  674. if((int)nodes.size() > maxComplexity) {
  675. result = Otc::PathFindResultTooFar;
  676. break;
  677. }
  678. // path found
  679. if(currentNode->pos == goalPos && (!foundNode || currentNode->cost < foundNode->cost))
  680. foundNode = currentNode;
  681. // cost too high
  682. if(foundNode && currentNode->totalCost >= foundNode->cost)
  683. break;
  684. for(int i=-1;i<=1;++i) {
  685. for(int j=-1;j<=1;++j) {
  686. if(i == 0 && j == 0)
  687. continue;
  688. bool wasSeen = false;
  689. bool hasCreature = false;
  690. bool isNotWalkable = true;
  691. bool isNotPathable = true;
  692. int speed = 100;
  693. Position neighborPos = currentNode->pos.translated(i, j);
  694. if(g_map.isAwareOfPosition(neighborPos)) {
  695. wasSeen = true;
  696. if(const TilePtr& tile = getTile(neighborPos)) {
  697. hasCreature = tile->hasCreature();
  698. isNotWalkable = !tile->isWalkable();
  699. isNotPathable = !tile->isPathable();
  700. speed = tile->getGroundSpeed();
  701. }
  702. } else {
  703. const MinimapTile& mtile = g_minimap.getTile(neighborPos);
  704. wasSeen = mtile.hasFlag(MinimapTileWasSeen);
  705. isNotWalkable = mtile.hasFlag(MinimapTileNotWalkable);
  706. isNotPathable = mtile.hasFlag(MinimapTileNotPathable);
  707. if(isNotWalkable || isNotPathable)
  708. wasSeen = true;
  709. speed = mtile.getSpeed();
  710. }
  711. float walkFactor = 0;
  712. if(neighborPos != goalPos) {
  713. if(!(flags & Otc::PathFindAllowNotSeenTiles) && !wasSeen)
  714. continue;
  715. if(wasSeen) {
  716. if(!(flags & Otc::PathFindAllowCreatures) && hasCreature)
  717. continue;
  718. if(!(flags & Otc::PathFindAllowNonPathable) && isNotPathable)
  719. continue;
  720. if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable)
  721. continue;
  722. }
  723. } else {
  724. if(!(flags & Otc::PathFindAllowNotSeenTiles) && !wasSeen)
  725. continue;
  726. if(wasSeen) {
  727. if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable)
  728. continue;
  729. }
  730. }
  731. Otc::Direction walkDir = currentNode->pos.getDirectionFromPosition(neighborPos);
  732. if(walkDir >= Otc::NorthEast)
  733. walkFactor += 3.0f;
  734. else
  735. walkFactor += 1.0f;
  736. float cost = currentNode->cost + (speed * walkFactor) / 100.0f;
  737. Node *neighborNode;
  738. if(nodes.find(neighborPos) == nodes.end()) {
  739. neighborNode = new Node(neighborPos);
  740. nodes[neighborPos] = neighborNode;
  741. } else {
  742. neighborNode = nodes[neighborPos];
  743. if(neighborNode->cost <= cost)
  744. continue;
  745. }
  746. neighborNode->prev = currentNode;
  747. neighborNode->cost = cost;
  748. neighborNode->totalCost = neighborNode->cost + neighborPos.distance(goalPos);
  749. neighborNode->dir = walkDir;
  750. searchList.push(std::make_pair(neighborNode, neighborNode->totalCost));
  751. }
  752. }
  753. if(!searchList.empty()) {
  754. currentNode = searchList.top().first;
  755. searchList.pop();
  756. } else
  757. currentNode = nullptr;
  758. }
  759. if(foundNode) {
  760. currentNode = foundNode;
  761. while(currentNode) {
  762. dirs.push_back(currentNode->dir);
  763. currentNode = currentNode->prev;
  764. }
  765. dirs.pop_back();
  766. std::reverse(dirs.begin(), dirs.end());
  767. result = Otc::PathFindResultOk;
  768. }
  769. for(auto it : nodes)
  770. delete it.second;
  771. return ret;
  772. }