mirror of
https://github.com/opentibiabr/remeres-map-editor
synced 2026-08-15 18:26:04 -04:00
Removed libxml2 to use pugixml. Fixed a bug with the progress bar. Changed some things in most xml files that pugixml did not like. Updated the Visual Studio project. Any NULL symbol has been changed to nullptr. This most probably broke the CMake that was recently added. And probably some other things.
409 lines
12 KiB
C++
409 lines
12 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
// This file is part of Remere's Map Editor
|
|
//////////////////////////////////////////////////////////////////////
|
|
// This program 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.
|
|
//
|
|
// This program 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/>.
|
|
//////////////////////////////////////////////////////////////////////
|
|
// $URL: http://svn.rebarp.se/svn/RME/trunk/source/creatures.hpp $
|
|
// $Id: creatures.hpp 310 2010-02-26 18:03:48Z admin $
|
|
|
|
#include "main.h"
|
|
|
|
#include "gui.h"
|
|
#include "materials.h"
|
|
#include "brush.h"
|
|
#include "creatures.h"
|
|
#include "creature_brush.h"
|
|
#include "pugicast.h"
|
|
|
|
CreatureDatabase creature_db;
|
|
|
|
CreatureType::CreatureType() :
|
|
isNpc(false),
|
|
missing(false),
|
|
in_other_tileset(false),
|
|
standard(false),
|
|
name(""),
|
|
brush(nullptr)
|
|
{
|
|
}
|
|
|
|
CreatureType::CreatureType(const CreatureType& ct) :
|
|
isNpc(ct.isNpc),
|
|
missing(ct.missing),
|
|
in_other_tileset(ct.in_other_tileset),
|
|
standard(ct.standard),
|
|
name(ct.name),
|
|
outfit(ct.outfit),
|
|
brush(ct.brush)
|
|
{
|
|
}
|
|
|
|
CreatureType& CreatureType::operator=(const CreatureType& ct)
|
|
{
|
|
isNpc = ct.isNpc;
|
|
missing = ct.missing;
|
|
in_other_tileset = ct.in_other_tileset;
|
|
standard = ct.standard;
|
|
name = ct.name;
|
|
outfit = ct.outfit;
|
|
brush = ct.brush;
|
|
return *this;
|
|
}
|
|
|
|
CreatureType::~CreatureType()
|
|
{
|
|
}
|
|
|
|
CreatureType* CreatureType::loadFromXML(pugi::xml_node node, wxArrayString& warnings)
|
|
{
|
|
pugi::xml_attribute attribute;
|
|
if (!(attribute = node.attribute("type"))) {
|
|
warnings.push_back(wxT("Couldn't read type tag of creature node."));
|
|
return nullptr;
|
|
}
|
|
|
|
const std::string& tmpType = attribute.as_string();
|
|
if (tmpType != "monster" && tmpType != "npc") {
|
|
warnings.push_back(wxT("Invalid type tag of creature node \"") + wxstr(tmpType) + wxT("\""));
|
|
return nullptr;
|
|
}
|
|
|
|
if (!(attribute = node.attribute("name"))) {
|
|
warnings.push_back(wxT("Couldn't read name tag of creature node."));
|
|
return nullptr;
|
|
}
|
|
|
|
CreatureType* ct = newd CreatureType();
|
|
ct->name = attribute.as_string();
|
|
ct->isNpc = tmpType == "npc";
|
|
|
|
if ((attribute = node.attribute("looktype"))) {
|
|
ct->outfit.lookType = pugi::cast<int32_t>(attribute.value());
|
|
if(gui.gfx.getCreatureSprite(ct->outfit.lookType) == nullptr) {
|
|
warnings.push_back(wxT("Invalid creature \"") + wxstr(ct->name) + wxT("\" look type #") + std::to_string(ct->outfit.lookType));
|
|
}
|
|
}
|
|
|
|
if ((attribute = node.attribute("lookitem"))) {
|
|
ct->outfit.lookItem = pugi::cast<int32_t>(attribute.value());
|
|
}
|
|
|
|
if ((attribute = node.attribute("lookaddon"))) {
|
|
ct->outfit.lookAddon = pugi::cast<int32_t>(attribute.value());
|
|
}
|
|
|
|
if ((attribute = node.attribute("lookhead"))) {
|
|
ct->outfit.lookHead = pugi::cast<int32_t>(attribute.value());
|
|
}
|
|
|
|
if ((attribute = node.attribute("lookbody"))) {
|
|
ct->outfit.lookBody = pugi::cast<int32_t>(attribute.value());
|
|
}
|
|
|
|
if ((attribute = node.attribute("looklegs"))) {
|
|
ct->outfit.lookLegs = pugi::cast<int32_t>(attribute.value());
|
|
}
|
|
|
|
if ((attribute = node.attribute("lookfeet"))) {
|
|
ct->outfit.lookFeet = pugi::cast<int32_t>(attribute.value());
|
|
}
|
|
return ct;
|
|
}
|
|
|
|
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"))) {
|
|
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;
|
|
}
|
|
|
|
CreatureDatabase::CreatureDatabase()
|
|
{
|
|
}
|
|
|
|
CreatureDatabase::~CreatureDatabase()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void CreatureDatabase::clear()
|
|
{
|
|
for(CreatureMap::iterator iter = creature_map.begin();
|
|
iter != creature_map.end();
|
|
++iter)
|
|
{
|
|
delete iter->second;
|
|
}
|
|
creature_map.clear();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool CreatureDatabase::hasMissing() const
|
|
{
|
|
for(CreatureMap::const_iterator iter = creature_map.begin();
|
|
iter != creature_map.end();
|
|
++iter)
|
|
{
|
|
if(iter->second->missing)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 (!result) {
|
|
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(ts != 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(ts != 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;
|
|
}
|
|
|
|
bool CreatureDatabase::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 creatureNodes = doc.append_child("creatures");
|
|
for (const auto& creatureEntry : creature_map) {
|
|
CreatureType* creatureType = creatureEntry.second;
|
|
if (!creatureType->standard) {
|
|
pugi::xml_node creatureNode = creatureNodes.append_child("creature");
|
|
|
|
creatureNode.append_attribute("name") = creatureType->name.c_str();
|
|
creatureNode.append_attribute("type") = creatureType->isNpc ? "npc" : "monster";
|
|
|
|
const Outfit& outfit = creatureType->outfit;
|
|
creatureNode.append_attribute("looktype") = outfit.lookType;
|
|
creatureNode.append_attribute("lookitem") = outfit.lookItem;
|
|
creatureNode.append_attribute("lookaddon") = outfit.lookAddon;
|
|
creatureNode.append_attribute("lookhead") = outfit.lookHead;
|
|
creatureNode.append_attribute("lookbody") = outfit.lookBody;
|
|
creatureNode.append_attribute("looklegs") = outfit.lookLegs;
|
|
creatureNode.append_attribute("lookfeet") = outfit.lookFeet;
|
|
}
|
|
}
|
|
return doc.save_file(filename.GetFullPath().mb_str(), "\t", pugi::format_default, pugi::encoding_utf8);
|
|
}
|