tibia-rme/source/brush.cpp

655 lines
17 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.
//
2020-07-30 11:42:28 -03:00
// Remere's Map Editor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2020-07-30 11:42:28 -03:00
// 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
2020-07-30 11:42:28 -03:00
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////
#include "main.h"
#include "brush.h"
#include "carpet_brush.h"
#include "creature_brush.h"
#include "doodad_brush.h"
#include "ground_brush.h"
#include "house_brush.h"
#include "house_exit_brush.h"
#include "raw_brush.h"
#include "spawn_brush.h"
#include "table_brush.h"
#include "wall_brush.h"
#include "waypoint_brush.h"
#include "settings.h"
#include "sprites.h"
#include "item.h"
#include "complexitem.h"
#include "creatures.h"
#include "creature.h"
#include "map.h"
#include "gui.h"
Brushes g_brushes;
2014-03-01 14:44:11 +01:00
Brushes::Brushes()
{
2015-12-06 11:42:37 -03:00
////
}
2014-03-01 14:44:11 +01:00
Brushes::~Brushes()
{
2015-12-06 11:42:37 -03:00
////
}
2014-03-01 14:44:11 +01:00
void Brushes::clear()
{
2015-12-06 11:42:37 -03:00
for(auto brushEntry : brushes) {
2014-03-01 14:44:11 +01:00
delete brushEntry.second;
}
brushes.clear();
2014-03-01 14:44:11 +01:00
2015-12-06 11:42:37 -03:00
for(auto borderEntry : borders) {
2014-03-01 14:44:11 +01:00
delete borderEntry.second;
}
borders.clear();
}
2014-03-01 14:44:11 +01:00
void Brushes::init()
{
addBrush(g_gui.optional_brush = newd OptionalBorderBrush());
addBrush(g_gui.eraser = newd EraserBrush());
addBrush(g_gui.spawn_brush = newd SpawnBrush());
addBrush(g_gui.normal_door_brush = newd DoorBrush(WALL_DOOR_NORMAL));
addBrush(g_gui.locked_door_brush = newd DoorBrush(WALL_DOOR_LOCKED));
addBrush(g_gui.magic_door_brush = newd DoorBrush(WALL_DOOR_MAGIC));
addBrush(g_gui.quest_door_brush = newd DoorBrush(WALL_DOOR_QUEST));
addBrush(g_gui.hatch_door_brush = newd DoorBrush(WALL_HATCH_WINDOW));
addBrush(g_gui.window_door_brush = newd DoorBrush(WALL_WINDOW));
addBrush(g_gui.house_brush = newd HouseBrush());
addBrush(g_gui.house_exit_brush = newd HouseExitBrush());
addBrush(g_gui.waypoint_brush = newd WaypointBrush());
addBrush(g_gui.pz_brush = newd FlagBrush(TILESTATE_PROTECTIONZONE));
addBrush(g_gui.rook_brush = newd FlagBrush(TILESTATE_NOPVP));
addBrush(g_gui.nolog_brush = newd FlagBrush(TILESTATE_NOLOGOUT));
addBrush(g_gui.pvp_brush = newd FlagBrush(TILESTATE_PVPZONE));
GroundBrush::init();
WallBrush::init();
TableBrush::init();
CarpetBrush::init();
}
bool Brushes::unserializeBrush(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("Brush node without name.");
return false;
}
const std::string& brushName = attribute.as_string();
2015-12-06 11:42:37 -03:00
if(brushName == "all" || brushName == "none") {
2016-11-05 15:28:14 +01:00
warnings.push_back(wxString("Using reserved brushname \"") << wxstr(brushName) << "\".");
return false;
}
Brush* brush = getBrush(brushName);
2015-12-06 11:42:37 -03:00
if(!brush) {
if(!(attribute = node.attribute("type"))) {
2016-11-05 15:28:14 +01:00
warnings.push_back("Couldn't read brush type");
return false;
}
const std::string brushType = attribute.as_string();
2015-12-06 11:42:37 -03:00
if(brushType == "border" || brushType == "ground") {
brush = newd GroundBrush();
2015-12-06 11:42:37 -03:00
} else if(brushType == "wall") {
brush = newd WallBrush();
2015-12-06 11:42:37 -03:00
} else if(brushType == "wall decoration") {
brush = newd WallDecorationBrush();
2015-12-06 11:42:37 -03:00
} else if(brushType == "carpet") {
brush = newd CarpetBrush();
2015-12-06 11:42:37 -03:00
} else if(brushType == "table") {
brush = newd TableBrush();
2015-12-06 11:42:37 -03:00
} else if(brushType == "doodad") {
brush = newd DoodadBrush();
} else {
2016-11-05 15:28:14 +01:00
warnings.push_back(wxString("Unknown brush type ") << wxstr(brushType));
return false;
}
ASSERT(brush);
brush->setName(brushName);
}
2015-12-06 11:42:37 -03:00
if(!node.first_child()) {
brushes.insert(std::make_pair(brush->getName(), brush));
return true;
}
wxArrayString subWarnings;
brush->load(node, subWarnings);
if(!subWarnings.empty()) {
2016-11-05 15:28:14 +01:00
warnings.push_back(wxString("Errors while loading brush \"") << wxstr(brush->getName()) << "\"");
warnings.insert(warnings.end(), subWarnings.begin(), subWarnings.end());
}
if(brush->getName() == "all" || brush->getName() == "none") {
2016-11-05 15:28:14 +01:00
warnings.push_back(wxString("Using reserved brushname '") << wxstr(brush->getName()) << "'.");
delete brush;
return false;
}
Brush* otherBrush = getBrush(brush->getName());
if(otherBrush) {
if(otherBrush != brush) {
2016-11-05 15:28:14 +01:00
warnings.push_back(wxString("Duplicate brush name ") << wxstr(brush->getName()) << ". Undefined behaviour may ensue.");
} else {
// Don't insert
return true;
}
}
brushes.insert(std::make_pair(brush->getName(), brush));
return true;
}
bool Brushes::unserializeBorder(pugi::xml_node node, wxArrayString& warnings)
{
pugi::xml_attribute attribute = node.attribute("id");
2015-12-06 11:42:37 -03:00
if(!attribute) {
2016-11-05 15:28:14 +01:00
warnings.push_back("Couldn't read border id node");
return false;
}
uint32_t id = attribute.as_uint();
2015-12-06 11:42:37 -03:00
if(borders[id]) {
2016-11-05 15:28:14 +01:00
warnings.push_back("Border ID " + std::to_string(id) + " already exists");
return false;
}
AutoBorder* border = newd AutoBorder(id);
border->load(node, warnings);
borders[id] = border;
return true;
}
2015-12-06 11:42:37 -03:00
void Brushes::addBrush(Brush *brush)
{
brushes.insert(std::make_pair(brush->getName(), brush));
}
2014-03-01 14:44:11 +01:00
Brush* Brushes::getBrush(const std::string& name) const
{
auto it = brushes.find(name);
2015-12-06 11:42:37 -03:00
if(it != brushes.end()) {
return it->second;
2014-03-01 14:44:11 +01:00
}
return nullptr;
}
// Brush
uint32_t Brush::id_counter = 0;
Brush::Brush() :
id(++id_counter), visible(false)
{
2015-12-06 11:42:37 -03:00
////
}
Brush::~Brush()
{
2015-12-06 11:42:37 -03:00
////
}
// TerrainBrush
TerrainBrush::TerrainBrush() :
look_id(0), hate_friends(false)
{
2015-12-06 11:42:37 -03:00
////
}
TerrainBrush::~TerrainBrush()
{
2015-12-06 11:42:37 -03:00
////
}
bool TerrainBrush::friendOf(TerrainBrush* other)
{
uint32_t borderID = other->getID();
2015-12-06 11:42:37 -03:00
for(uint32_t friendId : friends) {
if(friendId == borderID) {
//printf("%s is friend of %s\n", getName().c_str(), other->getName().c_str());
return !hate_friends;
2015-12-06 11:42:37 -03:00
} else if(friendId == 0xFFFFFFFF) {
//printf("%s is generic friend of %s\n", getName().c_str(), other->getName().c_str());
return !hate_friends;
}
}
//printf("%s is enemy of %s\n", getName().c_str(), other->getName().c_str());
return hate_friends;
}
//=============================================================================
// Flag brush
// draws pz etc.
2015-12-06 11:42:37 -03:00
FlagBrush::FlagBrush(uint32_t _flag) : flag(_flag)
{
////
}
2015-12-06 11:42:37 -03:00
FlagBrush::~FlagBrush()
{
////
}
2015-12-06 11:42:37 -03:00
std::string FlagBrush::getName() const
{
switch(flag) {
case TILESTATE_PROTECTIONZONE: return "PZ brush (0x01)";
case TILESTATE_NOPVP: return "No combat zone brush (0x04)";
case TILESTATE_NOLOGOUT: return "No logout zone brush (0x08)";
case TILESTATE_PVPZONE: return "PVP Zone brush (0x10)";
}
return "Unknown flag brush";
}
2015-12-06 11:42:37 -03:00
int FlagBrush::getLookID() const
{
switch(flag) {
case TILESTATE_PROTECTIONZONE: return EDITOR_SPRITE_PZ_TOOL;
case TILESTATE_NOPVP: return EDITOR_SPRITE_NOPVP_TOOL;
case TILESTATE_NOLOGOUT: return EDITOR_SPRITE_NOLOG_TOOL;
case TILESTATE_PVPZONE: return EDITOR_SPRITE_PVPZ_TOOL;
}
return 0;
}
2015-12-06 11:42:37 -03:00
bool FlagBrush::canDraw(BaseMap* map, const Position& position) const
{
2014-03-01 14:44:11 +01:00
Tile* tile = map->getTile(position);
return tile && tile->hasGround();
}
2015-12-06 11:42:37 -03:00
void FlagBrush::undraw(BaseMap* map, Tile* tile)
{
tile->unsetMapFlags(flag);
}
2015-12-06 11:42:37 -03:00
void FlagBrush::draw(BaseMap* map, Tile* tile, void* parameter)
{
if(tile->hasGround()) {
tile->setMapFlags(flag);
}
}
//=============================================================================
// Door brush
2015-12-06 11:42:37 -03:00
DoorBrush::DoorBrush(DoorType _doortype) : doortype(_doortype)
{
////
}
2015-12-06 11:42:37 -03:00
DoorBrush::~DoorBrush()
{
////
}
2015-12-06 11:42:37 -03:00
std::string DoorBrush::getName() const
{
switch(doortype) {
case WALL_DOOR_NORMAL: return "Normal door brush";
case WALL_DOOR_LOCKED: return "Locked door brush";
case WALL_DOOR_MAGIC: return "Magic door brush";
case WALL_DOOR_QUEST: return "Quest door brush";
case WALL_WINDOW: return "Window brush";
case WALL_HATCH_WINDOW: return "Hatch window brush";
default: return "Unknown door brush";
}
}
2015-12-06 11:42:37 -03:00
int DoorBrush::getLookID() const
{
switch(doortype) {
case WALL_DOOR_NORMAL: return EDITOR_SPRITE_DOOR_NORMAL;
case WALL_DOOR_LOCKED: return EDITOR_SPRITE_DOOR_LOCKED;
case WALL_DOOR_MAGIC: return EDITOR_SPRITE_DOOR_MAGIC;
case WALL_DOOR_QUEST: return EDITOR_SPRITE_DOOR_QUEST;
case WALL_WINDOW: return EDITOR_SPRITE_WINDOW_NORMAL;
case WALL_HATCH_WINDOW: return EDITOR_SPRITE_WINDOW_HATCH;
default: return EDITOR_SPRITE_DOOR_NORMAL;
}
}
2015-12-06 11:42:37 -03:00
void DoorBrush::switchDoor(Item* item)
{
ASSERT(item);
ASSERT(item->isBrushDoor());
WallBrush* wb = item->getWallBrush();
if(!wb) return;
bool new_open = !item->isOpen();
BorderType wall_alignment = item->getWallAlignment();
DoorType doortype = WALL_UNDEFINED;
2015-12-06 11:42:37 -03:00
for(std::vector<WallBrush::DoorType>::iterator iter = wb->door_items[wall_alignment].begin(); iter != wb->door_items[wall_alignment].end(); ++iter) {
WallBrush::DoorType& dt = *iter;
if(dt.id == item->getID()) {
doortype = dt.type;
break;
}
}
if(doortype == WALL_UNDEFINED) return;
2015-12-06 11:42:37 -03:00
for(std::vector<WallBrush::DoorType>::iterator iter = wb->door_items[wall_alignment].begin(); iter != wb->door_items[wall_alignment].end(); ++iter) {
WallBrush::DoorType& dt = *iter;
if(dt.type == doortype) {
ASSERT(dt.id);
const ItemType& type = g_items.getItemType(dt.id);
ASSERT(type.id != 0);
if(type.isOpen == new_open) {
item->setID(dt.id);
return;
}
}
}
}
2014-03-01 14:44:11 +01:00
bool DoorBrush::canDraw(BaseMap* map, const Position& position) const
{
Tile* tile = map->getTile(position);
2015-12-06 11:42:37 -03:00
if(!tile) {
2014-03-01 14:44:11 +01:00
return false;
}
Item* item = tile->getWall();
2015-12-06 11:42:37 -03:00
if(!item) {
2014-03-01 14:44:11 +01:00
return false;
}
WallBrush* wb = item->getWallBrush();
2015-12-06 11:42:37 -03:00
if(!wb) {
2014-03-01 14:44:11 +01:00
return false;
}
BorderType wall_alignment = item->getWallAlignment();
uint16_t discarded_id = 0; // The id of a discarded match
bool close_match = false;
bool open = false;
if(item->isBrushDoor()) {
open = item->isOpen();
}
WallBrush* test_brush = wb;
do {
for(std::vector<WallBrush::DoorType>::iterator iter = test_brush->door_items[wall_alignment].begin();
iter != test_brush->door_items[wall_alignment].end();
2015-12-06 11:42:37 -03:00
++iter) {
WallBrush::DoorType& dt = *iter;
if(dt.type == doortype) {
ASSERT(dt.id);
const ItemType& type = g_items.getItemType(dt.id);
ASSERT(type.id != 0);
if(type.isOpen == open) {
return true;
2015-12-06 11:42:37 -03:00
} else if(!close_match) {
discarded_id = dt.id;
close_match = true;
}
if(!close_match && discarded_id == 0) {
discarded_id = dt.id;
}
}
}
test_brush = test_brush->redirect_to;
} while(test_brush != wb && test_brush != nullptr);
// If we've found no perfect match, use a close-to perfect
if(discarded_id) {
return true;
}
return false;
}
2015-12-06 11:42:37 -03:00
void DoorBrush::undraw(BaseMap* map, Tile* tile)
{
for(ItemVector::iterator it = tile->items.begin(); it != tile->items.end(); ++it) {
Item* item = *it;
if(item->isBrushDoor()) {
item->getWallBrush()->draw(map, tile, nullptr);
if(g_settings.getInteger(Config::USE_AUTOMAGIC)) {
tile->wallize(map);
}
return;
}
}
}
2015-12-06 11:42:37 -03:00
void DoorBrush::draw(BaseMap* map, Tile* tile, void* parameter)
{
for(ItemVector::iterator item_iter = tile->items.begin(); item_iter != tile->items.end();) {
Item* item = *item_iter;
2015-12-06 11:42:37 -03:00
if(!item->isWall()) {
++item_iter;
continue;
}
WallBrush* wb = item->getWallBrush();
if(!wb) {
++item_iter;
continue;
}
BorderType wall_alignment = item->getWallAlignment();
uint16_t discarded_id = 0; // The id of a discarded match
bool close_match = false;
bool perfect_match = false;
bool open = false;
if(parameter) {
open = *reinterpret_cast<bool*>(parameter);
}
if(item->isBrushDoor()) {
open = item->isOpen();
}
WallBrush* test_brush = wb;
do {
for(std::vector<WallBrush::DoorType>::iterator iter = test_brush->door_items[wall_alignment].begin();
iter != test_brush->door_items[wall_alignment].end();
++iter)
{
WallBrush::DoorType& dt = *iter;
if(dt.type == doortype) {
ASSERT(dt.id);
const ItemType& type = g_items.getItemType(dt.id);
ASSERT(type.id != 0);
if(type.isOpen == open) {
item = transformItem(item, dt.id, tile);
perfect_match = true;
break;
2015-12-06 11:42:37 -03:00
} else if(!close_match) {
discarded_id = dt.id;
close_match = true;
}
if(!close_match && discarded_id == 0) {
discarded_id = dt.id;
}
}
}
test_brush = test_brush->redirect_to;
if(perfect_match) {
break;
}
} while(test_brush != wb && test_brush != nullptr);
// If we've found no perfect match, use a close-to perfect
2015-12-06 11:42:37 -03:00
if(!perfect_match && discarded_id) {
item = transformItem(item, discarded_id, tile);
}
if(g_settings.getInteger(Config::AUTO_ASSIGN_DOORID) && tile->isHouseTile()) {
Map* mmap = dynamic_cast<Map*>(map);
Door* door = dynamic_cast<Door*>(item);
if(mmap && door) {
House* house = mmap->houses.getHouse(tile->getHouseID());
ASSERT(house);
Map* real_map = dynamic_cast<Map*>(map);
if(real_map) {
door->setDoorID(house->getEmptyDoorID());
}
}
}
// We need to consider decorations!
while(true) {
// Vector has been modified, before we can use the iterator again we need to find the wall item again
item_iter = tile->items.begin();
while(true) {
if(item_iter == tile->items.end()) {
return;
}
if(*item_iter == item) {
++item_iter;
if(item_iter == tile->items.end()) {
return;
}
break;
}
++item_iter;
}
// Now it points to the correct item!
item = *item_iter;
if(item->isWall()) {
2016-11-12 10:30:06 -03:00
WallBrush* brush = item->getWallBrush();
if(brush && brush->isWallDecoration()) {
// We got a decoration!
2023-01-25 21:44:24 -03:00
for(std::vector<WallBrush::DoorType>::iterator it = brush->door_items[wall_alignment].begin(); it != brush->door_items[wall_alignment].end(); ++it) {
2016-11-12 10:30:06 -03:00
WallBrush::DoorType& dt = (*it);
if(dt.type == doortype) {
ASSERT(dt.id);
const ItemType& type = g_items.getItemType(dt.id);
ASSERT(type.id != 0);
if(type.isOpen == open) {
item = transformItem(item, dt.id, tile);
perfect_match = true;
break;
2015-12-06 11:42:37 -03:00
} else if(!close_match) {
discarded_id = dt.id;
close_match = true;
}
if(!close_match && discarded_id == 0) {
discarded_id = dt.id;
}
}
}
// If we've found no perfect match, use a close-to perfect
2015-12-06 11:42:37 -03:00
if(!perfect_match && discarded_id) {
item = transformItem(item, discarded_id, tile);
}
continue;
}
}
break;
}
// If we get this far in the loop we should return
return;
}
}
//=============================================================================
// Gravel brush
2015-12-06 11:42:37 -03:00
OptionalBorderBrush::OptionalBorderBrush()
{
////
}
2015-12-06 11:42:37 -03:00
OptionalBorderBrush::~OptionalBorderBrush()
{
////
}
2015-12-06 11:42:37 -03:00
std::string OptionalBorderBrush::getName() const
{
return "Optional Border Tool";
}
2015-12-06 11:42:37 -03:00
int OptionalBorderBrush::getLookID() const
{
return EDITOR_SPRITE_OPTIONAL_BORDER_TOOL;
}
2014-03-01 14:44:11 +01:00
bool OptionalBorderBrush::canDraw(BaseMap* map, const Position& position) const
{
Tile* tile = map->getTile(position);
// You can't do gravel on a mountain tile
if(tile) {
if(GroundBrush* bb = tile->getGroundBrush()) {
if(bb->hasOptionalBorder()) {
return false;
}
}
}
2014-03-01 14:44:11 +01:00
uint32_t x = position.x;
uint32_t y = position.y;
uint32_t z = position.z;
tile = map->getTile(x - 1, y - 1, z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
tile = map->getTile(x , y - 1, z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
tile = map->getTile(x + 1, y - 1, z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
tile = map->getTile(x - 1, y , z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
tile = map->getTile(x + 1, y , z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
tile = map->getTile(x - 1, y + 1, z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
tile = map->getTile(x , y + 1, z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
tile = map->getTile(x + 1, y + 1, z);
if(tile) if(GroundBrush* bb = tile->getGroundBrush()) if(bb->hasOptionalBorder()) return true;
return false;
}
2015-12-06 11:42:37 -03:00
void OptionalBorderBrush::undraw(BaseMap* map, Tile* tile)
{
tile->setOptionalBorder(false); // The bordering algorithm will handle this automagicaly
}
2015-12-06 11:42:37 -03:00
void OptionalBorderBrush::draw(BaseMap* map, Tile* tile, void* parameter)
{
tile->setOptionalBorder(true); // The bordering algorithm will handle this automagicaly
}