Advertisement
Guest User

Untitled

a guest
Aug 17th, 2019
183
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.46 KB | None | 0 0
  1. ////////////////////////////////////////////////////////////////////////
  2. // OpenTibia - an opensource roleplaying game
  3. ////////////////////////////////////////////////////////////////////////
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ////////////////////////////////////////////////////////////////////////
  17. #include "otpch.h"
  18. #include "iomap.h"
  19.  
  20. #include "map.h"
  21. #include "town.h"
  22. #include "tile.h"
  23. #include "item.h"
  24. #include "container.h"
  25. #include "depot.h"
  26.  
  27. #include "teleport.h"
  28. #include "beds.h"
  29.  
  30. #include "fileloader.h"
  31. #include "configmanager.h"
  32. #include "game.h"
  33.  
  34. extern ConfigManager g_config;
  35. extern Game g_game;
  36.  
  37. typedef uint8_t attribute_t;
  38. typedef uint32_t flags_t;
  39.  
  40. /*
  41. OTBM_ROOTV2
  42. |
  43. |--- OTBM_MAP_DATA
  44. | |
  45. | |--- OTBM_TILE_AREA
  46. | | |--- OTBM_TILE
  47. | | |--- OTBM_TILE_SQUARE (not implemented)
  48. | | |--- OTBM_TILE_REF (not implemented)
  49. | | |--- OTBM_HOUSETILE
  50. | |
  51. | |--- OTBM_SPAWNS (not implemented)
  52. | | |--- OTBM_SPAWN_AREA (not implemented)
  53. | | |--- OTBM_MONSTER (not implemented)
  54. | |
  55. | |--- OTBM_TOWNS
  56. | | |--- OTBM_TOWN
  57. | |
  58. | |--- OTBM_WAYPOINTS
  59. | |--- OTBM_WAYPOINT
  60. |
  61. |--- OTBM_ITEM_DEF (not implemented)
  62. */
  63.  
  64. Tile* IOMap::createTile(Item*& ground, Item* item, uint16_t px, uint16_t py, uint16_t pz)
  65. {
  66. Tile* tile = NULL;
  67. if(ground)
  68. {
  69. if((item && item->isBlocking(NULL)) || ground->isBlocking(NULL)) //tile is blocking with possibly some decoration, should be static
  70. tile = new StaticTile(px, py, pz);
  71. else //tile is not blocking with possibly multiple items, use dynamic
  72. tile = new DynamicTile(px, py, pz);
  73.  
  74. tile->__internalAddThing(ground);
  75. if(ground->getDecaying() != DECAYING_TRUE)
  76. {
  77. ground->__startDecaying();
  78. ground->setLoadedFromMap(true);
  79. }
  80.  
  81. ground = NULL;
  82. }
  83. else //no ground on this tile, so it will always block
  84. tile = new StaticTile(px, py, pz);
  85.  
  86. return tile;
  87. }
  88.  
  89. bool IOMap::loadMap(Map* map, const std::string& identifier)
  90. {
  91. FileLoader f;
  92. if(!f.openFile(identifier.c_str(), "OTBM", false, true))
  93. {
  94. std::stringstream ss;
  95. ss << "Could not open the file " << identifier << ".";
  96. setLastErrorString(ss.str());
  97. return false;
  98. }
  99.  
  100. uint32_t type = 0;
  101. NODE root = f.getChildNode((NODE)NULL, type);
  102.  
  103. PropStream propStream;
  104. if(!f.getProps(root, propStream))
  105. {
  106. setLastErrorString("Could not read root property.");
  107. return false;
  108. }
  109.  
  110. OTBM_root_header* rootHeader;
  111. if(!propStream.getStruct(rootHeader))
  112. {
  113. setLastErrorString("Could not read header.");
  114. return false;
  115. }
  116.  
  117. uint32_t headerVersion = rootHeader->version;
  118. if(headerVersion <= 0)
  119. {
  120. //In otbm version 1 the count variable after splashes/fluidcontainers and stackables
  121. //are saved as attributes instead, this solves alot of problems with items
  122. //that is changed (stackable/charges/fluidcontainer/splash) during an update.
  123. setLastErrorString("This map needs to be upgraded by using the latest map editor version to be able to load correctly.");
  124. return false;
  125. }
  126.  
  127. if(headerVersion > 3)
  128. {
  129. setLastErrorString("Unknown OTBM version detected.");
  130. return false;
  131. }
  132.  
  133. uint32_t headerMajorItems = rootHeader->majorVersionItems;
  134. if(headerMajorItems < 3)
  135. {
  136. setLastErrorString("This map needs to be upgraded by using the latest map editor version to be able to load correctly.");
  137. return false;
  138. }
  139.  
  140. if(headerMajorItems > (uint32_t)Items::dwMajorVersion)
  141. {
  142. setLastErrorString("The map was saved with a different items.otb version, an upgraded items.otb is required.");
  143. return false;
  144. }
  145.  
  146. uint32_t headerMinorItems = rootHeader->minorVersionItems;
  147. if(headerMinorItems < CLIENT_VERSION_810)
  148. {
  149. setLastErrorString("This map needs an updated items.otb.");
  150. return false;
  151. }
  152.  
  153. if(headerMinorItems > (uint32_t)Items::dwMinorVersion)
  154. setLastErrorString("This map needs an updated items.otb.");
  155.  
  156. std::clog << ">>> Map size: " << rootHeader->width << "x" << rootHeader->height << "." << std::endl;
  157. map->mapWidth = rootHeader->width;
  158. map->mapHeight = rootHeader->height;
  159.  
  160. NODE nodeMap = f.getChildNode(root, type);
  161. if(type != OTBM_MAP_DATA)
  162. {
  163. setLastErrorString("Could not read data node.");
  164. return false;
  165. }
  166.  
  167. if(!f.getProps(nodeMap, propStream))
  168. {
  169. setLastErrorString("Could not read map data attributes.");
  170. return false;
  171. }
  172.  
  173. std::string tmp;
  174. uint8_t attribute;
  175. while(propStream.getByte(attribute))
  176. {
  177. switch(attribute)
  178. {
  179. case OTBM_ATTR_DESCRIPTION:
  180. {
  181. if(!propStream.getString(tmp))
  182. {
  183. setLastErrorString("Invalid description tag.");
  184. return false;
  185. }
  186.  
  187. map->descriptions.push_back(tmp);
  188. break;
  189. }
  190. case OTBM_ATTR_EXT_SPAWN_FILE:
  191. {
  192. if(!propStream.getString(tmp))
  193. {
  194. setLastErrorString("Invalid spawnfile tag.");
  195. return false;
  196. }
  197.  
  198. map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1);
  199. map->spawnfile += tmp;
  200. break;
  201. }
  202. case OTBM_ATTR_EXT_HOUSE_FILE:
  203. {
  204. if(!propStream.getString(tmp))
  205. {
  206. setLastErrorString("Invalid housefile tag.");
  207. return false;
  208. }
  209.  
  210. map->housefile = identifier.substr(0, identifier.rfind('/') + 1);
  211. map->housefile += tmp;
  212. break;
  213. }
  214. default:
  215. {
  216. setLastErrorString("Unknown header node.");
  217. return false;
  218. }
  219. }
  220. }
  221.  
  222. std::clog << ">>> Map descriptions:";
  223. for(StringVec::iterator it = map->descriptions.begin(); it != map->descriptions.end(); ++it)
  224. std::clog << " - \"" << (*it) << "\"";
  225.  
  226. #ifdef __GROUND_CACHE__
  227. typedef std::map<uint16_t, std::pair<Item*, int32_t> > CacheMap;
  228. CacheMap groundCache;
  229.  
  230. #endif
  231. NODE nodeMapData = f.getChildNode(nodeMap, type);
  232. while(nodeMapData != NO_NODE)
  233. {
  234. if(f.getError() != ERROR_NONE)
  235. {
  236. setLastErrorString("Invalid map node.");
  237. return false;
  238. }
  239.  
  240. if(type == OTBM_TILE_AREA)
  241. {
  242. if(!f.getProps(nodeMapData, propStream))
  243. {
  244. setLastErrorString("Invalid map node.");
  245. return false;
  246. }
  247.  
  248. OTBM_Destination_coords* areaCoord;
  249. if(!propStream.getStruct(areaCoord))
  250. {
  251. setLastErrorString("Invalid map node.");
  252. return false;
  253. }
  254.  
  255. int32_t baseX = areaCoord->_x, baseY = areaCoord->_y, baseZ = areaCoord->_z;
  256. NODE nodeTile = f.getChildNode(nodeMapData, type);
  257. while(nodeTile != NO_NODE)
  258. {
  259. if(f.getError() != ERROR_NONE)
  260. {
  261. setLastErrorString("Could not read node data.");
  262. return false;
  263. }
  264.  
  265. if(type == OTBM_TILE || type == OTBM_HOUSETILE)
  266. {
  267. if(!f.getProps(nodeTile, propStream))
  268. {
  269. setLastErrorString("Could not read node data.");
  270. return false;
  271. }
  272.  
  273. OTBM_Tile_coords* tileCoord;
  274. if(!propStream.getStruct(tileCoord))
  275. {
  276. setLastErrorString("Could not read tile position.");
  277. return false;
  278. }
  279.  
  280. Tile* tile = NULL;
  281. Item* ground = NULL;
  282. uint32_t flags = 0;
  283.  
  284. uint16_t px = baseX + tileCoord->_x, py = baseY + tileCoord->_y, pz = baseZ;
  285. House* house = NULL;
  286. if(type == OTBM_HOUSETILE)
  287. {
  288. uint32_t houseId;
  289. if(!propStream.getLong(houseId))
  290. {
  291. std::stringstream ss;
  292. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not read house id.";
  293.  
  294. setLastErrorString(ss.str());
  295. return false;
  296. }
  297.  
  298. house = Houses::getInstance()->getHouse(houseId, true);
  299. if(!house)
  300. {
  301. std::stringstream ss;
  302. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not create house id: " << houseId;
  303.  
  304. setLastErrorString(ss.str());
  305. return false;
  306. }
  307.  
  308. tile = new HouseTile(px, py, pz, house);
  309. house->addTile(static_cast<HouseTile*>(tile));
  310. }
  311.  
  312. //read tile attributes
  313. uint8_t attribute = 0;
  314. while(propStream.getByte(attribute))
  315. {
  316. switch(attribute)
  317. {
  318. case OTBM_ATTR_TILE_FLAGS:
  319. {
  320. uint32_t _flags;
  321. if(!propStream.getLong(_flags))
  322. {
  323. std::stringstream ss;
  324. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to read tile flags.";
  325.  
  326. setLastErrorString(ss.str());
  327. return false;
  328. }
  329.  
  330. if((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE)
  331. flags |= TILESTATE_PROTECTIONZONE;
  332. else if((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE)
  333. flags |= TILESTATE_OPTIONALZONE;
  334. else if((_flags & TILESTATE_HARDCOREZONE) == TILESTATE_HARDCOREZONE)
  335. flags |= TILESTATE_HARDCOREZONE;
  336.  
  337. if((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT)
  338. flags |= TILESTATE_NOLOGOUT;
  339.  
  340. if((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
  341. {
  342. if(house)
  343. std::clog << "[x:" << px << ", y:" << py << ", z:" << pz << "] House tile flagged as refreshing!";
  344.  
  345. flags |= TILESTATE_REFRESH;
  346. }
  347.  
  348. break;
  349. }
  350.  
  351. case OTBM_ATTR_ITEM:
  352. {
  353. Item* item = Item::CreateItem(propStream);
  354. if(!item)
  355. {
  356. std::stringstream ss;
  357. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to create item.";
  358.  
  359. setLastErrorString(ss.str());
  360. return false;
  361. }
  362.  
  363. if(item->getItemCount() <= 0)
  364. item->setItemCount(1);
  365.  
  366. if(house && item->isMovable())
  367. {
  368. std::clog << "[Warning - IOMap::loadMap] Movable item in house: " << house->getId()
  369. << ", item type: " << item->getID() << ", at position " << px << "/" << py << "/"
  370. << pz << std::endl;
  371.  
  372. delete item;
  373. item = NULL;
  374. }
  375. else if(tile)
  376. {
  377. tile->__internalAddThing(item);
  378. if(item->getDecaying() != DECAYING_TRUE)
  379. {
  380. item->__startDecaying();
  381. item->setLoadedFromMap(true);
  382. }
  383. }
  384. else if(item->isGroundTile())
  385. {
  386. if(ground)
  387. {
  388. #ifdef __GROUND_CACHE__
  389. CacheMap::iterator it = groundCache.find(ground->getID());
  390. bool erase = it == groundCache.end();
  391. if(!erase)
  392. {
  393. it->second.second--;
  394. erase = it->second.second < 1;
  395. if(erase)
  396. groundCache.erase(it);
  397. }
  398.  
  399. if(erase)
  400. #endif
  401. delete ground;
  402. }
  403.  
  404. #ifdef __GROUND_CACHE__
  405. const ItemType& tit = Item::items[item->getID()];
  406. if(!(tit.magicEffect != MAGIC_EFFECT_NONE || !tit.walkStack || tit.transformUseTo != 0 || tit.cache ||
  407. item->floorChange() || item->canDecay() || item->getActionId() > 0 || item->getUniqueId() > 0))
  408. {
  409. CacheMap::iterator it = groundCache.find(item->getID());
  410. if(it != groundCache.end())
  411. {
  412. delete item;
  413. item = it->second.first;
  414. it->second.second++;
  415. }
  416. else
  417. groundCache[item->getID()] = std::make_pair(item, 1);
  418. }
  419.  
  420. #endif
  421. ground = item;
  422. }
  423. else
  424. {
  425. tile = createTile(ground, item, px, py, pz);
  426. tile->__internalAddThing(item);
  427. if(item->getDecaying() != DECAYING_TRUE)
  428. {
  429. item->__startDecaying();
  430. item->setLoadedFromMap(true);
  431. }
  432. }
  433.  
  434. break;
  435. }
  436.  
  437. default:
  438. {
  439. std::stringstream ss;
  440. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Unknown tile attribute.";
  441.  
  442. setLastErrorString(ss.str());
  443. return false;
  444. }
  445. }
  446. }
  447.  
  448. NODE nodeItem = f.getChildNode(nodeTile, type);
  449. while(nodeItem)
  450. {
  451. if(type == OTBM_ITEM)
  452. {
  453. PropStream propStream;
  454. f.getProps(nodeItem, propStream);
  455.  
  456. Item* item = Item::CreateItem(propStream);
  457. if(!item)
  458. {
  459. std::stringstream ss;
  460. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to create item.";
  461.  
  462. setLastErrorString(ss.str());
  463. return false;
  464. }
  465.  
  466. if(item->unserializeItemNode(f, nodeItem, propStream))
  467. {
  468. if(item->getItemCount() <= 0)
  469. item->setItemCount(1);
  470.  
  471. if(house && item->isMovable())
  472. {
  473. std::clog << "[Warning - IOMap::loadMap] Movable item in house: "
  474. << house->getId() << ", item type: " << item->getID()
  475. << ", pos " << px << "/" << py << "/" << pz << std::endl;
  476.  
  477. delete item;
  478. item = NULL;
  479. }
  480. else if(tile)
  481. {
  482. tile->__internalAddThing(item);
  483. if(item->getDecaying() != DECAYING_TRUE)
  484. {
  485. item->__startDecaying();
  486. item->setLoadedFromMap(true);
  487. }
  488. }
  489. else if(item->isGroundTile())
  490. {
  491. if(ground)
  492. {
  493. #ifdef __GROUND_CACHE__
  494. CacheMap::iterator it = groundCache.find(ground->getID());
  495. bool erase = it == groundCache.end();
  496. if(!erase)
  497. {
  498. it->second.second--;
  499. erase = it->second.second < 1;
  500. if(erase)
  501. groundCache.erase(it);
  502. }
  503.  
  504. if(erase)
  505. #endif
  506. delete ground;
  507. }
  508.  
  509. #ifdef __GROUND_CACHE__
  510. const ItemType& tit = Item::items[item->getID()];
  511. if(!(tit.magicEffect != MAGIC_EFFECT_NONE || !tit.walkStack || tit.transformUseTo != 0 || tit.cache ||
  512. item->floorChange() || item->canDecay() || item->getActionId() > 0 || item->getUniqueId() > 0))
  513. {
  514. CacheMap::iterator it = groundCache.find(item->getID());
  515. if(it != groundCache.end())
  516. {
  517. delete item;
  518. item = it->second.first;
  519. it->second.second++;
  520. }
  521. else
  522. groundCache[item->getID()] = std::make_pair(item, 1);
  523. }
  524.  
  525. #endif
  526. ground = item;
  527. }
  528. else
  529. {
  530. tile = createTile(ground, item, px, py, pz);
  531. tile->__internalAddThing(item);
  532. if(item->getDecaying() != DECAYING_TRUE)
  533. {
  534. item->__startDecaying();
  535. item->setLoadedFromMap(true);
  536. }
  537. }
  538. }
  539. else
  540. {
  541. std::stringstream ss;
  542. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to load item " << item->getID() << ".";
  543. setLastErrorString(ss.str());
  544.  
  545. delete item;
  546. item = NULL;
  547. return false;
  548. }
  549. }
  550. else
  551. {
  552. std::stringstream ss;
  553. ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Unknown node type.";
  554. setLastErrorString(ss.str());
  555. }
  556.  
  557. nodeItem = f.getNextNode(nodeItem, type);
  558. }
  559.  
  560. if(!tile)
  561. tile = createTile(ground, NULL, px, py, pz);
  562.  
  563. tile->setFlag((tileflags_t)flags);
  564. map->setTile(px, py, pz, tile);
  565. }
  566. else
  567. {
  568. setLastErrorString("Unknown tile node.");
  569. return false;
  570. }
  571.  
  572. nodeTile = f.getNextNode(nodeTile, type);
  573. }
  574. }
  575. else if(type == OTBM_TOWNS)
  576. {
  577. NODE nodeTown = f.getChildNode(nodeMapData, type);
  578. while(nodeTown != NO_NODE)
  579. {
  580. if(type == OTBM_TOWN)
  581. {
  582. if(!f.getProps(nodeTown, propStream))
  583. {
  584. setLastErrorString("Could not read town data.");
  585. return false;
  586. }
  587.  
  588. uint32_t townId = 0;
  589. if(!propStream.getLong(townId))
  590. {
  591. setLastErrorString("Could not read town id.");
  592. return false;
  593. }
  594.  
  595. Town* town = Towns::getInstance()->getTown(townId);
  596. if(!town)
  597. {
  598. town = new Town(townId);
  599. Towns::getInstance()->addTown(townId, town);
  600. }
  601.  
  602. std::string townName;
  603. if(!propStream.getString(townName))
  604. {
  605. setLastErrorString("Could not read town name.");
  606. return false;
  607. }
  608.  
  609. town->setName(townName);
  610. OTBM_Destination_coords *townCoords;
  611. if(!propStream.getStruct(townCoords))
  612. {
  613. setLastErrorString("Could not read town coordinates.");
  614. return false;
  615. }
  616.  
  617. town->setPosition(Position(townCoords->_x, townCoords->_y, townCoords->_z));
  618. }
  619. else
  620. {
  621. setLastErrorString("Unknown town node.");
  622. return false;
  623. }
  624.  
  625. nodeTown = f.getNextNode(nodeTown, type);
  626. }
  627. }
  628. else if(type == OTBM_WAYPOINTS && headerVersion > 1)
  629. {
  630. NODE nodeWaypoint = f.getChildNode(nodeMapData, type);
  631. while(nodeWaypoint != NO_NODE)
  632. {
  633. if(type == OTBM_WAYPOINT)
  634. {
  635. if(!f.getProps(nodeWaypoint, propStream))
  636. {
  637. setLastErrorString("Could not read waypoint data.");
  638. return false;
  639. }
  640.  
  641. std::string name;
  642. if(!propStream.getString(name))
  643. {
  644. setLastErrorString("Could not read waypoint name.");
  645. return false;
  646. }
  647.  
  648. OTBM_Destination_coords* waypoint_coords;
  649. if(!propStream.getStruct(waypoint_coords))
  650. {
  651. setLastErrorString("Could not read waypoint coordinates.");
  652. return false;
  653. }
  654.  
  655. map->waypoints.addWaypoint(WaypointPtr(new Waypoint(name,
  656. Position(waypoint_coords->_x, waypoint_coords->_y, waypoint_coords->_z))));
  657. }
  658. else
  659. {
  660. setLastErrorString("Unknown waypoint node.");
  661. return false;
  662. }
  663.  
  664. nodeWaypoint = f.getNextNode(nodeWaypoint, type);
  665. }
  666. }
  667. else
  668. {
  669. setLastErrorString("Unknown map node.");
  670. return false;
  671. }
  672.  
  673. nodeMapData = f.getNextNode(nodeMapData, type);
  674. }
  675.  
  676. #ifdef __GROUND_CACHE__
  677. for(CacheMap::iterator it = groundCache.begin(); it != groundCache.end(); ++it)
  678. {
  679. //it->second.first->setParent(NULL);
  680. g_game.grounds[it->second.first] = it->second.second;
  681. }
  682.  
  683. groundCache.clear();
  684. #endif
  685. return true;
  686. }
  687.  
  688. bool IOMap::loadSpawns(Map* map)
  689. {
  690. if(map->spawnfile.empty())
  691. map->spawnfile = g_config.getString(ConfigManager::MAP_NAME) + "-spawn.xml";
  692.  
  693. return Spawns::getInstance()->loadFromXml(map->spawnfile);
  694. }
  695.  
  696. bool IOMap::loadHouses(Map* map)
  697. {
  698. if(map->housefile.empty())
  699. map->housefile = g_config.getString(ConfigManager::MAP_NAME) + "-house.xml";
  700.  
  701. return Houses::getInstance()->loadFromXml(map->housefile);
  702. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement