tibia-rme/source/npcs.cpp

313 lines
8.7 KiB
C++
Raw Permalink Normal View History

//////////////////////////////////////////////////////////////////////
// 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 <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////
#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(""),
2023-10-09 19:12:16 -07:00
brush(nullptr) {
////
}
2023-10-09 19:12:16 -07:00
NpcType::NpcType(const NpcType &npc) :
missing(npc.missing),
in_other_tileset(npc.in_other_tileset),
standard(npc.standard),
name(npc.name),
outfit(npc.outfit),
2023-10-09 19:12:16 -07:00
brush(npc.brush) {
////
}
2023-10-09 19:12:16 -07:00
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;
}
2023-10-09 19:12:16 -07:00
NpcType::~NpcType() {
////
}
2023-10-09 19:12:16 -07:00
NpcType* NpcType::loadFromXML(pugi::xml_node node, wxArrayString &warnings) {
pugi::xml_attribute attribute;
2023-10-09 19:12:16 -07:00
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();
2023-10-09 19:12:16 -07:00
if ((attribute = node.attribute("looktype"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookType = attribute.as_int();
2023-10-09 19:12:16 -07:00
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));
}
}
2023-10-09 19:12:16 -07:00
if ((attribute = node.attribute("lookitem"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookItem = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = node.attribute("lookaddon"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookAddon = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = node.attribute("lookhead"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookHead = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = node.attribute("lookbody"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookBody = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = node.attribute("looklegs"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookLegs = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = node.attribute("lookfeet"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookFeet = attribute.as_int();
}
return npcType;
}
2023-10-09 19:12:16 -07:00
NpcType* NpcType::loadFromOTXML(const FileName &filename, pugi::xml_document &doc, wxArrayString &warnings) {
ASSERT(doc != nullptr);
pugi::xml_node node;
2023-10-09 19:12:16 -07:00
if (!(node = doc.child("npc"))) {
warnings.push_back("This file is not a npc file");
return nullptr;
}
pugi::xml_attribute attribute;
2023-10-09 19:12:16 -07:00
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());
2023-10-09 19:12:16 -07:00
for (pugi::xml_node optionNode = node.first_child(); optionNode; optionNode = optionNode.next_sibling()) {
if (as_lower_str(optionNode.name()) != "look") {
continue;
}
2023-10-09 19:12:16 -07:00
if ((attribute = optionNode.attribute("type"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookType = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = optionNode.attribute("item")) || (attribute = optionNode.attribute("lookex")) || (attribute = optionNode.attribute("typeex"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookItem = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = optionNode.attribute("addon"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookAddon = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = optionNode.attribute("head"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookHead = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = optionNode.attribute("body"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookBody = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = optionNode.attribute("legs"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookLegs = attribute.as_int();
}
2023-10-09 19:12:16 -07:00
if ((attribute = optionNode.attribute("feet"))) {
2022-08-19 02:50:53 -03:00
npcType->outfit.lookFeet = attribute.as_int();
}
}
return npcType;
}
2023-10-09 19:12:16 -07:00
NpcDatabase::NpcDatabase() {
////
}
2023-10-09 19:12:16 -07:00
NpcDatabase::~NpcDatabase() {
clear();
}
2023-10-09 19:12:16 -07:00
void NpcDatabase::clear() {
for (NpcMap::iterator iter = npcMap.begin(); iter != npcMap.end(); ++iter) {
delete iter->second;
}
npcMap.clear();
}
2023-10-09 19:12:16 -07:00
NpcType* NpcDatabase::operator[](const std::string &name) {
NpcMap::iterator iter = npcMap.find(as_lower_str(name));
2023-10-09 19:12:16 -07:00
if (iter != npcMap.end()) {
return iter->second;
}
return nullptr;
}
2023-10-09 19:12:16 -07:00
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;
}
2023-10-09 19:12:16 -07:00
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;
}
2023-10-09 19:12:16 -07:00
bool NpcDatabase::hasMissing() const {
for (NpcMap::const_iterator iter = npcMap.begin(); iter != npcMap.end(); ++iter) {
if (iter->second->missing) {
return true;
}
}
return false;
}
2023-10-09 19:12:16 -07:00
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());
2023-10-09 19:12:16 -07:00
if (!result) {
error = "Couldn't open file \"" + filename.GetFullName() + "\", invalid format?";
return false;
}
pugi::xml_node node = doc.child("npcs");
2023-10-09 19:12:16 -07:00
if (!node) {
error = "Invalid file signature, this file is not a valid npc file.";
return false;
}
2023-10-09 19:12:16 -07:00
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);
2023-10-09 19:12:16 -07:00
if (npcType) {
npcType->standard = standard;
2023-10-09 19:12:16 -07:00
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;
}
2023-10-09 19:12:16 -07:00
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());
2023-10-09 19:12:16 -07:00
if (!result) {
error = "Couldn't open file \"" + filename.GetFullName() + "\", invalid format?";
return false;
}
pugi::xml_node node;
2023-10-13 13:21:32 -07:00
if ((node = doc.child("npc"))) {
NpcType* npcType = NpcType::loadFromOTXML(filename, doc, warnings);
2023-10-09 19:12:16 -07:00
if (npcType) {
NpcType* current = (*this)[npcType->name];
2023-10-09 19:12:16 -07:00
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;
}
2023-10-09 19:12:16 -07:00
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");
2023-10-09 19:12:16 -07:00
for (const auto &npcEntry : npcMap) {
NpcType* npcType = npcEntry.second;
2023-10-09 19:12:16 -07:00
if (!npcType->standard) {
pugi::xml_node npcNode = npcNodes.append_child("npc");
npcNode.append_attribute("name") = npcType->name.c_str();
npcNode.append_attribute("type") = "npc";
2023-10-09 19:12:16 -07:00
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);
}