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