tibia-rme/source/materials.cpp

356 lines
11 KiB
C++
Raw Permalink Normal View History

//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
2020-07-30 11:42:28 -03:00
// 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 <wx/dir.h>
#include "editor.h"
#include "items.h"
#include "creatures.h"
#include "gui.h"
#include "materials.h"
#include "brush.h"
#include "creature_brush.h"
#include "raw_brush.h"
Materials g_materials;
Materials::Materials()
{
2015-12-06 11:42:37 -03:00
////
}
Materials::~Materials()
{
clear();
}
void Materials::clear()
{
2015-12-06 11:42:37 -03:00
for(TilesetContainer::iterator iter = tilesets.begin(); iter != tilesets.end(); ++iter) {
delete iter->second;
}
2015-12-06 11:42:37 -03:00
for(MaterialsExtensionList::iterator iter = extensions.begin(); iter != extensions.end(); ++iter) {
delete *iter;
}
tilesets.clear();
extensions.clear();
}
const MaterialsExtensionList& Materials::getExtensions()
{
return extensions;
}
MaterialsExtensionList Materials::getExtensionsByVersion(uint16_t version_id)
{
MaterialsExtensionList ret_list;
2015-12-06 11:42:37 -03:00
for(MaterialsExtensionList::iterator iter = extensions.begin(); iter != extensions.end(); ++iter) {
if((*iter)->isForVersion(version_id)) {
ret_list.push_back(*iter);
}
}
return ret_list;
}
bool Materials::loadMaterials(const FileName& identifier, wxString& error, wxArrayString& warnings)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(identifier.GetFullPath().mb_str());
2015-12-06 11:42:37 -03:00
if(!result) {
2016-11-05 15:28:14 +01:00
warnings.push_back("Could not open " + identifier.GetFullName() + " (file not found or syntax error)");
return false;
}
pugi::xml_node node = doc.child("materials");
2015-12-06 11:42:37 -03:00
if(!node) {
2016-11-05 15:28:14 +01:00
warnings.push_back(identifier.GetFullName() + ": Invalid rootheader.");
return false;
}
unserializeMaterials(identifier, node, error, warnings);
return true;
}
bool Materials::loadExtensions(FileName directoryName, wxString& error, wxArrayString& warnings)
{
directoryName.Mkdir(0755, wxPATH_MKDIR_FULL); // Create if it doesn't exist
wxDir ext_dir(directoryName.GetPath());
2015-12-06 11:42:37 -03:00
if(!ext_dir.IsOpened()) {
2016-11-05 15:28:14 +01:00
error = "Could not open extensions directory.";
return false;
}
wxString filename;
2015-12-06 11:42:37 -03:00
if(!ext_dir.GetFirst(&filename)) {
// No extensions found
return true;
}
StringVector clientVersions;
do {
FileName fn;
fn.SetPath(directoryName.GetPath());
fn.SetFullName(filename);
2016-11-05 15:28:14 +01:00
if(fn.GetExt() != "xml") {
continue;
}
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(fn.GetFullPath().mb_str());
2015-12-06 11:42:37 -03:00
if(!result) {
2016-11-05 15:28:14 +01:00
warnings.push_back("Could not open " + filename + " (file not found or syntax error)");
continue;
}
pugi::xml_node extensionNode = doc.child("materialsextension");
2015-12-06 11:42:37 -03:00
if(!extensionNode) {
2016-11-05 15:28:14 +01:00
warnings.push_back(filename + ": Invalid rootheader.");
continue;
}
pugi::xml_attribute attribute;
2015-12-06 11:42:37 -03:00
if(!(attribute = extensionNode.attribute("name"))) {
2016-11-05 15:28:14 +01:00
warnings.push_back(filename + ": Couldn't read extension name.");
continue;
}
const std::string& extensionName = attribute.as_string();
2015-12-06 11:42:37 -03:00
if(!(attribute = extensionNode.attribute("author"))) {
2016-11-05 15:28:14 +01:00
warnings.push_back(filename + ": Couldn't read extension name.");
continue;
}
const std::string& extensionAuthor = attribute.as_string();
2015-12-06 11:42:37 -03:00
if(!(attribute = extensionNode.attribute("description"))) {
2016-11-05 15:28:14 +01:00
warnings.push_back(filename + ": Couldn't read extension name.");
continue;
}
const std::string& extensionDescription = attribute.as_string();
2015-12-06 11:42:37 -03:00
if(extensionName.empty() || extensionAuthor.empty() || extensionDescription.empty()) {
2016-11-05 15:28:14 +01:00
warnings.push_back(filename + ": Couldn't read extension attributes (name, author, description).");
continue;
}
std::string extensionUrl = extensionNode.attribute("url").as_string();
extensionUrl.erase(std::remove(extensionUrl.begin(), extensionUrl.end(), '\''));
std::string extensionAuthorLink = extensionNode.attribute("authorurl").as_string();
extensionAuthorLink.erase(std::remove(extensionAuthorLink.begin(), extensionAuthorLink.end(), '\''));
MaterialsExtension* materialExtension = newd MaterialsExtension(extensionName, extensionAuthor, extensionDescription);
materialExtension->url = extensionUrl;
materialExtension->author_url = extensionAuthorLink;
2015-12-06 11:42:37 -03:00
if((attribute = extensionNode.attribute("client"))) {
clientVersions.clear();
const std::string& extensionClientString = attribute.as_string();
size_t lastPosition = 0;
size_t position = extensionClientString.find(';');
2015-12-06 11:42:37 -03:00
while(position != std::string::npos) {
clientVersions.push_back(extensionClientString.substr(lastPosition, position - lastPosition));
lastPosition = position + 1;
position = extensionClientString.find(';', lastPosition);
}
clientVersions.push_back(extensionClientString.substr(lastPosition));
2015-12-06 11:42:37 -03:00
for(const std::string& version : clientVersions) {
materialExtension->addVersion(version);
}
std::sort(materialExtension->version_list.begin(), materialExtension->version_list.end(), VersionComparisonPredicate);
2014-01-25 20:42:59 +01:00
auto duplicate = std::unique(materialExtension->version_list.begin(), materialExtension->version_list.end());
2015-12-06 11:42:37 -03:00
while(duplicate != materialExtension->version_list.end()) {
2014-01-25 20:42:59 +01:00
materialExtension->version_list.erase(duplicate);
duplicate = std::unique(materialExtension->version_list.begin(), materialExtension->version_list.end());
2014-01-25 20:42:59 +01:00
}
} else {
2016-11-05 15:28:14 +01:00
warnings.push_back(filename + ": Extension is not available for any version.");
}
extensions.push_back(materialExtension);
if(materialExtension->isForVersion(g_gui.GetCurrentVersionID())) {
unserializeMaterials(filename, extensionNode, error, warnings);
}
2015-12-06 11:42:37 -03:00
} while(ext_dir.GetNext(&filename));
return true;
}
bool Materials::unserializeMaterials(const FileName& filename, pugi::xml_node node, wxString& error, wxArrayString& warnings)
{
wxString warning;
pugi::xml_attribute attribute;
2015-12-06 11:42:37 -03:00
for(pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
const std::string& childName = as_lower_str(childNode.name());
2015-12-06 11:42:37 -03:00
if(childName == "include") {
if(!(attribute = childNode.attribute("file"))) {
continue;
}
FileName includeName;
includeName.SetPath(filename.GetPath());
includeName.SetFullName(wxString(attribute.as_string(), wxConvUTF8));
wxString subError;
2015-12-06 11:42:37 -03:00
if(!loadMaterials(includeName, subError, warnings)) {
2016-11-05 15:28:14 +01:00
warnings.push_back("Error while loading file \"" + includeName.GetFullName() + "\": " + subError);
}
2015-12-06 11:42:37 -03:00
} else if(childName == "metaitem") {
g_items.loadMetaItem(childNode);
2015-12-06 11:42:37 -03:00
} else if(childName == "border") {
g_brushes.unserializeBorder(childNode, warnings);
2015-12-06 11:42:37 -03:00
if(warning.size()) {
2016-11-05 15:28:14 +01:00
warnings.push_back("materials.xml: " + warning);
}
2015-12-06 11:42:37 -03:00
} else if(childName == "brush") {
g_brushes.unserializeBrush(childNode, warnings);
2015-12-06 11:42:37 -03:00
if(warning.size()) {
2016-11-05 15:28:14 +01:00
warnings.push_back("materials.xml: " + warning);
}
2015-12-06 11:42:37 -03:00
} else if(childName == "tileset") {
unserializeTileset(childNode, warnings);
}
}
return true;
}
void Materials::createOtherTileset()
{
Tileset* others;
Tileset* npc_tileset;
2015-12-06 11:42:37 -03:00
if(tilesets["Others"] != nullptr) {
others = tilesets["Others"];
others->clear();
} else {
others = newd Tileset(g_brushes, "Others");
tilesets["Others"] = others;
}
2015-12-06 11:42:37 -03:00
if(tilesets["NPCs"] != nullptr) {
npc_tileset = tilesets["NPCs"];
npc_tileset->clear();
} else {
npc_tileset = newd Tileset(g_brushes, "NPCs");
tilesets["NPCs"] = npc_tileset;
}
// There should really be an iterator to do this
for(int32_t id = 0; id <= g_items.getMaxID(); ++id) {
ItemType* type = g_items.getRawItemType(id);
if(!type) {
continue;
}
if(!type->isMetaItem()) {
Brush* brush;
if(type->in_other_tileset) {
others->getCategory(TILESET_RAW)->brushlist.push_back(type->raw_brush);
continue;
} else if(!type->raw_brush) {
brush = type->raw_brush = newd RAWBrush(type->id);
type->has_raw = true;
g_brushes.addBrush(type->raw_brush);
} else if(!type->has_raw) {
brush = type->raw_brush;
} else
continue;
brush->flagAsVisible();
others->getCategory(TILESET_RAW)->brushlist.push_back(type->raw_brush);
type->in_other_tileset = true;
}
}
for(CreatureMap::iterator iter = g_creatures.begin(); iter != g_creatures.end(); ++iter) {
CreatureType* type = iter->second;
2015-12-06 11:42:37 -03:00
if(type->in_other_tileset) {
if(type->isNpc) {
npc_tileset->getCategory(TILESET_CREATURE)->brushlist.push_back(type->brush);
} else {
others->getCategory(TILESET_CREATURE)->brushlist.push_back(type->brush);
}
2015-12-06 11:42:37 -03:00
} else if(type->brush == nullptr) {
type->brush = newd CreatureBrush(type);
g_brushes.addBrush(type->brush);
type->brush->flagAsVisible();
type->in_other_tileset = true;
2015-12-06 11:42:37 -03:00
if(type->isNpc) {
npc_tileset->getCategory(TILESET_CREATURE)->brushlist.push_back(type->brush);
} else {
others->getCategory(TILESET_CREATURE)->brushlist.push_back(type->brush);
}
}
}
}
bool Materials::unserializeTileset(pugi::xml_node node, wxArrayString& warnings)
{
pugi::xml_attribute attribute;
2015-12-06 11:42:37 -03:00
if(!(attribute = node.attribute("name"))) {
2016-11-05 15:28:14 +01:00
warnings.push_back("Couldn't read tileset name");
return false;
}
const std::string& name = attribute.as_string();
Tileset* tileset;
auto it = tilesets.find(name);
2015-12-06 11:42:37 -03:00
if(it != tilesets.end()) {
tileset = it->second;
} else {
tileset = newd Tileset(g_brushes, name);
tilesets.insert(std::make_pair(name, tileset));
}
2015-12-06 11:42:37 -03:00
for(pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
tileset->loadCategory(childNode, warnings);
}
return true;
}
bool Materials::isInTileset(Item* item, std::string tilesetName) const
{
const ItemType& type = g_items.getItemType(item->getID());
return type.id != 0 && (
isInTileset(type.brush, tilesetName) ||
isInTileset(type.doodad_brush, tilesetName) ||
isInTileset(type.raw_brush, tilesetName));
}
bool Materials::isInTileset(Brush* brush, std::string tilesetName) const
{
if(!brush)
return false;
TilesetContainer::const_iterator tilesetiter = tilesets.find(tilesetName);
if(tilesetiter == tilesets.end())
return false;
Tileset* tileset = tilesetiter->second;
return tileset->containsBrush(brush);
}