tibia-rme/source/doodad_brush.cpp
Eduardo Dantas 2057c5203b
improve: improve: replacing boosts with std functions (#415)
Removed boosts (replaced with equivalent on std 14/17/20)

With this modification, there are only 16 libs for the project to add, simplifying a lot.

So two additional libraries will be needed to compile: asio,
nlohmann-json

I also removed OTGZ support by default and disabled it, making it optional (needs to be enabled). Anyone who wants to use it will need to activate this, I think it's ideal since few should use it, so a majority don't use it, which is fairer that those who need to activate it to use it than those who don't need to have this in their program.
2023-03-25 21:42:38 -03:00

425 lines
12 KiB
C++

//////////////////////////////////////////////////////////////////////
// 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 "doodad_brush.h"
#include "basemap.h"
//=============================================================================
// Doodad brush
DoodadBrush::DoodadBrush() :
look_id(0),
thickness(0),
thickness_ceiling(0),
draggable(false),
on_blocking(false),
one_size(false),
do_new_borders(false),
on_duplicate(false),
clear_mapflags(0),
clear_statflags(0)
{
////
}
DoodadBrush::~DoodadBrush()
{
for(std::vector<AlternativeBlock*>::iterator alt_iter = alternatives.begin(); alt_iter != alternatives.end(); ++alt_iter) {
delete *alt_iter;
}
}
DoodadBrush::AlternativeBlock::AlternativeBlock() :
composite_chance(0),
single_chance(0)
{
////
}
DoodadBrush::AlternativeBlock::~AlternativeBlock()
{
for(std::vector<CompositeBlock>::iterator composite_iter = composite_items.begin(); composite_iter != composite_items.end(); ++composite_iter) {
CompositeTileList& tv = composite_iter->items;
for(CompositeTileList::iterator compt_iter = tv.begin(); compt_iter != tv.end(); ++compt_iter) {
ItemVector& items = compt_iter->second;
for(ItemVector::iterator iiter = items.begin(); iiter != items.end(); ++iiter)
delete *iiter;
}
}
for(std::vector<SingleBlock>::iterator single_iter = single_items.begin(); single_iter != single_items.end(); ++single_iter) {
delete single_iter->item;
}
}
bool DoodadBrush::loadAlternative(pugi::xml_node node, wxArrayString& warnings, AlternativeBlock* which)
{
AlternativeBlock* alternativeBlock;
if(which) {
alternativeBlock = which;
} else {
alternativeBlock = newd AlternativeBlock();
}
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 == "item") {
if(!(attribute = childNode.attribute("chance"))) {
warnings.push_back("Can't read chance tag of doodad item node.");
continue;
}
Item* item = Item::Create(childNode);
if(!item) {
warnings.push_back("Can't create item from doodad item node.");
continue;
}
ItemType* type = g_items.getRawItemType(item->getID());
if(!type) {
type->doodad_brush = this;
}
SingleBlock sb{ attribute.as_int(), item };
alternativeBlock->single_items.push_back(sb);
alternativeBlock->single_chance += sb.chance;
} else if(childName == "composite") {
if(!(attribute = childNode.attribute("chance"))) {
warnings.push_back("Can't read chance tag of doodad item node.");
continue;
}
alternativeBlock->composite_chance += attribute.as_int();
CompositeBlock cb;
cb.chance = alternativeBlock->composite_chance;
for(pugi::xml_node compositeNode = childNode.first_child(); compositeNode; compositeNode = compositeNode.next_sibling()) {
if(as_lower_str(compositeNode.name()) != "tile") {
continue;
}
if(!(attribute = compositeNode.attribute("x"))) {
warnings.push_back("Couldn't read positionX values of composite tile node.");
continue;
}
int32_t x = attribute.as_int();
if(!(attribute = compositeNode.attribute("y"))) {
warnings.push_back("Couldn't read positionY values of composite tile node.");
continue;
}
int32_t y = attribute.as_int();
int32_t z = compositeNode.attribute("z").as_int();
if(x < -0x7FFF || x > 0x7FFF) {
warnings.push_back("Invalid range of x value on composite tile node.");
continue;
} else if(y < -0x7FFF || y > 0x7FFF) {
warnings.push_back("Invalid range of y value on composite tile node.");
continue;
} else if(z < -0x7 || z > 0x7) {
warnings.push_back("Invalid range of z value on composite tile node.");
continue;
}
ItemVector items;
for(pugi::xml_node itemNode = compositeNode.first_child(); itemNode; itemNode = itemNode.next_sibling()) {
if(as_lower_str(itemNode.name()) != "item") {
continue;
}
Item* item = Item::Create(itemNode);
if(item) {
items.push_back(item);
ItemType* type = g_items.getRawItemType(item->getID());
if(!type) {
type->doodad_brush = this;
}
}
}
if(!items.empty()) {
cb.items.push_back(std::make_pair(Position(x, y, z), items));
}
}
alternativeBlock->composite_items.push_back(cb);
}
}
if(!which) {
alternatives.push_back(alternativeBlock);
}
return true;
}
bool DoodadBrush::load(pugi::xml_node node, wxArrayString& warnings)
{
pugi::xml_attribute attribute;
if((attribute = node.attribute("lookid"))) {
look_id = attribute.as_ushort();
}
if((attribute = node.attribute("server_lookid"))) {
look_id = g_items.getItemType(attribute.as_ushort()).clientID;
}
if((attribute = node.attribute("on_blocking"))) {
on_blocking = attribute.as_bool();
}
if((attribute = node.attribute("on_duplicate"))) {
on_duplicate = attribute.as_bool();
}
if((attribute = node.attribute("redo_borders")) || (attribute = node.attribute("reborder"))) {
do_new_borders = attribute.as_bool();
}
if((attribute = node.attribute("one_size"))) {
one_size = attribute.as_bool();
}
if((attribute = node.attribute("draggable"))) {
draggable = attribute.as_bool();
}
if(node.attribute("remove_optional_border").as_bool()) {
if(!do_new_borders) {
warnings.push_back("remove_optional_border will not work without redo_borders\n");
}
clear_statflags |= TILESTATE_OP_BORDER;
}
const std::string& thicknessString = node.attribute("thickness").as_string();
if(!thicknessString.empty()) {
size_t slash = thicknessString.find('/');
if(slash != std::string::npos) {
try {
thickness = std::stoi(thicknessString.substr(0, slash));
} catch (const std::invalid_argument&) {
thickness = 0;
} catch (const std::out_of_range&) {
thickness = 0;
}
try {
thickness_ceiling = std::stoi(thicknessString.substr(slash + 1));
} catch (const std::invalid_argument&) {
thickness_ceiling = 0;
} catch (const std::out_of_range&) {
thickness_ceiling = 0;
}
}
}
for(pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
if(as_lower_str(childNode.name()) != "alternate") {
continue;
}
if(!loadAlternative(childNode, warnings)) {
return false;
}
}
loadAlternative(node, warnings, alternatives.empty() ? nullptr : alternatives.back());
return true;
}
bool DoodadBrush::AlternativeBlock::ownsItem(uint16_t id) const
{
for(std::vector<SingleBlock>::const_iterator single_iter = single_items.begin(); single_iter != single_items.end(); ++single_iter) {
if(single_iter->item->getID() == id) {
return true;
}
}
for(std::vector<CompositeBlock>::const_iterator composite_iter = composite_items.begin(); composite_iter != composite_items.end(); ++composite_iter) {
const CompositeTileList& ctl = composite_iter->items;
for(CompositeTileList::const_iterator comp_iter = ctl.begin(); comp_iter != ctl.end(); ++comp_iter) {
const ItemVector& items = comp_iter->second;
for(ItemVector::const_iterator item_iter = items.begin(), item_end = items.end(); item_iter != item_end; ++item_iter) {
if((*item_iter)->getID() == id) {
return true;
}
}
}
}
return false;
}
bool DoodadBrush::ownsItem(Item* item) const
{
if(item->getDoodadBrush() == this) return true;
uint16_t id = item->getID();
for(std::vector<AlternativeBlock*>::const_iterator alt_iter = alternatives.begin(); alt_iter != alternatives.end(); ++alt_iter) {
if((*alt_iter)->ownsItem(id)) {
return true;
}
}
return false;
}
void DoodadBrush::undraw(BaseMap* map, Tile* tile)
{
// Remove all doodad-related
for(ItemVector::iterator item_iter = tile->items.begin(); item_iter != tile->items.end();) {
Item* item = *item_iter;
if(item->getDoodadBrush() != nullptr) {
if(item->isComplex() && g_settings.getInteger(Config::ERASER_LEAVE_UNIQUE)) {
++item_iter;
} else if(g_settings.getInteger(Config::DOODAD_BRUSH_ERASE_LIKE)) {
// Only delete items of the same doodad brush
if(ownsItem(item)) {
delete item;
item_iter = tile->items.erase(item_iter);
} else {
++item_iter;
}
} else {
delete item;
item_iter = tile->items.erase(item_iter);
}
} else {
++item_iter;
}
}
if(tile->ground && tile->ground->getDoodadBrush() != nullptr) {
if(g_settings.getInteger(Config::DOODAD_BRUSH_ERASE_LIKE)) {
// Only delete items of the same doodad brush
if(ownsItem(tile->ground)) {
delete tile->ground;
tile->ground = nullptr;
}
} else {
delete tile->ground;
tile->ground = nullptr;
}
}
}
void DoodadBrush::draw(BaseMap* map, Tile* tile, void* parameter)
{
int variation = 0;
if(parameter) {
variation = *reinterpret_cast<int*>(parameter);
}
if(alternatives.empty()) return;
variation %= alternatives.size();
const AlternativeBlock* ab_ptr = alternatives[variation];
ASSERT(ab_ptr);
int roll = random(1, ab_ptr->single_chance);
for(std::vector<SingleBlock>::const_iterator block_iter = ab_ptr->single_items.begin(); block_iter != ab_ptr->single_items.end(); ++block_iter) {
const SingleBlock& sb = *block_iter;
if(roll <= sb.chance) {
// Use this!
tile->addItem(sb.item->deepCopy());
break;
}
roll -= sb.chance;
}
if(clear_mapflags || clear_statflags) {
tile->setMapFlags(tile->getMapFlags() & (~clear_mapflags));
tile->setMapFlags(tile->getStatFlags() & (~clear_statflags));
}
}
const CompositeTileList& DoodadBrush::getComposite(int variation) const
{
static CompositeTileList empty;
if(alternatives.empty())
return empty;
variation %= alternatives.size();
const AlternativeBlock* ab_ptr = alternatives[variation];
ASSERT(ab_ptr);
int roll = random(1, ab_ptr->composite_chance);
for(std::vector<CompositeBlock>::const_iterator block_iter = ab_ptr->composite_items.begin(); block_iter != ab_ptr->composite_items.end(); ++block_iter) {
const CompositeBlock& cb = *block_iter;
if(roll <= cb.chance) {
return cb.items;
}
}
return empty;
}
bool DoodadBrush::isEmpty(int variation) const
{
if(hasCompositeObjects(variation))
return false;
if(hasSingleObjects(variation))
return false;
if(thickness <= 0)
return false;
return true;
}
int DoodadBrush::getCompositeChance(int ab) const
{
if(alternatives.empty()) return 0;
ab %= alternatives.size();
const AlternativeBlock* ab_ptr = alternatives[ab];
ASSERT(ab_ptr);
return ab_ptr->composite_chance;
}
int DoodadBrush::getSingleChance(int ab) const
{
if(alternatives.empty()) return 0;
ab %= alternatives.size();
const AlternativeBlock* ab_ptr = alternatives[ab];
ASSERT(ab_ptr);
return ab_ptr->single_chance;
}
int DoodadBrush::getTotalChance(int ab) const
{
if(alternatives.empty()) return 0;
ab %= alternatives.size();
const AlternativeBlock* ab_ptr = alternatives[ab];
ASSERT(ab_ptr);
return ab_ptr->composite_chance + ab_ptr->single_chance;
}
bool DoodadBrush::hasSingleObjects(int ab) const
{
if(alternatives.empty()) return false;
ab %= alternatives.size();
AlternativeBlock* ab_ptr = alternatives[ab];
ASSERT(ab_ptr);
return ab_ptr->single_chance > 0;
}
bool DoodadBrush::hasCompositeObjects(int ab) const
{
if(alternatives.empty()) return false;
ab %= alternatives.size();
AlternativeBlock* ab_ptr = alternatives[ab];
ASSERT(ab_ptr);
return ab_ptr->composite_chance > 0;
}