コード例 #1
0
ファイル: mapio.cpp プロジェクト: DevelopersPL/otclient
void Map::loadOtbm(const std::string& fileName)
{
    try {
        if(!g_things.isOtbLoaded())
            stdext::throw_exception("OTB isn't loaded yet to load a map.");

        FileStreamPtr fin = g_resources.openFile(fileName);
        if(!fin)
            stdext::throw_exception(stdext::format("Unable to load map '%s'", fileName));
        fin->cache();

        char identifier[4];
        if(fin->read(identifier, 1, 4) < 4)
            stdext::throw_exception("Could not read file identifier");

        if(memcmp(identifier, "OTBM", 4) != 0 && memcmp(identifier, "\0\0\0\0", 4) != 0)
            stdext::throw_exception(stdext::format("Invalid file identifier detected: %s", identifier));

        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));

        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",
                                               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",
                                        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));
            }
        }

        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(unlikely(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);
                    }

                    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(unlikely(nodeItem->getU8() != OTBM_ITEM))
                            stdext::throw_exception("invalid item node");

                        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");

                                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();
                        }

                        addThing(item, pos);
                    }

                    if(const TilePtr& tile = getTile(pos)) {
                        if(house)
                            tile->setFlag(TILESTATE_HOUSE);
                        tile->setFlag(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)));
                }
                g_towns.sort();
            } 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();

                    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));
        }

        fin->close();
    } catch(std::exception& e) {
        g_logger.error(stdext::format("Failed to load '%s': %s", fileName, e.what()));
    }
}