コード例 #1
0
ファイル: iomap.cpp プロジェクト: LuisPro/forgottenserver
bool IOMap::parseWaypoints(OTB::Loader& loader, const OTB::Node& waypointsNode, Map& map)
{
	PropStream propStream;
	for (auto& node : waypointsNode.children) {
		if (node.type != OTBM_WAYPOINT) {
			setLastErrorString("Unknown waypoint node.");
			return false;
		}

		if (!loader.getProps(node, propStream)) {
			setLastErrorString("Could not read waypoint data.");
			return false;
		}

		std::string name;
		if (!propStream.readString(name)) {
			setLastErrorString("Could not read waypoint name.");
			return false;
		}

		OTBM_Destination_coords waypoint_coords;
		if (!propStream.read(waypoint_coords)) {
			setLastErrorString("Could not read waypoint coordinates.");
			return false;
		}

		map.waypoints[name] = Position(waypoint_coords.x, waypoint_coords.y, waypoint_coords.z);
	}
	return true;
}
コード例 #2
0
ファイル: iomap.cpp プロジェクト: LuisPro/forgottenserver
bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName)
{
	PropStream propStream;
	if (!loader.getProps(mapNode, 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 = fileName.substr(0, fileName.rfind('/') + 1);
				map.spawnfile += tmp;
				break;

			case OTBM_ATTR_EXT_HOUSE_FILE:
				if (!propStream.readString(tmp)) {
					setLastErrorString("Invalid house tag.");
					return false;
				}

				map.housefile = fileName.substr(0, fileName.rfind('/') + 1);
				map.housefile += tmp;
				break;

			default:
				setLastErrorString("Unknown header node.");
				return false;
		}
	}
	return true;
}
コード例 #3
0
ファイル: iomap.cpp プロジェクト: LuisPro/forgottenserver
bool IOMap::parseTowns(OTB::Loader& loader, const OTB::Node& townsNode, Map& map)
{
	for (auto& townNode : townsNode.children) {
		PropStream propStream;
		if (townNode.type != OTBM_TOWN) {
			setLastErrorString("Unknown town node.");
			return false;
		}

		if (!loader.getProps(townNode, 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);

		OTBM_Destination_coords town_coords;
		if (!propStream.read(town_coords)) {
			setLastErrorString("Could not read town coordinates.");
			return false;
		}

		town->setTemplePos(Position(town_coords.x, town_coords.y, town_coords.z));
	}
	return true;
}
コード例 #4
0
ファイル: iomap.cpp プロジェクト: Fir3element/035
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;
}
コード例 #5
0
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;
}
コード例 #6
0
ファイル: iomapotbm.cpp プロジェクト: JoseEduardo/otserv
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;
}
コード例 #7
0
ファイル: iomap.cpp プロジェクト: LuisPro/forgottenserver
bool IOMap::loadMap(Map* map, const std::string& fileName)
{
	int64_t start = OTSYS_TIME();
	OTB::Loader loader{fileName, OTB::Identifier{{'O', 'T', 'B', 'M'}}};
	auto& root = loader.parseTree();

	PropStream propStream;
	if (!loader.getProps(root, propStream)) {
		setLastErrorString("Could not read root property.");
		return false;
	}

	OTBM_root_header root_header;
	if (!propStream.read(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 > Item::items.majorVersion) {
		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 > Item::items.minorVersion) {
		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;

	if (root.children.size() != 1 || root.children[0].type != OTBM_MAP_DATA) {
		setLastErrorString("Could not read data node.");
		return false;
	}

	auto& mapNode = root.children[0];
	if (!parseMapDataAttributes(loader, mapNode, *map, fileName)) {
		return false;
	}

	for (auto& mapDataNode : mapNode.children) {
		if (mapDataNode.type == OTBM_TILE_AREA) {
			if (!parseTileArea(loader, mapDataNode, *map)) {
				return false;
			}
		} else if (mapDataNode.type == OTBM_TOWNS) {
			if (!parseTowns(loader, mapDataNode, *map)) {
				return false;
			}
		} else if (mapDataNode.type == OTBM_WAYPOINTS && headerVersion > 1) {
			if (!parseWaypoints(loader, mapDataNode, *map)) {
				return false;
			}
		} else {
			setLastErrorString("Unknown map node.");
			return false;
		}
	}

	std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl;
	return true;
}
コード例 #8
0
ファイル: iomap.cpp プロジェクト: LuisPro/forgottenserver
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;
}