diff --git a/src/client/creatures.cpp b/src/client/creatures.cpp index d9f9b157..c5b44990 100644 --- a/src/client/creatures.cpp +++ b/src/client/creatures.cpp @@ -260,7 +260,7 @@ void CreatureManager::saveSpawns(const std::string& fileName) root->LinkEndChild(elem); } - std::string savePath = g_resources.getRealDir(fileName) + "/" + fileName; + std::string savePath = g_resources.getRealPath(fileName); if(!doc.SaveFile(savePath)) stdext::throw_exception(stdext::format("failed to save spawns XML %s: %s", savePath, doc.ErrorDesc())); } diff --git a/src/client/houses.cpp b/src/client/houses.cpp index f77653d7..61753acb 100644 --- a/src/client/houses.cpp +++ b/src/client/houses.cpp @@ -173,7 +173,7 @@ void HouseManager::save(const std::string& fileName) root->LinkEndChild(elem); } - std::string savePath = g_resources.getRealDir(fileName) + "/" + fileName; + std::string savePath = g_resources.getRealPath(fileName); if(!doc.SaveFile(savePath)) stdext::throw_exception(stdext::format("failed to save houses XML %s: %s", savePath, doc.ErrorDesc())); } diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 0bb16459..b6cd3850 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -123,6 +123,9 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_map", "setSpawnFile", &Map::setSpawnFile, &g_map); g_lua.bindSingletonFunction("g_map", "createTile", &Map::createTile, &g_map); g_lua.bindSingletonFunction("g_map", "getSize", &Map::getSize, &g_map); + g_lua.bindSingletonFunction("g_map", "setDescription", &Map::setDescription, &g_map); + g_lua.bindSingletonFunction("g_map", "getDescriptions", &Map::getDescriptions, &g_map); + g_lua.bindSingletonFunction("g_map", "clearDescriptions", &Map::clearDescriptions, &g_map); g_lua.bindSingletonFunction("g_map", "setShowZone", &Map::setShowZone, &g_map); g_lua.bindSingletonFunction("g_map", "setShowZones", &Map::setShowZones, &g_map); g_lua.bindSingletonFunction("g_map", "setZoneColor", &Map::setZoneColor, &g_map); diff --git a/src/client/map.h b/src/client/map.h index 367008fa..f714cec2 100644 --- a/src/client/map.h +++ b/src/client/map.h @@ -151,6 +151,7 @@ public: void setHouseFile(const std::string& file) { m_attribs.set(OTBM_ATTR_HOUSE_FILE, file); } void setSpawnFile(const std::string& file) { m_attribs.set(OTBM_ATTR_SPAWN_FILE, file); } void setDescription(const std::string& desc) { m_attribs.set(OTBM_ATTR_DESCRIPTION, desc); } + void clearDescriptions() { m_attribs.remove(OTBM_ATTR_DESCRIPTION); } void setWidth(uint16 w) { m_attribs.set(OTBM_ATTR_WIDTH, w); } void setHeight(uint16 h) { m_attribs.set(OTBM_ATTR_HEIGHT, h); } diff --git a/src/client/mapio.cpp b/src/client/mapio.cpp index 7d66459e..1ea7da42 100644 --- a/src/client/mapio.cpp +++ b/src/client/mapio.cpp @@ -34,363 +34,369 @@ void Map::loadOtbm(const std::string& fileName) { - FileStreamPtr fin = g_resources.openFile(fileName); - if(!fin) - stdext::throw_exception(stdext::format("Unable to load map '%s'", fileName)); + try { + FileStreamPtr fin = g_resources.openFile(fileName); + if(!fin) + stdext::throw_exception(stdext::format("Unable to load map '%s'", fileName)); - fin->cache(); - if(!g_things.isOtbLoaded()) - stdext::throw_exception("OTB isn't loaded yet to load a map."); + fin->cache(); + if(!g_things.isOtbLoaded()) + stdext::throw_exception("OTB isn't loaded yet to load a map."); - if(fin->getU32()) - stdext::throw_exception("Unknown file version detected"); + if(fin->getU32()) + stdext::throw_exception("Unknown file version detected"); - BinaryTreePtr root = fin->getBinaryTree(); - if(root->getU8()) - stdext::throw_exception("could not read root property!"); + BinaryTreePtr root = fin->getBinaryTree(); + if(root->getU8()) + stdext::throw_exception("could not read root property!"); - uint32 headerVersion = root->getU32(); - if(headerVersion > 3) - stdext::throw_exception(stdext::format("Unknown OTBM version detected: %u.", headerVersion)); + uint32 headerVersion = root->getU32(); + if(headerVersion > 3) + stdext::throw_exception(stdext::format("Unknown OTBM version detected: %u.", headerVersion)); - setWidth(root->getU16()); - setHeight(root->getU16()); + setWidth(root->getU16()); + setHeight(root->getU16()); - uint32 headerMajorItems = root->getU8(); - if(headerMajorItems > g_things.getOtbMajorVersion()) { - stdext::throw_exception(stdext::format("This map was saved with different OTB version. read %d what it's supposed to be: %d", + uint32 headerMajorItems = root->getU8(); + if(headerMajorItems > g_things.getOtbMajorVersion()) { + stdext::throw_exception(stdext::format("This map was saved with different OTB version. read %d what it's supposed to be: %d", headerMajorItems, g_things.getOtbMajorVersion())); - } + } - root->skip(3); - uint32 headerMinorItems = root->getU32(); - if(headerMinorItems > g_things.getOtbMinorVersion()) { - g_logger.warning(stdext::format("This map needs an updated OTB. read %d what it's supposed to be: %d or less", + root->skip(3); + uint32 headerMinorItems = root->getU32(); + if(headerMinorItems > g_things.getOtbMinorVersion()) { + g_logger.warning(stdext::format("This map needs an updated OTB. read %d what it's supposed to be: %d or less", headerMinorItems, g_things.getOtbMinorVersion())); - } + } - BinaryTreePtr node = root->getChildren()[0]; - if(node->getU8() != OTBM_MAP_DATA) - stdext::throw_exception("Could not read root data node"); - - while (node->canRead()) { - uint8 attribute = node->getU8(); - std::string tmp = node->getString(); - switch (attribute) { - case OTBM_ATTR_DESCRIPTION: - setDescription(tmp); - break; - case OTBM_ATTR_SPAWN_FILE: - setSpawnFile(fileName.substr(0, fileName.rfind('/') + 1) + tmp); - break; - case OTBM_ATTR_HOUSE_FILE: - setHouseFile(fileName.substr(0, fileName.rfind('/') + 1) + tmp); - break; - default: - stdext::throw_exception(stdext::format("Invalid attribute '%d'", (int)attribute)); + BinaryTreePtr node = root->getChildren()[0]; + if(node->getU8() != OTBM_MAP_DATA) + stdext::throw_exception("Could not read root data node"); + + while (node->canRead()) { + uint8 attribute = node->getU8(); + std::string tmp = node->getString(); + switch (attribute) { + case OTBM_ATTR_DESCRIPTION: + setDescription(tmp); + break; + case OTBM_ATTR_SPAWN_FILE: + setSpawnFile(fileName.substr(0, fileName.rfind('/') + 1) + tmp); + break; + case OTBM_ATTR_HOUSE_FILE: + setHouseFile(fileName.substr(0, fileName.rfind('/') + 1) + tmp); + break; + default: + stdext::throw_exception(stdext::format("Invalid attribute '%d'", (int)attribute)); + } } - } - for(const BinaryTreePtr& nodeMapData : node->getChildren()) { - uint8 mapDataType = nodeMapData->getU8(); - if(mapDataType == OTBM_TILE_AREA) { - Position basePos; - basePos.x = nodeMapData->getU16(); - basePos.y = nodeMapData->getU16(); - basePos.z = nodeMapData->getU8(); - - for(const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) { - uint8 type = nodeTile->getU8(); - if(type != OTBM_TILE && type != OTBM_HOUSETILE) - stdext::throw_exception(stdext::format("invalid node tile type %d", (int)type)); - - HousePtr house = nullptr; - uint32 flags = TILESTATE_NONE; - Position pos = basePos + nodeTile->getPoint(); - - if(type == OTBM_HOUSETILE) { - uint32 hId = nodeTile->getU32(); - TilePtr tile = getOrCreateTile(pos); - if(!(house = g_houses.getHouse(hId))) { - house = HousePtr(new House(hId)); - g_houses.addHouse(house); + for(const BinaryTreePtr& nodeMapData : node->getChildren()) { + uint8 mapDataType = nodeMapData->getU8(); + if(mapDataType == OTBM_TILE_AREA) { + Position basePos; + basePos.x = nodeMapData->getU16(); + basePos.y = nodeMapData->getU16(); + basePos.z = nodeMapData->getU8(); + + for(const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) { + uint8 type = nodeTile->getU8(); + if(type != OTBM_TILE && type != OTBM_HOUSETILE) + stdext::throw_exception(stdext::format("invalid node tile type %d", (int)type)); + + HousePtr house = nullptr; + uint32 flags = TILESTATE_NONE; + Position pos = basePos + nodeTile->getPoint(); + + if(type == OTBM_HOUSETILE) { + uint32 hId = nodeTile->getU32(); + TilePtr tile = getOrCreateTile(pos); + if(!(house = g_houses.getHouse(hId))) { + house = HousePtr(new House(hId)); + g_houses.addHouse(house); + } + house->setTile(tile); } - house->setTile(tile); - } - while(nodeTile->canRead()) { - uint8 tileAttr = nodeTile->getU8(); - switch(tileAttr) { - case OTBM_ATTR_TILE_FLAGS: { - uint32 _flags = nodeTile->getU32(); - 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) - flags |= TILESTATE_REFRESH; - - break; - } - case OTBM_ATTR_ITEM: { - addThing(Item::createFromOtb(nodeTile->getU16()), pos); - break; - } - default: { - stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %s", + while(nodeTile->canRead()) { + uint8 tileAttr = nodeTile->getU8(); + switch(tileAttr) { + case OTBM_ATTR_TILE_FLAGS: { + uint32 _flags = nodeTile->getU32(); + 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) + flags |= TILESTATE_REFRESH; + + break; + } + case OTBM_ATTR_ITEM: { + addThing(Item::createFromOtb(nodeTile->getU16()), pos); + break; + } + default: { + stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %s", (int)tileAttr, stdext::to_string(pos))); + } } } - } - for(const BinaryTreePtr& nodeItem : nodeTile->getChildren()) { - if(nodeItem->getU8() != OTBM_ITEM) - stdext::throw_exception("invalid item node"); + for(const BinaryTreePtr& nodeItem : nodeTile->getChildren()) { + if(nodeItem->getU8() != OTBM_ITEM) + stdext::throw_exception("invalid item node"); - ItemPtr item = Item::createFromOtb(nodeItem->getU16()); - item->unserializeItem(nodeItem); + ItemPtr item = Item::createFromOtb(nodeItem->getU16()); + item->unserializeItem(nodeItem); - if(item->isContainer()) { - for(const BinaryTreePtr& containerItem : nodeItem->getChildren()) { - if(containerItem->getU8() != OTBM_ITEM) - stdext::throw_exception("invalid container item node"); + if(item->isContainer()) { + for(const BinaryTreePtr& containerItem : nodeItem->getChildren()) { + if(containerItem->getU8() != OTBM_ITEM) + stdext::throw_exception("invalid container item node"); - ItemPtr cItem = Item::createFromOtb(containerItem->getU16()); - cItem->unserializeItem(containerItem); - item->addContainerItem(cItem); + ItemPtr cItem = Item::createFromOtb(containerItem->getU16()); + cItem->unserializeItem(containerItem); + item->addContainerItem(cItem); + } } - } - if(house && item->isMoveable()) { - g_logger.warning(stdext::format("Moveable item found in house: %d at pos %s - escaping...", item->getId(), stdext::to_string(pos))); - item.reset(); - } + if(house && item->isMoveable()) { + g_logger.warning(stdext::format("Moveable item found in house: %d at pos %s - escaping...", item->getId(), stdext::to_string(pos))); + item.reset(); + } - addThing(item, pos); - } + addThing(item, pos); + } - if(const TilePtr& tile = getTile(pos)) { - if(house) - tile->setHouseId(house->getId()); - tile->setFlags((tileflags_t)flags); - } - } - } else if(mapDataType == OTBM_TOWNS) { - TownPtr town = nullptr; - for(const BinaryTreePtr &nodeTown : nodeMapData->getChildren()) { - if(nodeTown->getU8() != OTBM_TOWN) - stdext::throw_exception("invalid town node."); - - uint32 townId = nodeTown->getU32(); - std::string townName = nodeTown->getString(); - - Position townCoords; - townCoords.x = nodeTown->getU16(); - townCoords.y = nodeTown->getU16(); - townCoords.z = nodeTown->getU8(); - - if(!(town = g_towns.getTown(townId))) { - town = TownPtr(new Town(townId, townName, townCoords)); - g_towns.addTown(town); + if(const TilePtr& tile = getTile(pos)) { + if(house) + tile->setHouseId(house->getId()); + tile->setFlags((tileflags_t)flags); + } } + } else if(mapDataType == OTBM_TOWNS) { + TownPtr town = nullptr; + for(const BinaryTreePtr &nodeTown : nodeMapData->getChildren()) { + if(nodeTown->getU8() != OTBM_TOWN) + stdext::throw_exception("invalid town node."); + + uint32 townId = nodeTown->getU32(); + std::string townName = nodeTown->getString(); + + Position townCoords; + townCoords.x = nodeTown->getU16(); + townCoords.y = nodeTown->getU16(); + townCoords.z = nodeTown->getU8(); + + if(!(town = g_towns.getTown(townId))) + g_towns.addTown(TownPtr(new Town(townId, townName, townCoords))); } - } else if(mapDataType == OTBM_WAYPOINTS && headerVersion > 1) { - for(const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) { - if(nodeWaypoint->getU8() != OTBM_WAYPOINT) - stdext::throw_exception("invalid waypoint node."); + } else if(mapDataType == OTBM_WAYPOINTS && headerVersion > 1) { + for(const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) { + if(nodeWaypoint->getU8() != OTBM_WAYPOINT) + stdext::throw_exception("invalid waypoint node."); - std::string name = nodeWaypoint->getString(); + std::string name = nodeWaypoint->getString(); - Position waypointPos; - waypointPos.x = nodeWaypoint->getU16(); - waypointPos.y = nodeWaypoint->getU16(); - waypointPos.z = nodeWaypoint->getU8(); + Position waypointPos; + waypointPos.x = nodeWaypoint->getU16(); + waypointPos.y = nodeWaypoint->getU16(); + waypointPos.z = nodeWaypoint->getU8(); - if(waypointPos.isValid() && !name.empty() && m_waypoints.find(waypointPos) == m_waypoints.end()) - m_waypoints.insert(std::make_pair(waypointPos, name)); - } - } else - stdext::throw_exception(stdext::format("Unknown map data node %d", (int)mapDataType)); - } + if(waypointPos.isValid() && !name.empty() && m_waypoints.find(waypointPos) == m_waypoints.end()) + m_waypoints.insert(std::make_pair(waypointPos, name)); + } + } else + stdext::throw_exception(stdext::format("Unknown map data node %d", (int)mapDataType)); + } - fin->close(); + fin->close(); + } catch(std::exception& e) { + g_logger.error(stdext::format("Failed to load '%s': %s", fileName, e.what())); + } } void Map::saveOtbm(const std::string& fileName) { - FileStreamPtr fin = g_resources.createFile(fileName); - if(!fin) - stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName)); - - fin->cache(); - std::string dir; - if(fileName.find_last_of('/') == std::string::npos) - dir = g_resources.getWorkDir(); - else - dir = fileName.substr(0, fileName.find_last_of('/')); - - uint32 version = 0; - if(g_things.getOtbMajorVersion() < ClientVersion820) - version = 1; - else - version = 2; - - /// Usually when a map has empty house/spawn file it means the map is new. - /// TODO: Ask the user for a map name instead of those ugly uses of substr - std::string::size_type sep_pos; - std::string houseFile = getHouseFile(); - std::string spawnFile = getSpawnFile(); - std::string cpyf; - - if((sep_pos = fileName.rfind('.')) != std::string::npos && stdext::ends_with(fileName, ".otbm")) - cpyf = fileName.substr(0, sep_pos); - - if(houseFile.empty()) - houseFile = cpyf + "-houses.xml"; - - if(spawnFile.empty()) - spawnFile = cpyf + "-spawns.xml"; - - /// we only need the filename to save to, the directory should be resolved by the OTBM loader not here - if((sep_pos = spawnFile.rfind('/')) != std::string::npos) - spawnFile = spawnFile.substr(sep_pos + 1); - - if((sep_pos = houseFile.rfind('/')) != std::string::npos) - houseFile = houseFile.substr(sep_pos + 1); - - fin->addU32(0); // file version - OutputBinaryTreePtr root(new OutputBinaryTree(fin)); - { - root->addU32(version); - - Size mapSize = getSize(); - root->addU16(mapSize.width()); - root->addU16(mapSize.height()); - - root->addU32(g_things.getOtbMajorVersion()); - root->addU32(g_things.getOtbMinorVersion()); - - root->startNode(OTBM_MAP_DATA); + try { + FileStreamPtr fin = g_resources.createFile(fileName); + if(!fin) + stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName)); + + fin->cache(); + std::string dir; + if(fileName.find_last_of('/') == std::string::npos) + dir = g_resources.getWorkDir(); + else + dir = fileName.substr(0, fileName.find_last_of('/')); + + uint32 version = 0; + if(g_things.getOtbMajorVersion() < ClientVersion820) + version = 1; + else + version = 2; + + /// Usually when a map has empty house/spawn file it means the map is new. + /// TODO: Ask the user for a map name instead of those ugly uses of substr + std::string::size_type sep_pos; + std::string houseFile = getHouseFile(); + std::string spawnFile = getSpawnFile(); + std::string cpyf; + + if((sep_pos = fileName.rfind('.')) != std::string::npos && stdext::ends_with(fileName, ".otbm")) + cpyf = fileName.substr(0, sep_pos); + + if(houseFile.empty()) + houseFile = cpyf + "-houses.xml"; + + if(spawnFile.empty()) + spawnFile = cpyf + "-spawns.xml"; + + /// we only need the filename to save to, the directory should be resolved by the OTBM loader not here + if((sep_pos = spawnFile.rfind('/')) != std::string::npos) + spawnFile = spawnFile.substr(sep_pos + 1); + + if((sep_pos = houseFile.rfind('/')) != std::string::npos) + houseFile = houseFile.substr(sep_pos + 1); + + fin->addU32(0); // file version + OutputBinaryTreePtr root(new OutputBinaryTree(fin)); { - // own description. - for(const auto& desc : getDescriptions()) { - root->addU8(OTBM_ATTR_DESCRIPTION); - root->addString(desc); - } + root->addU32(version); - // special one - root->addU8(OTBM_ATTR_DESCRIPTION); - root->addString(stdext::format("Saved with %s v%s", g_app.getName(), g_app.getVersion())); - - // spawn file. - root->addU8(OTBM_ATTR_SPAWN_FILE); - root->addString(spawnFile); - - // house file. - root->addU8(OTBM_ATTR_HOUSE_FILE); - root->addString(houseFile); - - int px = -1, py = -1, pz =-1; - bool firstNode = true; - - for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) { - for(const auto& it : m_tileBlocks[z]) { - const TileBlock& block = it.second; - for(const TilePtr& tile : block.getTiles()) { - if(!tile || tile->isEmpty()) - continue; - - const Position& pos = tile->getPosition(); - if(!pos.isValid()) - continue; - - if(pos.x < px || pos.x >= px + 256 - || pos.y < py || pos.y >= py + 256 - || pos.z != pz) { - if(!firstNode) - root->endNode(); /// OTBM_TILE_AREA - - firstNode = false; - root->startNode(OTBM_TILE_AREA); - - px = pos.x & 0xFF00; - py = pos.y & 0xFF00; - pz = pos.z; - root->addPos(px, py, pz); - } + Size mapSize = getSize(); + root->addU16(mapSize.width()); + root->addU16(mapSize.height()); - root->startNode(tile->isHouseTile() ? OTBM_HOUSETILE : OTBM_TILE); - root->addPoint(Point(pos.x, pos.y) & 0xFF); - if(tile->isHouseTile()) - root->addU32(tile->getHouseId()); + root->addU32(g_things.getOtbMajorVersion()); + root->addU32(g_things.getOtbMinorVersion()); - if(tile->getFlags()) { - root->addU8(OTBM_ATTR_TILE_FLAGS); - root->addU32(tile->getFlags()); - } + root->startNode(OTBM_MAP_DATA); + { + // own description. + for(const auto& desc : getDescriptions()) { + root->addU8(OTBM_ATTR_DESCRIPTION); + root->addString(desc); + } - const auto& itemList = tile->getItems(); - const ItemPtr& ground = tile->getGround(); - if(ground) { - // Those types are called "complex" needs other stuff to be written. - // For containers, there is container items, for depot, depot it and so on. - if(!ground->isContainer() && !ground->isDepot() - && !ground->isDoor() && !ground->isTeleport()) { - root->addU8(OTBM_ATTR_ITEM); - root->addU16(ground->getServerId()); - } else - ground->serializeItem(root); + // special one + root->addU8(OTBM_ATTR_DESCRIPTION); + root->addString(stdext::format("Saved with %s v%s", g_app.getName(), g_app.getVersion())); + + // spawn file. + root->addU8(OTBM_ATTR_SPAWN_FILE); + root->addString(spawnFile); + + // house file. + root->addU8(OTBM_ATTR_HOUSE_FILE); + root->addString(houseFile); + + int px = -1, py = -1, pz =-1; + bool firstNode = true; + + for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) { + for(const auto& it : m_tileBlocks[z]) { + const TileBlock& block = it.second; + for(const TilePtr& tile : block.getTiles()) { + if(!tile || tile->isEmpty()) + continue; + + const Position& pos = tile->getPosition(); + if(!pos.isValid()) + continue; + + if(pos.x < px || pos.x >= px + 256 + || pos.y < py || pos.y >= py + 256 + || pos.z != pz) { + if(!firstNode) + root->endNode(); /// OTBM_TILE_AREA + + firstNode = false; + root->startNode(OTBM_TILE_AREA); + + px = pos.x & 0xFF00; + py = pos.y & 0xFF00; + pz = pos.z; + root->addPos(px, py, pz); + } + + root->startNode(tile->isHouseTile() ? OTBM_HOUSETILE : OTBM_TILE); + root->addPoint(Point(pos.x, pos.y) & 0xFF); + if(tile->isHouseTile()) + root->addU32(tile->getHouseId()); + + if(tile->getFlags()) { + root->addU8(OTBM_ATTR_TILE_FLAGS); + root->addU32(tile->getFlags()); + } + + const auto& itemList = tile->getItems(); + const ItemPtr& ground = tile->getGround(); + if(ground) { + // Those types are called "complex" needs other stuff to be written. + // For containers, there is container items, for depot, depot it and so on. + if(!ground->isContainer() && !ground->isDepot() + && !ground->isDoor() && !ground->isTeleport()) { + root->addU8(OTBM_ATTR_ITEM); + root->addU16(ground->getServerId()); + } else + ground->serializeItem(root); + } + for(const ItemPtr& item : itemList) + if(!item->isGround()) + item->serializeItem(root); + + root->endNode(); // OTBM_TILE } - for(const ItemPtr& item : itemList) - if(!item->isGround()) - item->serializeItem(root); - - root->endNode(); // OTBM_TILE } } - } - if(!firstNode) - root->endNode(); // OTBM_TILE_AREA + if(!firstNode) + root->endNode(); // OTBM_TILE_AREA - root->startNode(OTBM_TOWNS); - for(const TownPtr& town : g_towns.getTowns()) { - root->startNode(OTBM_TOWN); + root->startNode(OTBM_TOWNS); + for(const TownPtr& town : g_towns.getTowns()) { + root->startNode(OTBM_TOWN); - root->addU32(town->getId()); - root->addString(town->getName()); + root->addU32(town->getId()); + root->addString(town->getName()); - Position townPos = town->getPos(); - root->addPos(townPos.x, townPos.y, townPos.z); + Position townPos = town->getPos(); + root->addPos(townPos.x, townPos.y, townPos.z); + root->endNode(); + } root->endNode(); - } - root->endNode(); - if(version > 1) { - root->startNode(OTBM_WAYPOINTS); - for(const auto& it : m_waypoints) { - root->startNode(OTBM_WAYPOINT); - root->addString(it.second); + if(version > 1) { + root->startNode(OTBM_WAYPOINTS); + for(const auto& it : m_waypoints) { + root->startNode(OTBM_WAYPOINT); + root->addString(it.second); - Position pos = it.first; - root->addPos(pos.x, pos.y, pos.z); + Position pos = it.first; + root->addPos(pos.x, pos.y, pos.z); + root->endNode(); + } root->endNode(); } - root->endNode(); } + root->endNode(); // OTBM_MAP_DATA } - root->endNode(); // OTBM_MAP_DATA - } - root->endNode(); + root->endNode(); - fin->flush(); - fin->close(); + fin->flush(); + fin->close(); + } catch(std::exception& e) { + g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what())); + } } bool Map::loadOtcm(const std::string& fileName) @@ -538,3 +544,5 @@ void Map::saveOtcm(const std::string& fileName) g_logger.error(stdext::format("failed to save OTCM map: %s", e.what())); } } + +/* vim: set ts=4 sw=4 et: */