////////////////////////////////////////////////////////////////////// // This file is part of Remere's Map Editor ////////////////////////////////////////////////////////////////////// // Remere's Map Editor is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Remere's Map Editor is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ////////////////////////////////////////////////////////////////////// #include "main.h" #include "gui.h" #include "materials.h" #include "brush.h" #include "npcs.h" #include "npc_brush.h" NpcDatabase g_npcs; NpcType::NpcType() : missing(false), in_other_tileset(false), standard(false), name(""), brush(nullptr) { //// } NpcType::NpcType(const NpcType& npc) : missing(npc.missing), in_other_tileset(npc.in_other_tileset), standard(npc.standard), name(npc.name), outfit(npc.outfit), brush(npc.brush) { //// } NpcType& NpcType::operator=(const NpcType& npc) { missing = npc.missing; in_other_tileset = npc.in_other_tileset; standard = npc.standard; name = npc.name; outfit = npc.outfit; brush = npc.brush; return *this; } NpcType::~NpcType() { //// } NpcType* NpcType::loadFromXML(pugi::xml_node node, wxArrayString& warnings) { pugi::xml_attribute attribute; if(!(attribute = node.attribute("name"))) { warnings.push_back("Couldn't read name tag of npc node."); return nullptr; } NpcType* npcType = newd NpcType(); npcType->name = attribute.as_string(); if((attribute = node.attribute("looktype"))) { npcType->outfit.lookType = attribute.as_int(); if(g_gui.gfx.getCreatureSprite(npcType->outfit.lookType) == nullptr) { warnings.push_back("Invalid npc \"" + wxstr(npcType->name) + "\" look type #" + std::to_string(npcType->outfit.lookType)); } } if((attribute = node.attribute("lookitem"))) { npcType->outfit.lookItem = attribute.as_int(); } if((attribute = node.attribute("lookaddon"))) { npcType->outfit.lookAddon = attribute.as_int(); } if((attribute = node.attribute("lookhead"))) { npcType->outfit.lookHead = attribute.as_int(); } if((attribute = node.attribute("lookbody"))) { npcType->outfit.lookBody = attribute.as_int(); } if((attribute = node.attribute("looklegs"))) { npcType->outfit.lookLegs = attribute.as_int(); } if((attribute = node.attribute("lookfeet"))) { npcType->outfit.lookFeet = attribute.as_int(); } return npcType; } NpcType* NpcType::loadFromOTXML(const FileName& filename, pugi::xml_document& doc, wxArrayString& warnings) { ASSERT(doc != nullptr); pugi::xml_node node; if(!(node = doc.child("npc"))) { warnings.push_back("This file is not a npc file"); return nullptr; } pugi::xml_attribute attribute; if(!(attribute = node.attribute("name"))) { warnings.push_back("Couldn't read name tag of npc node."); return nullptr; } NpcType* npcType = newd NpcType(); npcType->name = nstr(filename.GetName()); npcType->name = nstr(filename.GetName()); 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"))) { npcType->outfit.lookType = attribute.as_int(); } if((attribute = optionNode.attribute("item")) || (attribute = optionNode.attribute("lookex")) || (attribute = optionNode.attribute("typeex"))) { npcType->outfit.lookItem = attribute.as_int(); } if((attribute = optionNode.attribute("addon"))) { npcType->outfit.lookAddon = attribute.as_int(); } if((attribute = optionNode.attribute("head"))) { npcType->outfit.lookHead = attribute.as_int(); } if((attribute = optionNode.attribute("body"))) { npcType->outfit.lookBody = attribute.as_int(); } if((attribute = optionNode.attribute("legs"))) { npcType->outfit.lookLegs = attribute.as_int(); } if((attribute = optionNode.attribute("feet"))) { npcType->outfit.lookFeet = attribute.as_int(); } } return npcType; } NpcDatabase::NpcDatabase() { //// } NpcDatabase::~NpcDatabase() { clear(); } void NpcDatabase::clear() { for(NpcMap::iterator iter = npcMap.begin(); iter != npcMap.end(); ++iter) { delete iter->second; } npcMap.clear(); } NpcType* NpcDatabase::operator[](const std::string& name) { NpcMap::iterator iter = npcMap.find(as_lower_str(name)); if(iter != npcMap.end()) { return iter->second; } return nullptr; } NpcType* NpcDatabase::addMissingNpcType(const std::string& name) { assert((*this)[name] == nullptr); NpcType* npcType = newd NpcType(); npcType->name = name; npcType->missing = true; npcType->outfit.lookType = 130; npcMap.insert(std::make_pair(as_lower_str(name), npcType)); return npcType; } NpcType* NpcDatabase::addNpcType(const std::string& name, const Outfit& outfit) { assert((*this)[name] == nullptr); NpcType* npcType = newd NpcType(); npcType->name = name; npcType->missing = false; npcType->outfit = outfit; npcMap.insert(std::make_pair(as_lower_str(name), npcType)); return npcType; } bool NpcDatabase::hasMissing() const { for(NpcMap::const_iterator iter = npcMap.begin(); iter != npcMap.end(); ++iter) { if(iter->second->missing) { return true; } } return false; } bool NpcDatabase::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 = "Couldn't open file \"" + filename.GetFullName() + "\", invalid format?"; return false; } pugi::xml_node node = doc.child("npcs"); if(!node) { error = "Invalid file signature, this file is not a valid npc file."; return false; } for(pugi::xml_node npcNode = node.first_child(); npcNode; npcNode = npcNode.next_sibling()) { if(as_lower_str(npcNode.name()) != "npc") { continue; } NpcType* npcType = NpcType::loadFromXML(npcNode, warnings); if(npcType) { npcType->standard = standard; if((*this)[npcType->name]) { warnings.push_back("Duplicate npc with name \"" + wxstr(npcType->name) + "\"! Discarding..."); delete npcType; } else { npcMap[as_lower_str(npcType->name)] = npcType; } } } return true; } bool NpcDatabase::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 = "Couldn't open file \"" + filename.GetFullName() + "\", invalid format?"; return false; } pugi::xml_node node; if(node = doc.child("npc")) { NpcType* npcType = NpcType::loadFromOTXML(filename, doc, warnings); if(npcType) { NpcType* current = (*this)[npcType->name]; if(current) { *current = *npcType; delete npcType; } else { npcMap[as_lower_str(npcType->name)] = npcType; Tileset* tileSet = nullptr; tileSet = g_materials.tilesets["NPCs"]; ASSERT(tileSet != nullptr); Brush* brush = newd NpcBrush(npcType); g_brushes.addBrush(brush); TilesetCategory* tileSetCategory = tileSet->getCategory(TILESET_NPC); tileSetCategory->brushlist.push_back(brush); } } } else { error = "This is not valid OT npc data file."; return false; } return true; } bool NpcDatabase::saveToXML(const FileName& filename) { pugi::xml_document doc; pugi::xml_node decl = doc.prepend_child(pugi::node_declaration); decl.append_attribute("version") = "1.0"; pugi::xml_node npcNodes = doc.append_child("npcs"); for(const auto& npcEntry : npcMap) { NpcType* npcType = npcEntry.second; if(!npcType->standard) { pugi::xml_node npcNode = npcNodes.append_child("npc"); npcNode.append_attribute("name") = npcType->name.c_str(); npcNode.append_attribute("type") = "npc"; const Outfit& outfit = npcType->outfit; npcNode.append_attribute("looktype") = outfit.lookType; npcNode.append_attribute("lookitem") = outfit.lookItem; npcNode.append_attribute("lookaddon") = outfit.lookAddon; npcNode.append_attribute("lookhead") = outfit.lookHead; npcNode.append_attribute("lookbody") = outfit.lookBody; npcNode.append_attribute("looklegs") = outfit.lookLegs; npcNode.append_attribute("lookfeet") = outfit.lookFeet; } } return doc.save_file(filename.GetFullPath().mb_str(), "\t", pugi::format_default, pugi::encoding_utf8); }