예제 #1
0
파일: creatures.cpp 프로젝트: LaloHao/rme
bool CreatureDatabase::loadFromXML(const FileName& filename, bool standard, wxString& error, wxArrayString& warnings)
{
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_file(filename.GetFullPath().mb_str());
	if (!result) {
		error = wxT("Couldn't open file \"") + filename.GetFullName() + wxT("\", invalid format?");
		return false;
	}

	pugi::xml_node node = doc.child("creatures");
	if (!node) {
		error = wxT("Invalid file signature, this file is not a valid creatures file.");
		return false;
	}

	for (pugi::xml_node creatureNode = node.first_child(); creatureNode; creatureNode = creatureNode.next_sibling()) {
		if (as_lower_str(creatureNode.name()) != "creature") {
			continue;
		}

		CreatureType* creatureType = CreatureType::loadFromXML(creatureNode, warnings);
		if (creatureType) {
			creatureType->standard = standard;
			if ((*this)[creatureType->name]) {
				warnings.push_back(wxT("Duplicate creature type name \"") + wxstr(creatureType->name) + wxT("\"! Discarding..."));
				delete creatureType;
			} else {
				creature_map[as_lower_str(creatureType->name)] = creatureType;
			}
		}
	}
	return true;
}
예제 #2
0
void ClientVersion::loadOTBInfo(pugi::xml_node otbNode)
{
	if (as_lower_str(otbNode.name()) != "otb") {
		return;
	}

	pugi::xml_attribute attribute;
	OtbVersion otb = {"", OTB_VERSION_3, CLIENT_VERSION_NONE};
	if (!(attribute = otbNode.attribute("client"))) {
		wxLogError(wxT("Node 'otb' must contain 'client' tag."));
		return;
	}

	otb.name = attribute.as_string();
	if (!(attribute = otbNode.attribute("id"))) {
		wxLogError(wxT("Node 'otb' must contain 'id' tag."));
		return;
	}

	otb.id = pugi::cast<int32_t>(attribute.value());
	if (!(attribute = otbNode.attribute("version"))) {
		wxLogError(wxT("Node 'otb' must contain 'version' tag."));
		return;
	}

	OtbFormatVersion versionId = static_cast<OtbFormatVersion>(pugi::cast<int32_t>(attribute.value()));
	if (versionId < OTB_VERSION_1 || versionId > OTB_VERSION_3) {
		wxLogError(wxT("Node 'otb' unrecognized format version (version 1..3 supported)."));
		return;
	}

	otb.format_version = versionId;
	otb_versions[otb.name] = otb;
}
예제 #3
0
파일: creatures.cpp 프로젝트: LaloHao/rme
CreatureType* CreatureDatabase::operator[](const std::string& name)
{
	CreatureMap::iterator iter = creature_map.find(as_lower_str(name));
	if(iter != creature_map.end()) {
		return iter->second;
	}
	return nullptr;
}
예제 #4
0
파일: waypoints.cpp 프로젝트: TheSumm/rme
void Waypoints::addWaypoint(Waypoint* wp)
{
	removeWaypoint(wp->name);
	if(wp->pos != Position()) {
		Tile* t = map.getTile(wp->pos);
		if(!t)
			map.setTile(wp->pos, t = map.allocator(map.createTileL(wp->pos)));
		t->getLocation()->increaseWaypointCount();
	}
	waypoints.insert(std::make_pair(as_lower_str(wp->name), wp));
}
예제 #5
0
파일: creatures.cpp 프로젝트: LaloHao/rme
CreatureType* CreatureDatabase::addCreatureType(const std::string& name, bool isNpc, const Outfit& outfit)
{
	assert((*this)[name] == nullptr);
	
	CreatureType* ct = newd CreatureType();
	ct->name = name;
	ct->isNpc = isNpc;
	ct->missing = false;
	ct->outfit = outfit;

	creature_map.insert(std::make_pair(as_lower_str(name), ct));
	return ct;
}
예제 #6
0
파일: creatures.cpp 프로젝트: LaloHao/rme
CreatureType* CreatureDatabase::addMissingCreatureType(const std::string& name, bool isNpc)
{
	assert((*this)[name] == nullptr);
	
	CreatureType* ct = newd CreatureType();
	ct->name = name;
	ct->isNpc = isNpc;
	ct->missing = true;
	ct->outfit.lookType = 130;

	creature_map.insert(std::make_pair(as_lower_str(name), ct));
	return ct;
}
예제 #7
0
void ClientVersion::loadVersionExtensions(pugi::xml_node versionNode)
{
	pugi::xml_attribute attribute;
	if (!(attribute = versionNode.attribute("name"))) {
		// Error has already been displayed earlier, no need to show another error about the same thing
		return;
	}

	ClientVersion* version = get(attribute.as_string());
	if (!version) {
		// Same rationale as above
		return;
	}

	for (pugi::xml_node childNode = versionNode.first_child(); childNode; childNode = childNode.next_sibling()) {
		if (as_lower_str(childNode.name()) != "extensions") {
			continue;
		}

		const std::string& from = childNode.attribute("from").as_string();
		const std::string& to = childNode.attribute("to").as_string();
			
		ClientVersion* fromVersion = get(from);
		ClientVersion* toVersion = get(to);

		if ((from.empty() && to.empty()) || (!fromVersion && !toVersion)) {
			wxLogError(wxT("Unknown client extension data."));
			continue;
		}
			
		if (!fromVersion) {
			fromVersion = client_versions.begin()->second;
		}
		
		if (!toVersion) {
			toVersion = client_versions.rbegin()->second;
		}

		for (const auto& versionEntry : client_versions) {
			ClientVersion* version = versionEntry.second;
			if (version->getID() >= fromVersion->getID() && version->getID() <= toVersion->getID()) {
				version->extension_versions.push_back(version);
			}
		}

		std::sort(version->extension_versions.begin(), version->extension_versions.end(), VersionComparisonPredicate);
	}
}
예제 #8
0
파일: items.cpp 프로젝트: Codex-NG/rme
bool ItemDatabase::loadFromGameXml(const FileName& identifier, wxString& error, wxArrayString& warnings)
{
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_file(identifier.GetFullPath().mb_str());
	if(!result) {
		error = wxT("Could not load items.xml (Syntax error?)");
		return false;
	}

	pugi::xml_node node = doc.child("items");
	if(!node) {
		error = wxT("items.xml, invalid root node.");
		return false;
	}

	for(pugi::xml_node itemNode = node.first_child(); itemNode; itemNode = itemNode.next_sibling()) {
		if(as_lower_str(itemNode.name()) != "item") {
			continue;
		}

		int32_t fromId = pugi::cast<int32_t>(itemNode.attribute("fromid").value());
		int32_t toId = pugi::cast<int32_t>(itemNode.attribute("toid").value());
		if(pugi::xml_attribute attribute = itemNode.attribute("id")) {
			fromId = toId = pugi::cast<int32_t>(attribute.value());
		}

		if(fromId == 0 || toId == 0) {
			error = wxT("Could not read item id from item node.");
			return false;
		}

		for(int32_t id = fromId; id <= toId; ++id) {
			if(!loadItemFromGameXml(itemNode, id)) {
				return false;
			}
		}
	}
	return true;
}
예제 #9
0
파일: materials.cpp 프로젝트: TheSumm/rme
bool Materials::unserializeMaterials(const FileName& filename, pugi::xml_node node, wxString& error, wxArrayString& warnings)
{
	wxString warning;
	pugi::xml_attribute attribute;
	for(pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
		const std::string& childName = as_lower_str(childNode.name());
		if(childName == "include") {
			if(!(attribute = childNode.attribute("file"))) {
				continue;
			}

			FileName includeName;
			includeName.SetPath(filename.GetPath());
			includeName.SetFullName(wxString(attribute.as_string(), wxConvUTF8));

			wxString subError;
			if(!loadMaterials(includeName, subError, warnings)) {
				warnings.push_back("Error while loading file \"" + includeName.GetFullName() + "\": " + subError);
			}
		} else if(childName == "metaitem") {
			g_items.loadMetaItem(childNode);
		} else if(childName == "border") {
			g_brushes.unserializeBorder(childNode, warnings);
			if(warning.size()) {
				warnings.push_back("materials.xml: " + warning);
			}
		} else if(childName == "brush") {
			g_brushes.unserializeBrush(childNode, warnings);
			if(warning.size()) {
				warnings.push_back("materials.xml: " + warning);
			}
		} else if(childName == "tileset") {
			unserializeTileset(childNode, warnings);
		}
	}
	return true;
}
예제 #10
0
void ClientVersion::loadVersions()
{
	// Clean up old stuff
	ClientVersion::unloadVersions();

	// Locate the clients.xml file
	wxFileName file_to_load;

	wxFileName exec_dir_client_xml;
	exec_dir_client_xml.Assign(gui.GetExecDirectory());
	exec_dir_client_xml.SetFullName(wxT("clients.xml"));
	
	wxFileName data_dir_client_xml;
	data_dir_client_xml.Assign(gui.GetDataDirectory());
	data_dir_client_xml.SetFullName(wxT("clients.xml"));

	file_to_load = exec_dir_client_xml;
	if (!file_to_load.FileExists()) {
		file_to_load = data_dir_client_xml;
		if (!file_to_load.FileExists()) {
			file_to_load.Clear();
		}
	}

	if (!file_to_load.FileExists()) {
		wxLogError(wxString() +
			wxT("Could not load clients.xml, editor will NOT be able to load any client data files.\n") +
			wxT("Checked paths:\n") +
			exec_dir_client_xml.GetFullPath() + "\n" +
			data_dir_client_xml.GetFullPath()
		);
		return;
	}

	// Parse the file
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_file(file_to_load.GetFullPath().mb_str());
	if (result) {
		pugi::xml_node node = doc.child("client_config");
		if (!node) {
			wxLogError(wxT("Could not load clients.xml (syntax error), editor will NOT be able to load any client data files."));
			return;
		}

		for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
			const std::string& childName = as_lower_str(childNode.name());
			if (childName == "otbs") {
				for (pugi::xml_node otbNode = childNode.first_child(); otbNode; otbNode = otbNode.next_sibling()) {
					if (as_lower_str(otbNode.name()) == "otb") {
						loadOTBInfo(otbNode);
					}
				}
			} else if (childName == "clients") {
				for (pugi::xml_node versionNode = childNode.first_child(); versionNode; versionNode = versionNode.next_sibling()) {
					if (as_lower_str(versionNode.name()) == "client") {
						loadVersionExtensions(versionNode);
					}
				}
			}
		}

		for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
			if (as_lower_str(childNode.name()) != "clients") {
				continue;
			}

			for (pugi::xml_node versionNode = childNode.first_child(); versionNode; versionNode = versionNode.next_sibling()) {
				if (as_lower_str(versionNode.name()) == "client") {
					loadVersion(versionNode);
				}
			}
		}
	}

	// Assign a default if there isn't one.
	if (!latest_version && !client_versions.empty()) {
		latest_version = client_versions.begin()->second;
	}

	// Load the data directory info
	try
	{
		json::mValue read_obj;
		json::read_or_throw(settings.getString(Config::TIBIA_DATA_DIRS), read_obj);
		json::mArray& vers_obj = read_obj.get_array();
		for(json::mArray::iterator ver_iter = vers_obj.begin(); ver_iter != vers_obj.end(); ++ver_iter)
		{
			json::mObject& ver_obj = ver_iter->get_obj();
			ClientVersion* version = get(ver_obj["id"].get_str());
			if (version == nullptr)
				continue;
			version->setClientPath(wxstr(ver_obj["path"].get_str()));
		}
	}
	catch (std::runtime_error&)
	{
		// pass
		;
	}
}
예제 #11
0
void ClientVersion::loadVersion(pugi::xml_node versionNode)
{
	pugi::xml_attribute attribute;
	if (!(attribute = versionNode.attribute("name"))) {
		wxLogError(wxT("Node 'client' must contain 'name', 'data_directory' and 'otb' tags."));
		return;
	}

	const std::string& versionName = attribute.as_string();
	if (!(attribute = versionNode.attribute("data_directory"))) {
		wxLogError(wxT("Node 'client' must contain 'name', 'data_directory' and 'otb' tags."));
		return;
	}

	const std::string& dataPath = attribute.as_string();
	if (!(attribute = versionNode.attribute("otb"))) {
		wxLogError(wxT("Node 'client' must contain 'name', 'data_directory' and 'otb' tags."));
		return;
	}

	const std::string& otbVersionName = attribute.as_string();
	if (otb_versions.find(otbVersionName) == otb_versions.end()) {
		wxLogError(wxT("Node 'client' 'otb' tag is invalid (couldn't find this otb version)."));
		return;
	}

	ClientVersion* version = newd ClientVersion(otb_versions[otbVersionName], versionName, wxstr(dataPath));

	bool should_be_default = versionNode.attribute("default").as_bool();
	version->visible = versionNode.attribute("visible").as_bool();

	for (pugi::xml_node childNode = versionNode.first_child(); childNode; childNode = childNode.next_sibling()) {
		const std::string& childName = as_lower_str(childNode.name());
		if (childName == "otbm") {
			if (!(attribute = childNode.attribute("version"))) {
				wxLogError(wxT("Node 'otbm' missing version."));
				continue;
			}

			int32_t otbmVersion = pugi::cast<int32_t>(attribute.value()) - 1;
			if (otbmVersion < MAP_OTBM_1 || otbmVersion > MAP_OTBM_4) {
				wxLogError(wxT("Node 'otbm' unsupported version."));
				continue;
			}

			if (childNode.attribute("preffered").as_bool() || version->preferred_map_version == MAP_OTBM_UNKNOWN) {
				version->preferred_map_version = static_cast<MapVersionID>(otbmVersion);
			}
			version->map_versions_supported.push_back(version->preferred_map_version);
		} else if (childName == "fucked_up_charges") {
			version->usesFuckedUpCharges = true;
		} else if (childName == "data") {
			if (!(attribute = childNode.attribute("sprversion"))) {
				wxLogError(wxT("Node 'data' does not have 'datversion' / 'sprversion' tags."));
				continue;
			}

			const std::string& sprVersion = attribute.as_string();
			if (!(attribute = childNode.attribute("datversion"))) {
				wxLogError(wxT("Node 'data' does not have 'datversion' / 'sprversion' tags."));
				continue;
			}

			const std::string& datVersion = attribute.as_string();
			ClientData client_data = {DAT_VERSION_74, SPR_VERSION_70, 0, 0};
			if (datVersion == "7.4") {
				client_data.datVersion = DAT_VERSION_74;
			} else if (datVersion == "7.6") {
				client_data.datVersion = DAT_VERSION_76;
			} else if (datVersion == "7.8") {
				client_data.datVersion = DAT_VERSION_78;
			} else if (datVersion == "8.6") {
				client_data.datVersion = DAT_VERSION_86;
			} else if (datVersion == "9.6") {
				client_data.datVersion = DAT_VERSION_96;
			} else {
				wxLogError(wxT("Node 'data' 'datversion' is invalid (7.4, 7.6, 7.8, 8.6 and 9.6 are supported)"));
				continue;
			}

			if (sprVersion == "7.0") {
				client_data.sprVersion = SPR_VERSION_70;
			} else if (sprVersion == "9.6") {
				client_data.sprVersion = SPR_VERSION_96;
			} else {
				wxLogError(wxT("Node 'data' 'sprversion' is invalid (7.0 and 9.6 are supported)"));
				continue;
			}

			if (!(attribute = childNode.attribute("dat")) || !wxString(attribute.as_string(), wxConvUTF8).ToULong((unsigned long*)&client_data.datSignature, 16)) {
				wxLogError(wxT("Node 'data' 'dat' tag is not hex-formatted."));
				continue;
			}

			if (!(attribute = childNode.attribute("spr")) || !wxString(attribute.as_string(), wxConvUTF8).ToULong((unsigned long*)&client_data.sprSignature, 16)) {
				wxLogError(wxT("Node 'data' 'spr' tag is not hex-formatted."));
				continue;
			}

			version->data_versions.push_back(client_data);
		}
	}

	if (client_versions[version->getID()] != nullptr) {
		wxString error;
		error << wxT("Duplicate version id ") << version->getID() << wxT(", discarding version '") << version->name << wxT("'.");
		wxLogError(error);
		delete version;
		return;
	}

	client_versions[version->getID()] = version;
	if (should_be_default)
		latest_version = version;
}
예제 #12
0
파일: creatures.cpp 프로젝트: LaloHao/rme
bool CreatureDatabase::importXMLFromOT(const FileName& filename, wxString& error, wxArrayString& warnings)
{
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_file(filename.GetFullPath().mb_str());
	if (!result) {
		error = wxT("Couldn't open file \"") + filename.GetFullName() + wxT("\", invalid format?");
		return false;
	}

	pugi::xml_node node;
	if ((node = doc.child("monsters"))) {
		for (pugi::xml_node monsterNode = node.first_child(); monsterNode; monsterNode = monsterNode.next_sibling()) {
			if (as_lower_str(monsterNode.name()) != "monster") {
				continue;
			}

			pugi::xml_attribute attribute;
			if (!(attribute = monsterNode.attribute("file"))) {
				continue;
			}

			FileName monsterFile(filename);
			monsterFile.SetFullName(wxString(attribute.as_string(), wxConvUTF8));

			pugi::xml_document monsterDoc;
			pugi::xml_parse_result monsterResult = monsterDoc.load_file(monsterFile.GetFullPath().mb_str());
			if (!monsterResult) {
				continue;
			}

			CreatureType* creatureType = CreatureType::loadFromOTXML(monsterFile, monsterDoc, warnings);
			if (creatureType) {
				CreatureType* current = (*this)[creatureType->name];
				if (current) {
					*current = *creatureType;
					delete creatureType;
				} else {
					creature_map[as_lower_str(creatureType->name)] = creatureType;

					Tileset* tileSet = nullptr;
					if (creatureType->isNpc) {
						tileSet = materials.tilesets["NPCs"];
					} else {
						tileSet = materials.tilesets["Others"];
					}
					ASSERT(tileSet != nullptr);
					
					Brush* brush = newd CreatureBrush(creatureType);
					brushes.addBrush(brush);

					TilesetCategory* tileSetCategory = tileSet->getCategory(TILESET_CREATURE);
					tileSetCategory->brushlist.push_back(brush);
				}
			}
		}
	} else if ((node = doc.child("monster")) || (node = doc.child("npc"))) {
		CreatureType* creatureType = CreatureType::loadFromOTXML(filename, doc, warnings);
		if (creatureType) {
			CreatureType* current = (*this)[creatureType->name];

			if (current) {
				*current = *creatureType;
				delete creatureType;
			} else {
				creature_map[as_lower_str(creatureType->name)] = creatureType;

				Tileset* tileSet = nullptr;
				if (creatureType->isNpc) {
					tileSet = materials.tilesets["NPCs"];
				} else {
					tileSet = materials.tilesets["Others"];
				}
				ASSERT(tileSet != nullptr);

				Brush* brush = newd CreatureBrush(creatureType);
				brushes.addBrush(brush);

				TilesetCategory* tileSetCategory = tileSet->getCategory(TILESET_CREATURE);
				tileSetCategory->brushlist.push_back(brush);
			}
		}
	} else {
		error = wxT("This is not valid OT npc/monster data file.");
		return false;
	}
	return true;
}
예제 #13
0
파일: creatures.cpp 프로젝트: LaloHao/rme
CreatureType* CreatureType::loadFromOTXML(const FileName& filename, pugi::xml_document& doc, wxArrayString& warnings)
{
	ASSERT(doc != nullptr);

	bool isNpc;
	pugi::xml_node node;
	if ((node = doc.child("monster"))) {
		isNpc = false;
	} else if ((node = doc.child("npc"))) {
		isNpc = true;
	} else {
		warnings.push_back(wxT("This file is not a monster/npc file"));
		return nullptr;
	}

	pugi::xml_attribute attribute;
	if (!(attribute = node.attribute("name"))) {
		warnings.push_back(wxT("Couldn't read name tag of creature node."));
		return nullptr;
	}

	CreatureType* ct = newd CreatureType();
	if (isNpc) {
		ct->name = nstr(filename.GetName());
	} else {
		ct->name = attribute.as_string();
	}
	ct->isNpc = isNpc;

	for (pugi::xml_node optionNode = node.first_child(); optionNode; optionNode = optionNode.next_sibling()) {
		if (as_lower_str(optionNode.name()) != "look") {
			continue;
		}

		if ((attribute = optionNode.attribute("type"))) {
			ct->outfit.lookType = pugi::cast<int32_t>(attribute.value());
		}

        if ((attribute = optionNode.attribute("item")) || (attribute = optionNode.attribute("lookex")) 
                || (attribute = optionNode.attribute("typeex"))) {
			ct->outfit.lookItem = pugi::cast<int32_t>(attribute.value());
		}

		if ((attribute = optionNode.attribute("addon"))) {
			ct->outfit.lookAddon = pugi::cast<int32_t>(attribute.value());
		}

		if ((attribute = optionNode.attribute("head"))) {
			ct->outfit.lookHead = pugi::cast<int32_t>(attribute.value());
		}

		if ((attribute = optionNode.attribute("body"))) {
			ct->outfit.lookBody = pugi::cast<int32_t>(attribute.value());
		}

		if ((attribute = optionNode.attribute("legs"))) {
			ct->outfit.lookLegs = pugi::cast<int32_t>(attribute.value());
		}

		if ((attribute = optionNode.attribute("feet"))) {
			ct->outfit.lookFeet = pugi::cast<int32_t>(attribute.value());
		}
	}
	return ct;
}
예제 #14
0
파일: wall_brush.cpp 프로젝트: TheSumm/rme
bool WallBrush::load(pugi::xml_node node, wxArrayString& warnings)
{
	pugi::xml_attribute attribute;
	if((attribute = node.attribute("lookid"))) {
		look_id = pugi::cast<uint16_t>(attribute.value());
	}

	if((attribute = node.attribute("server_lookid"))) {
		look_id = g_items[pugi::cast<uint16_t>(attribute.value())].clientID;
	}

	for(pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
		const std::string& childName = as_lower_str(childNode.name());
		if(childName == "wall") {
			const std::string& typeString = childNode.attribute("type").as_string();
			if(typeString.empty()) {
				warnings.push_back("Could not read type tag of wall node\n");
				continue;
			}

			uint32_t alignment;
			if(typeString == "vertical") {
				alignment = WALL_VERTICAL;
			} else if(typeString == "horizontal") {
				alignment = WALL_HORIZONTAL;
			} else if(typeString == "corner") {
				alignment = WALL_NORTHWEST_DIAGONAL;
			} else if(typeString == "pole") {
				alignment = WALL_POLE;
			} else if(typeString == "south end") {
				alignment = WALL_SOUTH_END;
			} else if(typeString == "east end") {
				alignment = WALL_EAST_END;
			} else if(typeString == "north end") {
				alignment = WALL_NORTH_END;
			} else if(typeString == "west end") {
				alignment = WALL_WEST_END;
			} else if(typeString == "south T") {
				alignment = WALL_SOUTH_T;
			} else if(typeString == "east T") {
				alignment = WALL_EAST_T;
			} else if(typeString == "west T") {
				alignment = WALL_WEST_T;
			} else if(typeString == "north T") {
				alignment = WALL_NORTH_T;
			} else if(typeString == "northwest diagonal") {
				alignment = WALL_NORTHWEST_DIAGONAL;
			} else if(typeString == "northeast diagonal") {
				alignment = WALL_NORTHEAST_DIAGONAL;
			} else if(typeString == "southwest diagonal") {
				alignment = WALL_SOUTHWEST_DIAGONAL;
			} else if(typeString == "southeast diagonal") {
				alignment = WALL_SOUTHEAST_DIAGONAL;
			} else if(typeString == "intersection") {
				alignment = WALL_INTERSECTION;
			} else if(typeString == "untouchable") {
				alignment = WALL_UNTOUCHABLE;
			} else {
				warnings.push_back("Unknown wall alignment '" + wxstr(typeString) + "'\n");
				continue;
			}

			for(pugi::xml_node subChildNode = childNode.first_child(); subChildNode; subChildNode = subChildNode.next_sibling()) {
				const std::string& subChildName = as_lower_str(subChildNode.name());
				if(subChildName == "item") {
					uint16_t id = pugi::cast<uint16_t>(subChildNode.attribute("id").value());
					if(id == 0) {
						warnings.push_back("Could not read id tag of item node\n");
						break;
					}

					ItemType& it = g_items[id];
					if(it.id == 0) {
						warnings.push_back("There is no itemtype with id " + std::to_string(id));
						return false;
					} else if(it.brush && it.brush != this) {
						warnings.push_back("Itemtype id " + std::to_string(id) + " already has a brush");
						return false;
					}

					it.isWall = true;
					it.brush = this;
					it.border_alignment = ::BorderType(alignment);

					WallType wt;
					wt.id = id;

					wall_items[alignment].total_chance += pugi::cast<int32_t>(subChildNode.attribute("chance").value());
					wt.chance = wall_items[alignment].total_chance;

					wall_items[alignment].items.push_back(wt);
				} else if(subChildName == "door") {
					uint16_t id = pugi::cast<uint16_t>(subChildNode.attribute("id").value());
					if(id == 0) {
						warnings.push_back("Could not read id tag of door node\n");
						break;
					}

					const std::string& type = subChildNode.attribute("type").as_string();
					if(type.empty()) {
						warnings.push_back("Could not read type tag of door node\n");
						continue;
					}

					bool isOpen;
					pugi::xml_attribute openAttribute = subChildNode.attribute("open");
					if(openAttribute) {
						isOpen = openAttribute.as_bool();
					} else {
						isOpen = true;
						if(type != "window" && type != "any window" && type != "hatch window") {
							warnings.push_back("Could not read open tag of door node\n");
							break;
						}
					}

					ItemType& it = g_items[id];
					if(it.id == 0) {
						warnings.push_back("There is no itemtype with id " + std::to_string(id));
						return false;
					} else if(it.brush && it.brush != this) {
						warnings.push_back("Itemtype id " + std::to_string(id) + " already has a brush");
						return false;
					}

					it.isWall = true;
					it.brush = this;
					it.isBrushDoor = true;
					it.wall_hate_me = subChildNode.attribute("hate").as_bool();
					it.isOpen = isOpen;
					it.border_alignment = ::BorderType(alignment);

					DoorType dt;
					bool all_windows = false;
					bool all_doors = false;
					if(type == "normal") {
						dt.type = WALL_DOOR_NORMAL;
					} else if(type == "locked") {
						dt.type = WALL_DOOR_LOCKED;
					} else if(type == "quest") {
						dt.type = WALL_DOOR_QUEST;
					} else if(type == "magic") {
						dt.type = WALL_DOOR_MAGIC;
					} else if(type == "archway") {
						dt.type = WALL_ARCHWAY;
					} else if(type == "window") {
						dt.type = WALL_WINDOW;
					} else if(type == "hatch_window" || type == "hatch window") {
						dt.type = WALL_HATCH_WINDOW;
					} else if(type == "any door") {
						all_doors = true;
					} else if(type == "any window") {
						all_windows = true;
					} else if(type == "any") {
						all_windows = true;
						all_doors = true;
					} else {
						warnings.push_back("Unknown door type '" + wxstr(type) + "'\n");
						break;
					}

					dt.id = id;
					if(all_windows) {
						dt.type = WALL_WINDOW;       door_items[alignment].push_back(dt);
						dt.type = WALL_HATCH_WINDOW; door_items[alignment].push_back(dt);
					}

					if(all_doors) {
						dt.type = WALL_ARCHWAY;     door_items[alignment].push_back(dt);
						dt.type = WALL_DOOR_NORMAL; door_items[alignment].push_back(dt);
						dt.type = WALL_DOOR_LOCKED; door_items[alignment].push_back(dt);
						dt.type = WALL_DOOR_QUEST;  door_items[alignment].push_back(dt);
						dt.type = WALL_DOOR_MAGIC;  door_items[alignment].push_back(dt);
					}

					if(!all_doors && !all_windows) {
						door_items[alignment].push_back(dt);
					}
				}
			}
		} else if(childName == "friend") {
			const std::string& name = childNode.attribute("name").as_string();
			if(name.empty()) {
				continue;
			}

			if(name == "all") {
				//friends.push_back(-1);
			} else {
				Brush* brush = g_brushes.getBrush(name);
				if(brush) {
					friends.push_back(brush->getID());
				} else {
					warnings.push_back("Brush '" + wxstr(name) + "' is not defined.");
				}

				if(childNode.attribute("redirect").as_bool()) {
					if (!brush->isWall()) {
						warnings.push_back("Wall brush redirect link: '" + wxstr(name) + "' is not a wall brush.");
					} else if(!redirect_to) {
						redirect_to = brush->asWall();
					} else {
						warnings.push_back( "Wall brush '" + wxstr(getName()) + "' has more than one redirect link.");
					}
				}
			}
		}
	}
	return true;
}