bool IOMap::loadMap(Map* map, const std::string& identifier) { int64_t start = OTSYS_TIME(); FileLoader f; if (!f.openFile(identifier.c_str(), "OTBM")) { std::ostringstream ss; ss << "Could not open the file " << identifier << '.'; setLastErrorString(ss.str()); return false; } uint32_t type; PropStream propStream; NODE root = f.getChildNode(nullptr, type); if (!f.getProps(root, propStream)) { setLastErrorString("Could not read root property."); return false; } const OTBM_root_header* root_header; if (!propStream.readStruct(root_header)) { setLastErrorString("Could not read header."); return false; } uint32_t headerVersion = root_header->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 need to be upgraded by using the latest map editor version to be able to load correctly."); return false; } if (headerVersion > 2) { setLastErrorString("Unknown OTBM version detected."); return false; } if (root_header->majorVersionItems < 3) { setLastErrorString("This map need to be upgraded by using the latest map editor version to be able to load correctly."); return false; } if (root_header->majorVersionItems > Items::dwMajorVersion) { setLastErrorString("The map was saved with a different items.otb version, an upgraded items.otb is required."); return false; } if (root_header->minorVersionItems < CLIENT_VERSION_810) { setLastErrorString("This map needs to be updated."); return false; } if (root_header->minorVersionItems > Items::dwMinorVersion) { std::cout << "[Warning - IOMap::loadMap] This map needs an updated items.otb." << std::endl; } std::cout << "> Map size: " << root_header->width << "x" << root_header->height << '.' << std::endl; map->width = root_header->width; map->height = root_header->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 mapDescription; std::string tmp; uint8_t attribute; while (propStream.read<uint8_t>(attribute)) { switch (attribute) { case OTBM_ATTR_DESCRIPTION: if (!propStream.readString(mapDescription)) { setLastErrorString("Invalid description tag."); return false; } break; case OTBM_ATTR_EXT_SPAWN_FILE: if (!propStream.readString(tmp)) { setLastErrorString("Invalid spawn tag."); return false; } map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1); map->spawnfile += tmp; break; case OTBM_ATTR_EXT_HOUSE_FILE: if (!propStream.readString(tmp)) { setLastErrorString("Invalid house tag."); return false; } map->housefile = identifier.substr(0, identifier.rfind('/') + 1); map->housefile += tmp; break; default: setLastErrorString("Unknown header node."); return false; } } 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; } const OTBM_Destination_coords* area_coord; if (!propStream.readStruct(area_coord)) { setLastErrorString("Invalid map node."); return false; } int32_t base_x = area_coord->x; int32_t base_y = area_coord->y; int32_t base_z = area_coord->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) { setLastErrorString("Unknown tile node."); return false; } if (!f.getProps(nodeTile, propStream)) { setLastErrorString("Could not read node data."); return false; } const OTBM_Tile_coords* tile_coord; if (!propStream.readStruct(tile_coord)) { setLastErrorString("Could not read tile position."); return false; } uint16_t px = base_x + tile_coord->x; uint16_t py = base_y + tile_coord->y; uint16_t pz = base_z; bool isHouseTile = false; House* house = nullptr; Tile* tile = nullptr; Item* ground_item = nullptr; uint32_t tileflags = TILESTATE_NONE; if (type == OTBM_HOUSETILE) { uint32_t houseId; if (!propStream.read<uint32_t>(houseId)) { std::ostringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not read house id."; setLastErrorString(ss.str()); return false; } house = map->houses.addHouse(houseId); if (!house) { std::ostringstream 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(reinterpret_cast<HouseTile*>(tile)); isHouseTile = true; } //read tile attributes while (propStream.read<uint8_t>(attribute)) { switch (attribute) { case OTBM_ATTR_TILE_FLAGS: { uint32_t flags; if (!propStream.read<uint32_t>(flags)) { std::ostringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to read tile flags."; setLastErrorString(ss.str()); return false; } if ((flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE) { tileflags |= TILESTATE_PROTECTIONZONE; } else if ((flags & TILESTATE_NOPVPZONE) == TILESTATE_NOPVPZONE) { tileflags |= TILESTATE_NOPVPZONE; } else if ((flags & TILESTATE_PVPZONE) == TILESTATE_PVPZONE) { tileflags |= TILESTATE_PVPZONE; } if ((flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT) { tileflags |= TILESTATE_NOLOGOUT; } break; } case OTBM_ATTR_ITEM: { Item* item = Item::CreateItem(propStream); if (!item) { std::ostringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to create item."; setLastErrorString(ss.str()); return false; } if (isHouseTile && item->isMoveable()) { std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << px << ", y: " << py << ", z: " << pz << "]." << std::endl; delete item; } else { if (item->getItemCount() <= 0) { item->setItemCount(1); } if (tile) { tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } else if (item->isGroundTile()) { delete ground_item; ground_item = item; } else { tile = createTile(ground_item, item, px, py, pz); tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } } break; } default: std::ostringstream 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) { std::ostringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Unknown node type."; setLastErrorString(ss.str()); return false; } PropStream stream; if (!f.getProps(nodeItem, stream)) { setLastErrorString("Invalid item node."); return false; } Item* item = Item::CreateItem(stream); if (!item) { std::ostringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to create item."; setLastErrorString(ss.str()); return false; } if (!item->unserializeItemNode(f, nodeItem, stream)) { std::ostringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to load item " << item->getID() << '.'; setLastErrorString(ss.str()); delete item; return false; } if (isHouseTile && item->isMoveable()) { std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << px << ", y: " << py << ", z: " << pz << "]." << std::endl; delete item; } else { if (item->getItemCount() <= 0) { item->setItemCount(1); } if (tile) { tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } else if (item->isGroundTile()) { delete ground_item; ground_item = item; } else { tile = createTile(ground_item, item, px, py, pz); tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } } nodeItem = f.getNextNode(nodeItem, type); } if (!tile) { tile = createTile(ground_item, nullptr, px, py, pz); } tile->setFlag(static_cast<tileflags_t>(tileflags)); map->setTile(px, py, pz, tile); nodeTile = f.getNextNode(nodeTile, type); } } else if (type == OTBM_TOWNS) { NODE nodeTown = f.getChildNode(nodeMapData, type); while (nodeTown != NO_NODE) { if (type != OTBM_TOWN) { setLastErrorString("Unknown town node."); return false; } if (!f.getProps(nodeTown, propStream)) { setLastErrorString("Could not read town data."); return false; } uint32_t townId; if (!propStream.read<uint32_t>(townId)) { setLastErrorString("Could not read town id."); return false; } Town* town = map->towns.getTown(townId); if (!town) { town = new Town(townId); map->towns.addTown(townId, town); } std::string townName; if (!propStream.readString(townName)) { setLastErrorString("Could not read town name."); return false; } town->setName(townName); const OTBM_Destination_coords* town_coords; if (!propStream.readStruct(town_coords)) { setLastErrorString("Could not read town coordinates."); return false; } town->setTemplePos(Position(town_coords->x, town_coords->y, town_coords->z)); 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) { setLastErrorString("Unknown waypoint node."); return false; } if (!f.getProps(nodeWaypoint, propStream)) { setLastErrorString("Could not read waypoint data."); return false; } std::string name; if (!propStream.readString(name)) { setLastErrorString("Could not read waypoint name."); return false; } const OTBM_Destination_coords* waypoint_coords; if (!propStream.readStruct(waypoint_coords)) { setLastErrorString("Could not read waypoint coordinates."); return false; } map->waypoints[name] = Position(waypoint_coords->x, waypoint_coords->y, waypoint_coords->z); nodeWaypoint = f.getNextNode(nodeWaypoint, type); } } else { setLastErrorString("Unknown map node."); return false; } nodeMapData = f.getNextNode(nodeMapData, type); } std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; return true; }
bool IOMap::loadMap(Map* map, const std::string& identifier) { FileLoader f; if(!f.openFile(identifier.c_str(), 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.GET_STRUCT(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 > 2) { 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::cout << "> 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.GET_UCHAR(attribute)) { switch(attribute) { case OTBM_ATTR_DESCRIPTION: { if(!propStream.GET_STRING(tmp)) { setLastErrorString("Invalid description tag."); return false; } map->descriptions.push_back(tmp); break; } case OTBM_ATTR_EXT_SPAWN_FILE: { if(!propStream.GET_STRING(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.GET_STRING(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::cout << "> Map descriptions: " << std::endl; for(StringVec::iterator it = map->descriptions.begin(); it != map->descriptions.end(); ++it) std::cout << (*it) << std::endl; 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* area_coord; if(!propStream.GET_STRUCT(area_coord)) { setLastErrorString("Invalid map node."); return false; } int32_t base_x = area_coord->_x, base_y = area_coord->_y, base_z = area_coord->_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.GET_STRUCT(tileCoord)) { setLastErrorString("Could not read tile position."); return false; } Tile* tile = NULL; Item* groundItem = NULL; uint32_t tileflags = 0; uint16_t px = base_x + tileCoord->_x, py = base_y + tileCoord->_y, pz = base_z; House* house = NULL; if(type == OTBM_HOUSETILE) { uint32_t _houseid; if(!propStream.GET_ULONG(_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; while(propStream.GET_UCHAR(attribute)) { switch(attribute) { case OTBM_ATTR_TILE_FLAGS: { uint32_t flags; if(!propStream.GET_ULONG(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) tileflags |= TILESTATE_PROTECTIONZONE; else if((flags & TILESTATE_NOPVPZONE) == TILESTATE_NOPVPZONE) tileflags |= TILESTATE_NOPVPZONE; else if((flags & TILESTATE_PVPZONE) == TILESTATE_PVPZONE) tileflags |= TILESTATE_PVPZONE; if((flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT) tileflags |= TILESTATE_NOLOGOUT; if((flags & TILESTATE_REFRESH) == TILESTATE_REFRESH) { if(house) std::cout << "[x:" << px << ", y:" << py << ", z:" << pz << "] House tile flagged as refreshing!"; tileflags |= 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(house && item->isMoveable()) { std::cout << "[Warning - IOMap::loadMap] Movable item in house: " << house->getHouseId(); std::cout << ", item type: " << item->getID() << ", at position " << px << "/" << py << "/"; std::cout << pz << std::endl; delete item; item = NULL; } else if(tile) { tile->__internalAddThing(item); item->__startDecaying(); item->setLoadedFromMap(true); } else if(item->isGroundTile()) { if(groundItem) delete groundItem; groundItem = item; } else { tile = createTile(groundItem, item, px, py, pz); tile->__internalAddThing(item); 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(house && item->isMoveable()) { std::cout << "[Warning - IOMap::loadMap] Movable item in house: "; std::cout << house->getHouseId() << ", item type: " << item->getID(); std::cout << ", pos " << px << "/" << py << "/" << pz << std::endl; delete item; item = NULL; } else if(tile) { tile->__internalAddThing(item); item->__startDecaying(); item->setLoadedFromMap(true); } else if(item->isGroundTile()) { if(groundItem) delete groundItem; groundItem = item; } else { tile = createTile(groundItem, item, px, py, pz); tile->__internalAddThing(item); 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(groundItem, NULL, px, py, pz); tile->setFlag((tileflags_t)tileflags); 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.GET_ULONG(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.GET_STRING(townName)) { setLastErrorString("Could not read town name."); return false; } town->setName(townName); OTBM_Destination_coords *town_coords; if(!propStream.GET_STRUCT(town_coords)) { setLastErrorString("Could not read town coordinates."); return false; } town->setTemplePos(Position(town_coords->_x, town_coords->_y, town_coords->_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.GET_STRING(name)) { setLastErrorString("Could not read waypoint name."); return false; } OTBM_Destination_coords* waypoint_coords; if(!propStream.GET_STRUCT(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); } return true; }
bool IOMapOTBM::loadMap(Map* map, const std::string& identifier) { int64_t start = OTSYS_TIME(); 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; } unsigned long type; PropStream propStream; NODE root = f.getChildNode((NODE)NULL, type); if(!f.getProps(root, propStream)){ setLastErrorString("Could not read root property."); return false; } OTBM_root_header root_header; if( !propStream.GET_UINT32(root_header.version) || !propStream.GET_UINT16(root_header.width) || !propStream.GET_UINT16(root_header.height) || !propStream.GET_UINT32(root_header.majorVersionItems) || !propStream.GET_UINT32(root_header.minorVersionItems)) { setLastErrorString("Could not read header."); return false; } int header_version = root_header.version; if(header_version <= 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(header_version > 2){ setLastErrorString("Unknown OTBM version detected, please update your server."); return false; } if(root_header.majorVersionItems < 3){ setLastErrorString("This map needs to be upgraded by using the latest map editor version to be able to load correctly."); return false; } if(root_header.majorVersionItems > (unsigned long)Items::dwMajorVersion){ setLastErrorString("The map was saved with a different items.otb version, an upgraded items.otb is required."); return false; } // Prevent load maps saved with items.otb previous to // version 800, because of the change to stackable of // itemid 3965 if(root_header.minorVersionItems < CLIENT_VERSION_810){ setLastErrorString("This map needs to be updated."); return false; } if(root_header.minorVersionItems > (unsigned long)Items::dwMinorVersion){ std::cout << "Warning: [OTBM loader] This map needs an updated items.otb." <<std::endl; } if(root_header.minorVersionItems == CLIENT_VERSION_854_BAD){ std::cout << "Warning: [OTBM loader] This map needs uses an incorrect version of items.otb." <<std::endl; } std::cout << "Map size: " << root_header.width << "x" << root_header.height << std::endl; map->mapWidth = root_header.width; map->mapHeight = root_header.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; } unsigned char attribute; std::string mapDescription; std::string tmp; while(propStream.GET_UINT8(attribute)){ switch(attribute){ case OTBM_ATTR_DESCRIPTION: if(!propStream.GET_STRING(mapDescription)){ setLastErrorString("Invalid description tag."); return false; } std::cout << "Map description: " << mapDescription << std::endl; break; case OTBM_ATTR_EXT_SPAWN_FILE: if(!propStream.GET_STRING(tmp)){ setLastErrorString("Invalid spawn tag."); return false; } map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1); map->spawnfile += tmp; break; case OTBM_ATTR_EXT_HOUSE_FILE: if(!propStream.GET_STRING(tmp)){ setLastErrorString("Invalid house tag."); return false; } map->housefile = identifier.substr(0, identifier.rfind('/') + 1); map->housefile += tmp; break; default: setLastErrorString("Unknown header node."); return false; break; } } 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_Tile_area_coords area_coord; if( !propStream.GET_UINT16(area_coord._x) || !propStream.GET_UINT16(area_coord._y) || !propStream.GET_UINT8(area_coord._z)) { setLastErrorString("Invalid map node."); return false; } int32_t base_x, base_y, base_z; base_x = area_coord._x; base_y = area_coord._y; base_z = area_coord._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; } unsigned short px, py, pz; OTBM_Tile_coords tile_coord; if( !propStream.GET_UINT8(tile_coord._x) || !propStream.GET_UINT8(tile_coord._y)) { setLastErrorString("Could not read tile position."); return false; } px = base_x + tile_coord._x; py = base_y + tile_coord._y; pz = base_z; bool isHouseTile = false; House* house = NULL; Tile* tile = NULL; Item* ground_item = NULL; uint32_t tileflags = TILESTATE_NONE; if(type == OTBM_HOUSETILE){ uint32_t _houseid; if(!propStream.GET_UINT32(_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)); isHouseTile = true; } //read tile attributes unsigned char attribute; while(propStream.GET_UINT8(attribute)){ switch(attribute){ case OTBM_ATTR_TILE_FLAGS: { uint32_t flags; if(!propStream.GET_UINT32(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){ tileflags |= TILESTATE_PROTECTIONZONE; } else if((flags & TILESTATE_NOPVPZONE) == TILESTATE_NOPVPZONE){ tileflags |= TILESTATE_NOPVPZONE; } else if((flags & TILESTATE_PVPZONE) == TILESTATE_PVPZONE){ tileflags |= TILESTATE_PVPZONE; } if((flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT){ tileflags |= TILESTATE_NOLOGOUT; } if((flags & TILESTATE_REFRESH) == TILESTATE_REFRESH){ if(house){ std::cout << "Warning [x:" << px << ", y:" << py << ", z:" << pz << "] " << " House tile flagged as refreshing!"; } tileflags |= 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(isHouseTile && !item->isNotMoveable()){ std::cout << "Warning: [OTBM loader] Moveable item in house id = " << house->getId() << " Item type = " << item->getID() << std::endl; delete item; item = NULL; } else{ if(tile){ tile->__internalAddThing(item); item->__startDecaying(); } else if(item->isGroundTile()){ if(ground_item) delete ground_item; ground_item = item; } else{ // !tile tile = createTile(ground_item, item, px, py, pz); tile->__internalAddThing(item); item->__startDecaying(); } } break; } default: std::stringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] " << "Unknown tile attribute."; setLastErrorString(ss.str()); return false; break; } } 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(isHouseTile && !item->isNotMoveable()){ std::cout << "Warning: [OTBM loader] Moveable item in house id = " << house->getId() << " Item type = " << item->getID() << std::endl; delete item; } else{ if(tile){ tile->__internalAddThing(item); item->__startDecaying(); } else if(item->isGroundTile()){ if(ground_item) delete ground_item; ground_item = item; } else{ // !tile tile = createTile(ground_item, item, px, py, pz); tile->__internalAddThing(item); item->__startDecaying(); } } } else{ std::stringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] " << "Failed to load item " << item->getID() << "."; setLastErrorString(ss.str()); delete item; 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_item, NULL, px, py, pz); tile->setFlag((tileflags_t)tileflags); 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.GET_UINT32(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.GET_STRING(townName)){ setLastErrorString("Could not read town name."); return false; } town->setName(townName); OTBM_TownTemple_coords town_coords; if( !propStream.GET_UINT16(town_coords._x) || !propStream.GET_UINT16(town_coords._y) || !propStream.GET_UINT8(town_coords._z)) { setLastErrorString("Could not read town coordinates."); return false; } Position pos; pos.x = town_coords._x; pos.y = town_coords._y; pos.z = town_coords._z; town->setTemplePos(pos); } else{ setLastErrorString("Unknown town node."); return false; } nodeTown = f.getNextNode(nodeTown, type); } } else if(type == OTBM_WAYPOINTS && header_version >= 2){ 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; Position pos; if(!propStream.GET_STRING(name)){ setLastErrorString("Could not read waypoint name."); return false; } OTBM_TownTemple_coords wp_coords; if( !propStream.GET_UINT16(wp_coords._x) || !propStream.GET_UINT16(wp_coords._y) || !propStream.GET_UINT8(wp_coords._z)) { setLastErrorString("Could not read waypoint coordinates."); return false; } pos.x = wp_coords._x; pos.y = wp_coords._y; pos.z = wp_coords._z; Waypoint_ptr wp(new Waypoint(name, pos)); map->waypoints.addWaypoint(wp); } else{ setLastErrorString("Unknown waypoint node."); return false; } nodeWaypoint = f.getNextNode(nodeWaypoint, type); } } else{ setLastErrorString("Unknown map node."); return false; } nodeMapData = f.getNextNode(nodeMapData, type); } std::cout << "Notice: [OTBM Loader] Loading time : " << (OTSYS_TIME() - start)/(1000.) << " s" << std::endl; return true; }
void Action::undo(DirtyList* dirty_list) { if(changes.empty()) return; editor.selection.start(Selection::INTERNAL); ChangeList::reverse_iterator it = changes.rbegin(); while(it != changes.rend()) { Change* c = *it; switch(c->type) { case CHANGE_TILE: { void** data = &c->data; Tile* oldtile = reinterpret_cast<Tile*>(*data); ASSERT(oldtile); Position pos = oldtile->getPosition(); if(editor.IsLiveClient()) { QTreeNode* nd = editor.map.getLeaf(pos.x, pos.y); if(!nd || !nd->isVisible(pos.z > 7)) { // Delete all changes that affect tiles outside our view c->clear(); ++it; continue; } } Tile* newtile = editor.map.swapTile(pos, oldtile); // Update server side change list (for broadcast) if(editor.IsLiveServer() && dirty_list) dirty_list->AddPosition(pos.x, pos.y, pos.z); if(oldtile->isSelected()) editor.selection.addInternal(oldtile); if(newtile->isSelected()) editor.selection.removeInternal(newtile); if(newtile->getHouseID() != oldtile->getHouseID()) { // oooooomggzzz we need to remove it from the appropriate house! House* house = editor.map.houses.getHouse(newtile->getHouseID()); if(house) { house->removeTile(newtile); } else { // Set tile house to 0, house has been removed newtile->setHouse(nullptr); } house = editor.map.houses.getHouse(oldtile->getHouseID()); if(house) { house->addTile(oldtile); } } if(oldtile->spawn) { if(newtile->spawn) { if(*oldtile->spawn != *newtile->spawn) { editor.map.removeSpawn(newtile); editor.map.addSpawn(oldtile); } } else { editor.map.addSpawn(oldtile); } } else if(newtile->spawn) { editor.map.removeSpawn(newtile); } *data = newtile; // Update client dirty list if(editor.IsLiveClient() && dirty_list && type != ACTION_REMOTE) { // Local action, assemble changes dirty_list->AddChange(c); } } break; case CHANGE_MOVE_HOUSE_EXIT: { std::pair<uint32_t, Position>* p = reinterpret_cast<std::pair<uint32_t, Position>* >(c->data); ASSERT(p); House* whathouse = editor.map.houses.getHouse(p->first); if(whathouse) { Position oldpos = whathouse->getExit(); whathouse->setExit(p->second); p->second = oldpos; } } break; case CHANGE_MOVE_WAYPOINT: { std::pair<std::string, Position>* p = reinterpret_cast<std::pair<std::string, Position>* >(c->data); ASSERT(p); Waypoint* wp = editor.map.waypoints.getWaypoint(p->first); if(wp) { // Change the tiles TileLocation* oldtile = editor.map.getTileL(wp->pos); TileLocation* newtile = editor.map.getTileL(p->second); // Only need to remove from old if it actually exists if(p->second != Position()) if(oldtile && oldtile->getWaypointCount() > 0) oldtile->decreaseWaypointCount(); newtile->increaseWaypointCount(); // Update shit Position oldpos = wp->pos; wp->pos = p->second; p->second = oldpos; } } break; default: break; } ++it; } editor.selection.finish(Selection::INTERNAL); commited = false; }
void Action::commit(DirtyList* dirty_list) { editor.selection.start(Selection::INTERNAL); ChangeList::const_iterator it = changes.begin(); while(it != changes.end()) { Change* c = *it; switch(c->type) { case CHANGE_TILE: { void** data = &c->data; Tile* newtile = reinterpret_cast<Tile*>(*data); ASSERT(newtile); Position pos = newtile->getPosition(); if(editor.IsLiveClient()) { QTreeNode* nd = editor.map.getLeaf(pos.x, pos.y); if(!nd || !nd->isVisible(pos.z > 7)) { // Delete all changes that affect tiles outside our view c->clear(); ++it; continue; } } Tile* oldtile = editor.map.swapTile(pos, newtile); TileLocation* location = newtile->getLocation(); // Update other nodes in the network if(editor.IsLiveServer() && dirty_list) dirty_list->AddPosition(pos.x, pos.y, pos.z); newtile->update(); //std::cout << "\tSwitched tile at " << pos.x << ";" << pos.y << ";" << pos.z << " from " << (void*)oldtile << " to " << *data << std::endl; if(newtile->isSelected()) editor.selection.addInternal(newtile); if(oldtile) { if(newtile->getHouseID() != oldtile->getHouseID()) { // oooooomggzzz we need to add it to the appropriate house! House* house = editor.map.houses.getHouse(oldtile->getHouseID()); if(house) house->removeTile(oldtile); house = editor.map.houses.getHouse(newtile->getHouseID()); if(house) house->addTile(newtile); } if(oldtile->spawn) { if(newtile->spawn) { if(*oldtile->spawn != *newtile->spawn) { editor.map.removeSpawn(oldtile); editor.map.addSpawn(newtile); } } else { // Spawn has been removed editor.map.removeSpawn(oldtile); } } else if(newtile->spawn) { editor.map.addSpawn(newtile); } //oldtile->update(); if(oldtile->isSelected()) editor.selection.removeInternal(oldtile); *data = oldtile; } else { *data = editor.map.allocator(location); if(newtile->getHouseID() != 0) { // oooooomggzzz we need to add it to the appropriate house! House* house = editor.map.houses.getHouse(newtile->getHouseID()); if(house) { house->addTile(newtile); } } if(newtile->spawn) editor.map.addSpawn(newtile); } // Mark the tile as modified newtile->modify(); // Update client dirty list if(editor.IsLiveClient() && dirty_list && type != ACTION_REMOTE) { // Local action, assemble changes dirty_list->AddChange(c); } } break; case CHANGE_MOVE_HOUSE_EXIT: { std::pair<uint32_t, Position>* p = reinterpret_cast<std::pair<uint32_t, Position>* >(c->data); ASSERT(p); House* whathouse = editor.map.houses.getHouse(p->first); if(whathouse) { Position oldpos = whathouse->getExit(); whathouse->setExit(p->second); p->second = oldpos; } } break; case CHANGE_MOVE_WAYPOINT: { std::pair<std::string, Position>* p = reinterpret_cast<std::pair<std::string, Position>* >(c->data); ASSERT(p); Waypoint* wp = editor.map.waypoints.getWaypoint(p->first); if(wp) { // Change the tiles TileLocation* oldtile = editor.map.getTileL(wp->pos); TileLocation* newtile = editor.map.getTileL(p->second); // Only need to remove from old if it actually exists if(p->second != Position()) if(oldtile && oldtile->getWaypointCount() > 0) oldtile->decreaseWaypointCount(); newtile->increaseWaypointCount(); // Update shit Position oldpos = wp->pos; wp->pos = p->second; p->second = oldpos; } } break; default: break; } ++it; } editor.selection.finish(Selection::INTERNAL); commited = true; }
bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map) { PropStream propStream; if (!loader.getProps(tileAreaNode, propStream)) { setLastErrorString("Invalid map node."); return false; } OTBM_Destination_coords area_coord; if (!propStream.read(area_coord)) { setLastErrorString("Invalid map node."); return false; } uint16_t base_x = area_coord.x; uint16_t base_y = area_coord.y; uint16_t z = area_coord.z; for (auto& tileNode : tileAreaNode.children) { if (tileNode.type != OTBM_TILE && tileNode.type != OTBM_HOUSETILE) { setLastErrorString("Unknown tile node."); return false; } if (!loader.getProps(tileNode, propStream)) { setLastErrorString("Could not read node data."); return false; } OTBM_Tile_coords tile_coord; if (!propStream.read(tile_coord)) { setLastErrorString("Could not read tile position."); return false; } uint16_t x = base_x + tile_coord.x; uint16_t y = base_y + tile_coord.y; bool isHouseTile = false; House* house = nullptr; Tile* tile = nullptr; Item* ground_item = nullptr; uint32_t tileflags = TILESTATE_NONE; if (tileNode.type == OTBM_HOUSETILE) { uint32_t houseId; if (!propStream.read<uint32_t>(houseId)) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not read house id."; setLastErrorString(ss.str()); return false; } house = map.houses.addHouse(houseId); if (!house) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId; setLastErrorString(ss.str()); return false; } tile = new HouseTile(x, y, z, house); house->addTile(static_cast<HouseTile*>(tile)); isHouseTile = true; } uint8_t attribute; //read tile attributes while (propStream.read<uint8_t>(attribute)) { switch (attribute) { case OTBM_ATTR_TILE_FLAGS: { uint32_t flags; if (!propStream.read<uint32_t>(flags)) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags."; setLastErrorString(ss.str()); return false; } if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) { tileflags |= TILESTATE_PROTECTIONZONE; } else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) { tileflags |= TILESTATE_NOPVPZONE; } else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) { tileflags |= TILESTATE_PVPZONE; } if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) { tileflags |= TILESTATE_NOLOGOUT; } break; } case OTBM_ATTR_ITEM: { Item* item = Item::CreateItem(propStream); if (!item) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; setLastErrorString(ss.str()); return false; } if (isHouseTile && item->isMoveable()) { std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl; delete item; } else { if (item->getItemCount() <= 0) { item->setItemCount(1); } if (tile) { tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } else if (item->isGroundTile()) { delete ground_item; ground_item = item; } else { tile = createTile(ground_item, item, x, y, z); tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } } break; } default: std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute."; setLastErrorString(ss.str()); return false; } } for (auto& itemNode : tileNode.children) { if (itemNode.type != OTBM_ITEM) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown node type."; setLastErrorString(ss.str()); return false; } PropStream stream; if (!loader.getProps(itemNode, stream)) { setLastErrorString("Invalid item node."); return false; } Item* item = Item::CreateItem(stream); if (!item) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; setLastErrorString(ss.str()); return false; } if (!item->unserializeItemNode(loader, itemNode, stream)) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to load item " << item->getID() << '.'; setLastErrorString(ss.str()); delete item; return false; } if (isHouseTile && item->isMoveable()) { std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl; delete item; } else { if (item->getItemCount() <= 0) { item->setItemCount(1); } if (tile) { tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } else if (item->isGroundTile()) { delete ground_item; ground_item = item; } else { tile = createTile(ground_item, item, x, y, z); tile->internalAddThing(item); item->startDecaying(); item->setLoadedFromMap(true); } } } if (!tile) { tile = createTile(ground_item, nullptr, x, y, z); } tile->setFlag(static_cast<tileflags_t>(tileflags)); map.setTile(x, y, z, tile); } return true; }
bool IOMapOTMM::loadMap(Map& map, NodeFileReadHandle& f, const FileName& identifier, bool showdialog) { BinaryNode* root = f.getRootNode(); if(!root) { error(wxT("Could not read root node.")); return false; } root->skip(1); // Skip the type byte uint8_t u8; uint16_t u16; uint32_t u32; if(!root->getU32(u32) || u32 != 1) { // Version error(wxT("Unsupported or damaged map version.")); return false; } if(!root->getU16(u16)) { error(wxT("Could not read root header.")); return false; } map.width = u16; if(!root->getU16(u16)) { error(wxT("Could not read root header.")); return false; } map.height = u16; if(!root->getU32(u32) || u32 > (unsigned long)item_db.MajorVersion) { // OTB major version if(queryUser(wxT("Map error"), wxT("The loaded map appears to be a items.otb format that deviates from the items.otb loaded by the editor. Do you still want to attempt to load the map?"))) { warning(wxT("Unsupported or damaged map version")); } else { error(wxT("Outdated items.otb, could not load map.")); return false; } } if(!root->getU32(u32) || u32 > (unsigned long)item_db.MinorVersion) { // OTB minor version warning(wxT("The editor needs an updated items.otb version.")); } BinaryNode* mapHeaderNode = root->getChild(); if(mapHeaderNode == NULL || !mapHeaderNode->getByte(u8) || u8 != OTMM_MAP_DATA) { error(wxT("Could not get root child node. Cannot recover from fatal error!")); return false; } int nodes_loaded = 0; BinaryNode* mapNode = mapHeaderNode->getChild(); if(mapNode) do { ++nodes_loaded; if(showdialog && nodes_loaded % 15 == 0) { gui.SetLoadDone(int(100.0 * f.tell() / f.size())); } uint8_t node_type; if(!mapNode->getByte(node_type)) { warning(wxT("Invalid map node")); continue; } switch(node_type) { case OTMM_EDITOR: { } break; case OTMM_DESCRIPTION: { std::string desc; mapNode->getString(desc); map.setMapDescription(desc); } break; case OTMM_TILE_DATA: { BinaryNode* tileNode = mapNode->getChild(); if(tileNode) do { Tile* tile = NULL; uint8_t tile_type; if(!tileNode->getByte(tile_type)) { warning(wxT("Invalid tile type")); continue; } if(tile_type != OTMM_TILE && tile_type != OTMM_HOUSETILE) { warning(wxT("Unknown type of tile node")); continue; } uint16_t x_offset, y_offset; uint8_t z_offset; if(!tileNode->getU16(x_offset) || !tileNode->getU16(y_offset) || !tileNode->getU8(z_offset) ) { warning(wxT("Could not read position of tile")); continue; } const Position pos(x_offset, y_offset, z_offset); if(map.getTile(pos)) { warning(wxT("Duplicate tile at %d:%d:%d, discarding duplicate"), pos.x, pos.y, pos.z); continue; } tile = map.allocator(pos); House* house = NULL; if(tile_type == OTMM_HOUSETILE) { uint32_t house_id; if(!tileNode->getU32(house_id)) { warning(wxT("House tile without house data, discarding tile")); continue; } if(house_id) { house = map.houses.getHouse(house_id); if(!house) { house = newd House(map); house->id = house_id; map.houses.addHouse(house); } } else { warning(wxT("Invalid house id from tile %d:%d:%d"), pos.x, pos.y, pos.z); } } uint16_t ground_id; tileNode->getU16(ground_id); if(ground_id != 0) { tile->addItem(Item::Create(ground_id)); } uint8_t attribute; while(tileNode->getU8(attribute)) { switch(attribute) { case OTMM_ATTR_TILE_FLAGS: { uint32_t flags = 0; if(!tileNode->getU32(flags)) { warning(wxT("Invalid tile flags of tile on %d:%d:%d"), pos.x, pos.y, pos.z); } tile->setMapFlags(flags); } break; default: { warning(wxT("Unknown tile attribute at %d:%d:%d"), pos.x, pos.y, pos.z); } break; } } BinaryNode* itemNode = tileNode->getChild(); if(itemNode) do { Item* item = NULL; uint8_t item_type; if(!itemNode->getByte(item_type)) { warning(wxT("Unknown item type %d:%d:%d"), pos.x, pos.y, pos.z); continue; } if(item_type == OTMM_ITEM) { item = Item::Create_OTMM(*this, itemNode); if(item) { if(item->unserializeItemNode_OTMM(*this, itemNode) == false) { warning(wxT("Couldn't unserialize item attributes at %d:%d:%d"), pos.x, pos.y, pos.z); } tile->addItem(item); } } else { warning(wxT("Unknown type of tile child node")); } } while(itemNode->advance()); tile->update(); if(house) { house->addTile(tile); } map.setTile(pos, tile); } while(tileNode->advance()); } break; case OTMM_SPAWN_DATA: { BinaryNode* spawnNode = mapNode->getChild(); if(spawnNode) do { uint8_t spawn_type; if(!spawnNode->getByte(spawn_type)) { warning(wxT("Could not read spawn type.")); continue; } if(spawn_type != OTMM_SPAWN_AREA) { warning(wxT("Invalid spawn type.")); continue; } // Read position uint16_t spawn_x, spawn_y; uint8_t spawn_z; uint32_t radius; if(!spawnNode->getU16(spawn_x) || !spawnNode->getU16(spawn_y) || !spawnNode->getU8(spawn_z) ) { warning(wxT("Could not read spawn position.")); continue; } const Position spawnpos(spawn_x, spawn_y, spawn_z); // Read radius if(!spawnNode->getU32(radius)) { warning(wxT("Could not read spawn radius.")); continue; } // Adjust radius radius = min(radius, uint32_t(settings.getInteger(Config::MAX_SPAWN_RADIUS))); // Create and assign spawn Tile* spawn_tile = map.getTile(spawnpos); if(spawn_tile && spawn_tile->spawn) { warning(wxT("Duplicate spawn on position %d:%d:%d\n"), spawn_tile->getX(), spawn_tile->getY(), spawn_tile->getZ()); continue; } Spawn* spawn = newd Spawn(radius); if(!spawn_tile) { spawn_tile = map.allocator(spawnpos); map.setTile(spawnpos, spawn_tile); } spawn_tile->spawn = spawn; map.addSpawn(spawn_tile); // Read any creatures associated with the spawn BinaryNode* creatureNode = spawnNode->getChild(); if(creatureNode) do { uint8_t creature_type; if(!creatureNode->getByte(creature_type)) { warning(wxT("Could not read type of creature node.")); continue; } bool isNPC; std::string name; uint32_t spawntime = 0; // Only applicable for monsters if(creature_type == OTMM_NPC) { isNPC = true; if(!creatureNode->getString(name)) { warning(wxT("Could not read name of NPC.")); return false; } } else if(creature_type == OTMM_MONSTER) { isNPC = false; if(!creatureNode->getString(name)) { warning(wxT("Could not read name of monster.")); return false; } if(!creatureNode->getU32(spawntime)) { warning(wxT("Could not read spawn time of monster.")); return false; } } else { warning(wxT("Unknown creature node type (0x%.2x)."), creature_type); return false; } // Read creature position uint16_t creature_x, creature_y; uint8_t creature_z; if(!creatureNode->getU16(creature_x) || !creatureNode->getU16(creature_y) || !creatureNode->getU8(creature_z) ) { warning(wxT("Could not read creature position.")); continue; } const Position creaturepos(creature_x, creature_y, creature_z); // Check radius if(uint32_t(abs(creaturepos.x - spawnpos.x)) > radius || uint32_t(abs(creaturepos.y - spawnpos.y)) > radius) { // Outside of the spawn... } // Create creature and put on map Tile* creature_tile; if(creaturepos == spawnpos) { creature_tile = spawn_tile; } else { creature_tile = map.getTile(creaturepos); } if(!creature_tile) { warning(wxT("Discarding creature \"%s\" at %d:%d:%d due to invalid position"), name.c_str(), creaturepos.x, creaturepos.y, creaturepos.z); break; } if(creature_tile->creature) { warning(wxT("Duplicate creature \"%s\" at %d:%d:%d, discarding"), name.c_str(), creaturepos.x, creaturepos.y, creaturepos.z); break; } CreatureType* type = creature_db[name]; if(!type) { type = creature_db.addMissingCreatureType(name, isNPC); } Creature* creature = newd Creature(type); creature->setSpawnTime(spawntime); creature_tile->creature = creature; if(creature_tile->spawn_count == 0) { // No spawn, create a newd one (this happends if the radius of the spawn has been decreased due to settings) ASSERT(creature_tile->spawn == NULL); Spawn* spawn = newd Spawn(5); creature_tile->spawn = spawn; map.addSpawn(creature_tile); } } while(creatureNode->advance()); } while(spawnNode->advance()); } break; case OTMM_TOWN_DATA: { BinaryNode* townNode = mapNode->getChild(); if(townNode) do { uint8_t town_type; if(!townNode->getByte(town_type)) { warning(wxT("Could not read town type")); continue; } if(town_type != OTMM_TOWN) { warning(wxT("Unknown town type")); continue; } uint32_t town_id; if(!townNode->getU32(town_id)) { warning(wxT("Invalid town id")); continue; } Town* town = map.towns.getTown(town_id); if(town) { warning(wxT("Duplicate town id %d, discarding duplicate"), town_id); continue; } else { town = newd Town(town_id); if(!map.towns.addTown(town)) { delete town; continue; } } std::string town_name; if(!townNode->getString(town_name)) { warning(wxT("Invalid town name")); continue; } town->setName(town_name); Position pos; uint16_t x; uint16_t y; uint8_t z; if(!townNode->getU16(x) || !townNode->getU16(y) || !townNode->getU8(z)) { warning(wxT("Invalid town temple position")); continue; } pos.x = x; pos.y = y; pos.z = z; town->setTemplePosition(pos); } while(townNode->advance()); } break; case OTMM_HOUSE_DATA: { BinaryNode* houseNode = mapNode->getChild(); if(houseNode) do { uint8_t house_type; if(!houseNode->getByte(house_type)) { warning(wxT("Could not read house type")); continue; } if(house_type != OTMM_HOUSE) { warning(wxT("Unknown house type.")); continue; } uint32_t house_id; if(!houseNode->getU32(house_id)) { warning(wxT("Could not read house id.")); continue; } House* house = map.houses.getHouse(house_id); if(!house) { continue; } std::string house_name; if(!houseNode->getString(house_name)) { warning(wxT("Could not read house name.")); continue; } uint32_t town_id; if(!houseNode->getU32(town_id)) { warning(wxT("Could not read house town id.")); continue; } uint32_t rent; if(!houseNode->getU32(rent)) { warning(wxT("Could not read house rent.")); continue; } house->name = house_name; house->townid = town_id; house->rent = rent; uint16_t x; uint16_t y; uint8_t z; if(!houseNode->getU16(x) || !houseNode->getU16(y) || !houseNode->getU8(z)) { warning(wxT("Invalid town temple position")); continue; } house->setExit(Position(x, y, z)); } while(houseNode->advance()); } break; } } while(mapNode->advance()); return true; }