Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ////////////////////////////////////////////////////////////////////////
- // OpenTibia - an opensource roleplaying game
- ////////////////////////////////////////////////////////////////////////
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- ////////////////////////////////////////////////////////////////////////
- #include "otpch.h"
- #include "iomap.h"
- #include "map.h"
- #include "town.h"
- #include "tile.h"
- #include "item.h"
- #include "container.h"
- #include "depot.h"
- #include "teleport.h"
- #include "beds.h"
- #include "fileloader.h"
- #include "configmanager.h"
- #include "game.h"
- extern ConfigManager g_config;
- extern Game g_game;
- typedef uint8_t attribute_t;
- typedef uint32_t flags_t;
- /*
- OTBM_ROOTV2
- |
- |--- OTBM_MAP_DATA
- | |
- | |--- OTBM_TILE_AREA
- | | |--- OTBM_TILE
- | | |--- OTBM_TILE_SQUARE (not implemented)
- | | |--- OTBM_TILE_REF (not implemented)
- | | |--- OTBM_HOUSETILE
- | |
- | |--- OTBM_SPAWNS (not implemented)
- | | |--- OTBM_SPAWN_AREA (not implemented)
- | | |--- OTBM_MONSTER (not implemented)
- | |
- | |--- OTBM_TOWNS
- | | |--- OTBM_TOWN
- | |
- | |--- OTBM_WAYPOINTS
- | |--- OTBM_WAYPOINT
- |
- |--- OTBM_ITEM_DEF (not implemented)
- */
- Tile* IOMap::createTile(Item*& ground, Item* item, uint16_t px, uint16_t py, uint16_t pz)
- {
- Tile* tile = NULL;
- if(ground)
- {
- if((item && item->isBlocking(NULL)) || ground->isBlocking(NULL)) //tile is blocking with possibly some decoration, should be static
- tile = new StaticTile(px, py, pz);
- else //tile is not blocking with possibly multiple items, use dynamic
- tile = new DynamicTile(px, py, pz);
- tile->__internalAddThing(ground);
- if(ground->getDecaying() != DECAYING_TRUE)
- {
- ground->__startDecaying();
- ground->setLoadedFromMap(true);
- }
- ground = NULL;
- }
- else //no ground on this tile, so it will always block
- tile = new StaticTile(px, py, pz);
- return tile;
- }
- bool IOMap::loadMap(Map* map, const std::string& identifier)
- {
- FileLoader f;
- if(!f.openFile(identifier.c_str(), "OTBM", false, true))
- {
- std::stringstream ss;
- ss << "Could not open the file " << identifier << ".";
- setLastErrorString(ss.str());
- return false;
- }
- uint32_t type = 0;
- NODE root = f.getChildNode((NODE)NULL, type);
- PropStream propStream;
- if(!f.getProps(root, propStream))
- {
- setLastErrorString("Could not read root property.");
- return false;
- }
- OTBM_root_header* rootHeader;
- if(!propStream.getStruct(rootHeader))
- {
- setLastErrorString("Could not read header.");
- return false;
- }
- uint32_t headerVersion = rootHeader->version;
- if(headerVersion <= 0)
- {
- //In otbm version 1 the count variable after splashes/fluidcontainers and stackables
- //are saved as attributes instead, this solves alot of problems with items
- //that is changed (stackable/charges/fluidcontainer/splash) during an update.
- setLastErrorString("This map needs to be upgraded by using the latest map editor version to be able to load correctly.");
- return false;
- }
- if(headerVersion > 3)
- {
- setLastErrorString("Unknown OTBM version detected.");
- return false;
- }
- uint32_t headerMajorItems = rootHeader->majorVersionItems;
- if(headerMajorItems < 3)
- {
- setLastErrorString("This map needs to be upgraded by using the latest map editor version to be able to load correctly.");
- return false;
- }
- if(headerMajorItems > (uint32_t)Items::dwMajorVersion)
- {
- setLastErrorString("The map was saved with a different items.otb version, an upgraded items.otb is required.");
- return false;
- }
- uint32_t headerMinorItems = rootHeader->minorVersionItems;
- if(headerMinorItems < CLIENT_VERSION_810)
- {
- setLastErrorString("This map needs an updated items.otb.");
- return false;
- }
- if(headerMinorItems > (uint32_t)Items::dwMinorVersion)
- setLastErrorString("This map needs an updated items.otb.");
- std::clog << ">>> Map size: " << rootHeader->width << "x" << rootHeader->height << "." << std::endl;
- map->mapWidth = rootHeader->width;
- map->mapHeight = rootHeader->height;
- NODE nodeMap = f.getChildNode(root, type);
- if(type != OTBM_MAP_DATA)
- {
- setLastErrorString("Could not read data node.");
- return false;
- }
- if(!f.getProps(nodeMap, propStream))
- {
- setLastErrorString("Could not read map data attributes.");
- return false;
- }
- std::string tmp;
- uint8_t attribute;
- while(propStream.getByte(attribute))
- {
- switch(attribute)
- {
- case OTBM_ATTR_DESCRIPTION:
- {
- if(!propStream.getString(tmp))
- {
- setLastErrorString("Invalid description tag.");
- return false;
- }
- map->descriptions.push_back(tmp);
- break;
- }
- case OTBM_ATTR_EXT_SPAWN_FILE:
- {
- if(!propStream.getString(tmp))
- {
- setLastErrorString("Invalid spawnfile tag.");
- return false;
- }
- map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1);
- map->spawnfile += tmp;
- break;
- }
- case OTBM_ATTR_EXT_HOUSE_FILE:
- {
- if(!propStream.getString(tmp))
- {
- setLastErrorString("Invalid housefile tag.");
- return false;
- }
- map->housefile = identifier.substr(0, identifier.rfind('/') + 1);
- map->housefile += tmp;
- break;
- }
- default:
- {
- setLastErrorString("Unknown header node.");
- return false;
- }
- }
- }
- std::clog << ">>> Map descriptions:";
- for(StringVec::iterator it = map->descriptions.begin(); it != map->descriptions.end(); ++it)
- std::clog << " - \"" << (*it) << "\"";
- #ifdef __GROUND_CACHE__
- typedef std::map<uint16_t, std::pair<Item*, int32_t> > CacheMap;
- CacheMap groundCache;
- #endif
- NODE nodeMapData = f.getChildNode(nodeMap, type);
- while(nodeMapData != NO_NODE)
- {
- if(f.getError() != ERROR_NONE)
- {
- setLastErrorString("Invalid map node.");
- return false;
- }
- if(type == OTBM_TILE_AREA)
- {
- if(!f.getProps(nodeMapData, propStream))
- {
- setLastErrorString("Invalid map node.");
- return false;
- }
- OTBM_Destination_coords* areaCoord;
- if(!propStream.getStruct(areaCoord))
- {
- setLastErrorString("Invalid map node.");
- return false;
- }
- int32_t baseX = areaCoord->_x, baseY = areaCoord->_y, baseZ = areaCoord->_z;
- NODE nodeTile = f.getChildNode(nodeMapData, type);
- while(nodeTile != NO_NODE)
- {
- if(f.getError() != ERROR_NONE)
- {
- setLastErrorString("Could not read node data.");
- return false;
- }
- if(type == OTBM_TILE || type == OTBM_HOUSETILE)
- {
- if(!f.getProps(nodeTile, propStream))
- {
- setLastErrorString("Could not read node data.");
- return false;
- }
- OTBM_Tile_coords* tileCoord;
- if(!propStream.getStruct(tileCoord))
- {
- setLastErrorString("Could not read tile position.");
- return false;
- }
- Tile* tile = NULL;
- Item* ground = NULL;
- uint32_t flags = 0;
- uint16_t px = baseX + tileCoord->_x, py = baseY + tileCoord->_y, pz = baseZ;
- House* house = NULL;
- if(type == OTBM_HOUSETILE)
- {
- uint32_t houseId;
- if(!propStream.getLong(houseId))
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not read house id.";
- setLastErrorString(ss.str());
- return false;
- }
- house = Houses::getInstance()->getHouse(houseId, true);
- if(!house)
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not create house id: " << houseId;
- setLastErrorString(ss.str());
- return false;
- }
- tile = new HouseTile(px, py, pz, house);
- house->addTile(static_cast<HouseTile*>(tile));
- }
- //read tile attributes
- uint8_t attribute = 0;
- while(propStream.getByte(attribute))
- {
- switch(attribute)
- {
- case OTBM_ATTR_TILE_FLAGS:
- {
- uint32_t _flags;
- if(!propStream.getLong(_flags))
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to read tile flags.";
- setLastErrorString(ss.str());
- return false;
- }
- if((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE)
- flags |= TILESTATE_PROTECTIONZONE;
- else if((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE)
- flags |= TILESTATE_OPTIONALZONE;
- else if((_flags & TILESTATE_HARDCOREZONE) == TILESTATE_HARDCOREZONE)
- flags |= TILESTATE_HARDCOREZONE;
- if((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT)
- flags |= TILESTATE_NOLOGOUT;
- if((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
- {
- if(house)
- std::clog << "[x:" << px << ", y:" << py << ", z:" << pz << "] House tile flagged as refreshing!";
- flags |= TILESTATE_REFRESH;
- }
- break;
- }
- case OTBM_ATTR_ITEM:
- {
- Item* item = Item::CreateItem(propStream);
- if(!item)
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to create item.";
- setLastErrorString(ss.str());
- return false;
- }
- if(item->getItemCount() <= 0)
- item->setItemCount(1);
- if(house && item->isMovable())
- {
- std::clog << "[Warning - IOMap::loadMap] Movable item in house: " << house->getId()
- << ", item type: " << item->getID() << ", at position " << px << "/" << py << "/"
- << pz << std::endl;
- delete item;
- item = NULL;
- }
- else if(tile)
- {
- tile->__internalAddThing(item);
- if(item->getDecaying() != DECAYING_TRUE)
- {
- item->__startDecaying();
- item->setLoadedFromMap(true);
- }
- }
- else if(item->isGroundTile())
- {
- if(ground)
- {
- #ifdef __GROUND_CACHE__
- CacheMap::iterator it = groundCache.find(ground->getID());
- bool erase = it == groundCache.end();
- if(!erase)
- {
- it->second.second--;
- erase = it->second.second < 1;
- if(erase)
- groundCache.erase(it);
- }
- if(erase)
- #endif
- delete ground;
- }
- #ifdef __GROUND_CACHE__
- const ItemType& tit = Item::items[item->getID()];
- if(!(tit.magicEffect != MAGIC_EFFECT_NONE || !tit.walkStack || tit.transformUseTo != 0 || tit.cache ||
- item->floorChange() || item->canDecay() || item->getActionId() > 0 || item->getUniqueId() > 0))
- {
- CacheMap::iterator it = groundCache.find(item->getID());
- if(it != groundCache.end())
- {
- delete item;
- item = it->second.first;
- it->second.second++;
- }
- else
- groundCache[item->getID()] = std::make_pair(item, 1);
- }
- #endif
- ground = item;
- }
- else
- {
- tile = createTile(ground, item, px, py, pz);
- tile->__internalAddThing(item);
- if(item->getDecaying() != DECAYING_TRUE)
- {
- item->__startDecaying();
- item->setLoadedFromMap(true);
- }
- }
- break;
- }
- default:
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Unknown tile attribute.";
- setLastErrorString(ss.str());
- return false;
- }
- }
- }
- NODE nodeItem = f.getChildNode(nodeTile, type);
- while(nodeItem)
- {
- if(type == OTBM_ITEM)
- {
- PropStream propStream;
- f.getProps(nodeItem, propStream);
- Item* item = Item::CreateItem(propStream);
- if(!item)
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to create item.";
- setLastErrorString(ss.str());
- return false;
- }
- if(item->unserializeItemNode(f, nodeItem, propStream))
- {
- if(item->getItemCount() <= 0)
- item->setItemCount(1);
- if(house && item->isMovable())
- {
- std::clog << "[Warning - IOMap::loadMap] Movable item in house: "
- << house->getId() << ", item type: " << item->getID()
- << ", pos " << px << "/" << py << "/" << pz << std::endl;
- delete item;
- item = NULL;
- }
- else if(tile)
- {
- tile->__internalAddThing(item);
- if(item->getDecaying() != DECAYING_TRUE)
- {
- item->__startDecaying();
- item->setLoadedFromMap(true);
- }
- }
- else if(item->isGroundTile())
- {
- if(ground)
- {
- #ifdef __GROUND_CACHE__
- CacheMap::iterator it = groundCache.find(ground->getID());
- bool erase = it == groundCache.end();
- if(!erase)
- {
- it->second.second--;
- erase = it->second.second < 1;
- if(erase)
- groundCache.erase(it);
- }
- if(erase)
- #endif
- delete ground;
- }
- #ifdef __GROUND_CACHE__
- const ItemType& tit = Item::items[item->getID()];
- if(!(tit.magicEffect != MAGIC_EFFECT_NONE || !tit.walkStack || tit.transformUseTo != 0 || tit.cache ||
- item->floorChange() || item->canDecay() || item->getActionId() > 0 || item->getUniqueId() > 0))
- {
- CacheMap::iterator it = groundCache.find(item->getID());
- if(it != groundCache.end())
- {
- delete item;
- item = it->second.first;
- it->second.second++;
- }
- else
- groundCache[item->getID()] = std::make_pair(item, 1);
- }
- #endif
- ground = item;
- }
- else
- {
- tile = createTile(ground, item, px, py, pz);
- tile->__internalAddThing(item);
- if(item->getDecaying() != DECAYING_TRUE)
- {
- item->__startDecaying();
- item->setLoadedFromMap(true);
- }
- }
- }
- else
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to load item " << item->getID() << ".";
- setLastErrorString(ss.str());
- delete item;
- item = NULL;
- return false;
- }
- }
- else
- {
- std::stringstream ss;
- ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Unknown node type.";
- setLastErrorString(ss.str());
- }
- nodeItem = f.getNextNode(nodeItem, type);
- }
- if(!tile)
- tile = createTile(ground, NULL, px, py, pz);
- tile->setFlag((tileflags_t)flags);
- map->setTile(px, py, pz, tile);
- }
- else
- {
- setLastErrorString("Unknown tile node.");
- return false;
- }
- nodeTile = f.getNextNode(nodeTile, type);
- }
- }
- else if(type == OTBM_TOWNS)
- {
- NODE nodeTown = f.getChildNode(nodeMapData, type);
- while(nodeTown != NO_NODE)
- {
- if(type == OTBM_TOWN)
- {
- if(!f.getProps(nodeTown, propStream))
- {
- setLastErrorString("Could not read town data.");
- return false;
- }
- uint32_t townId = 0;
- if(!propStream.getLong(townId))
- {
- setLastErrorString("Could not read town id.");
- return false;
- }
- Town* town = Towns::getInstance()->getTown(townId);
- if(!town)
- {
- town = new Town(townId);
- Towns::getInstance()->addTown(townId, town);
- }
- std::string townName;
- if(!propStream.getString(townName))
- {
- setLastErrorString("Could not read town name.");
- return false;
- }
- town->setName(townName);
- OTBM_Destination_coords *townCoords;
- if(!propStream.getStruct(townCoords))
- {
- setLastErrorString("Could not read town coordinates.");
- return false;
- }
- town->setPosition(Position(townCoords->_x, townCoords->_y, townCoords->_z));
- }
- else
- {
- setLastErrorString("Unknown town node.");
- return false;
- }
- nodeTown = f.getNextNode(nodeTown, type);
- }
- }
- else if(type == OTBM_WAYPOINTS && headerVersion > 1)
- {
- NODE nodeWaypoint = f.getChildNode(nodeMapData, type);
- while(nodeWaypoint != NO_NODE)
- {
- if(type == OTBM_WAYPOINT)
- {
- if(!f.getProps(nodeWaypoint, propStream))
- {
- setLastErrorString("Could not read waypoint data.");
- return false;
- }
- std::string name;
- if(!propStream.getString(name))
- {
- setLastErrorString("Could not read waypoint name.");
- return false;
- }
- OTBM_Destination_coords* waypoint_coords;
- if(!propStream.getStruct(waypoint_coords))
- {
- setLastErrorString("Could not read waypoint coordinates.");
- return false;
- }
- map->waypoints.addWaypoint(WaypointPtr(new Waypoint(name,
- Position(waypoint_coords->_x, waypoint_coords->_y, waypoint_coords->_z))));
- }
- else
- {
- setLastErrorString("Unknown waypoint node.");
- return false;
- }
- nodeWaypoint = f.getNextNode(nodeWaypoint, type);
- }
- }
- else
- {
- setLastErrorString("Unknown map node.");
- return false;
- }
- nodeMapData = f.getNextNode(nodeMapData, type);
- }
- #ifdef __GROUND_CACHE__
- for(CacheMap::iterator it = groundCache.begin(); it != groundCache.end(); ++it)
- {
- //it->second.first->setParent(NULL);
- g_game.grounds[it->second.first] = it->second.second;
- }
- groundCache.clear();
- #endif
- return true;
- }
- bool IOMap::loadSpawns(Map* map)
- {
- if(map->spawnfile.empty())
- map->spawnfile = g_config.getString(ConfigManager::MAP_NAME) + "-spawn.xml";
- return Spawns::getInstance()->loadFromXml(map->spawnfile);
- }
- bool IOMap::loadHouses(Map* map)
- {
- if(map->housefile.empty())
- map->housefile = g_config.getString(ConfigManager::MAP_NAME) + "-house.xml";
- return Houses::getInstance()->loadFromXml(map->housefile);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement