mirror of
https://github.com/opentibiabr/remeres-map-editor
synced 2026-08-15 18:26:04 -04:00
Created the complete organization of npcs, that way, it is possible to create own files for npcs, no longer depending on being together with the monsters this pull request was made to be worked together with the canary project: https://github.com/opentibiabr/canary The spawns npcs is "yellow" and spawns monsters is "red" (the old) With this change, the map will automatically create an "npc.xml" the first time it is saved. It is possible to edit only the xml, it is not necessary to add or modify the npc by opening the editor map Thus, there is a script for npcs, similar to how it works with old spawns
2018 lines
59 KiB
C++
2018 lines
59 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 "editor.h"
|
|
#include "materials.h"
|
|
#include "map.h"
|
|
#include "complexitem.h"
|
|
#include "settings.h"
|
|
#include "gui.h"
|
|
#include "map_display.h"
|
|
#include "brush.h"
|
|
#include "ground_brush.h"
|
|
#include "wall_brush.h"
|
|
#include "table_brush.h"
|
|
#include "carpet_brush.h"
|
|
#include "waypoint_brush.h"
|
|
#include "house_exit_brush.h"
|
|
#include "doodad_brush.h"
|
|
#include "monster_brush.h"
|
|
#include "npc_brush.h"
|
|
#include "spawn_monster_brush.h"
|
|
#include "spawn_npc_brush.h"
|
|
|
|
#include "live_server.h"
|
|
#include "live_client.h"
|
|
#include "live_action.h"
|
|
|
|
Editor::Editor(CopyBuffer& copybuffer) :
|
|
live_server(nullptr),
|
|
live_client(nullptr),
|
|
actionQueue(newd ActionQueue(*this)),
|
|
selection(*this),
|
|
copybuffer(copybuffer),
|
|
replace_brush(nullptr)
|
|
{
|
|
wxString error;
|
|
wxArrayString warnings;
|
|
bool ok = true;
|
|
|
|
ClientVersionID defaultVersion = ClientVersionID(g_settings.getInteger(Config::DEFAULT_CLIENT_VERSION));
|
|
if(defaultVersion == CLIENT_VERSION_NONE)
|
|
defaultVersion = ClientVersion::getLatestVersion()->getID();
|
|
|
|
if(g_gui.GetCurrentVersionID() != defaultVersion) {
|
|
if(g_gui.CloseAllEditors()) {
|
|
ok = g_gui.LoadVersion(defaultVersion, error, warnings);
|
|
g_gui.PopupDialog("Error", error, wxOK);
|
|
g_gui.ListDialog("Warnings", warnings);
|
|
} else {
|
|
throw std::runtime_error("All maps of different versions were not closed.");
|
|
}
|
|
}
|
|
|
|
if(!ok)
|
|
throw std::runtime_error("Couldn't load client version");
|
|
|
|
MapVersion version;
|
|
version.otbm = g_gui.GetCurrentVersion().getPrefferedMapVersionID();
|
|
version.client = g_gui.GetCurrentVersionID();
|
|
map.convert(version);
|
|
|
|
map.height = 2048;
|
|
map.width = 2048;
|
|
|
|
static int unnamed_counter = 0;
|
|
|
|
std::string sname = "Untitled-" + i2s(++unnamed_counter);
|
|
map.name = sname + ".otbm";
|
|
map.spawnmonsterfile = sname + "-monster.xml";
|
|
map.spawnnpcfile = sname + "-npc.xml";
|
|
map.housefile = sname + "-house.xml";
|
|
map.description = "No map description available.";
|
|
map.unnamed = true;
|
|
|
|
map.doChange();
|
|
}
|
|
|
|
Editor::Editor(CopyBuffer& copybuffer, const FileName& fn) :
|
|
live_server(nullptr),
|
|
live_client(nullptr),
|
|
actionQueue(newd ActionQueue(*this)),
|
|
selection(*this),
|
|
copybuffer(copybuffer),
|
|
replace_brush(nullptr)
|
|
{
|
|
MapVersion ver;
|
|
if(!IOMapOTBM::getVersionInfo(fn, ver)) {
|
|
// g_gui.PopupDialog("Error", "Could not open file \"" + fn.GetFullPath() + "\".", wxOK);
|
|
throw std::runtime_error("Could not open file \"" + nstr(fn.GetFullPath()) + "\".\nThis is not a valid OTBM file or it does not exist.");
|
|
}
|
|
|
|
/*
|
|
if(ver < CLIENT_VERSION_760) {
|
|
long b = g_gui.PopupDialog("Error", "Unsupported Client Version (pre 7.6), do you want to try to load the map anyways?", wxYES | wxNO);
|
|
if(b == wxID_NO) {
|
|
valid_state = false;
|
|
return;
|
|
}
|
|
}
|
|
*/
|
|
|
|
bool success = true;
|
|
if(g_gui.GetCurrentVersionID() != ver.client) {
|
|
wxString error;
|
|
wxArrayString warnings;
|
|
if(g_gui.CloseAllEditors()) {
|
|
success = g_gui.LoadVersion(ver.client, error, warnings);
|
|
if(!success)
|
|
g_gui.PopupDialog("Error", error, wxOK);
|
|
else
|
|
g_gui.ListDialog("Warnings", warnings);
|
|
} else {
|
|
throw std::runtime_error("All maps of different versions were not closed.");
|
|
}
|
|
}
|
|
|
|
if(success) {
|
|
ScopedLoadingBar LoadingBar("Loading OTBM map...");
|
|
success = map.open(nstr(fn.GetFullPath()));
|
|
/* TODO
|
|
if(success && ver.client == CLIENT_VERSION_854_BAD) {
|
|
int ok = g_gui.PopupDialog("Incorrect OTB", "This map has been saved with an incorrect OTB version, do you want to convert it to the new OTB version?\n\nIf you are not sure, click Yes.", wxYES | wxNO);
|
|
|
|
if(ok == wxID_YES){
|
|
ver.client = CLIENT_VERSION_854;
|
|
map.convert(ver);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
Editor::Editor(CopyBuffer& copybuffer, LiveClient* client) :
|
|
live_server(nullptr),
|
|
live_client(client),
|
|
actionQueue(newd NetworkedActionQueue(*this)),
|
|
selection(*this),
|
|
copybuffer(copybuffer),
|
|
replace_brush(nullptr)
|
|
{
|
|
;
|
|
}
|
|
|
|
Editor::~Editor() {
|
|
if(IsLive()) {
|
|
CloseLiveServer();
|
|
}
|
|
|
|
UnnamedRenderingLock();
|
|
selection.clear();
|
|
delete actionQueue;
|
|
}
|
|
|
|
void Editor::addBatch(BatchAction* action, int stacking_delay)
|
|
{
|
|
actionQueue->addBatch(action, stacking_delay);
|
|
g_gui.UpdateMenus();
|
|
}
|
|
|
|
void Editor::addAction(Action* action, int stacking_delay )
|
|
{
|
|
actionQueue->addAction(action, stacking_delay);
|
|
g_gui.UpdateMenus();
|
|
}
|
|
|
|
void Editor::saveMap(FileName filename, bool showdialog)
|
|
{
|
|
std::string savefile = filename.GetFullPath().mb_str(wxConvUTF8).data();
|
|
bool save_as = false;
|
|
bool save_otgz = false;
|
|
|
|
if(savefile.empty()) {
|
|
savefile = map.filename;
|
|
|
|
FileName c1(wxstr(savefile));
|
|
FileName c2(wxstr(map.filename));
|
|
save_as = c1 != c2;
|
|
}
|
|
|
|
// If not named yet, propagate the file name to the auxilliary files
|
|
if(map.unnamed) {
|
|
FileName _name(filename);
|
|
_name.SetExt("xml");
|
|
|
|
_name.SetName(filename.GetName() + "-monster");
|
|
map.spawnmonsterfile = nstr(_name.GetFullName());
|
|
_name.SetName(filename.GetName() + "-npc");
|
|
map.spawnnpcfile = nstr(_name.GetFullName());
|
|
_name.SetName(filename.GetName() + "-house");
|
|
map.housefile = nstr(_name.GetFullName());
|
|
|
|
map.unnamed = false;
|
|
}
|
|
|
|
// File object to convert between local paths etc.
|
|
FileName converter;
|
|
converter.Assign(wxstr(savefile));
|
|
std::string map_path = nstr(converter.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
|
|
|
|
// Make temporary backups
|
|
//converter.Assign(wxstr(savefile));
|
|
std::string backup_otbm, backup_house, backup_spawn, backup_spawn_npc;
|
|
|
|
if(converter.GetExt() == "otgz") {
|
|
save_otgz = true;
|
|
if(converter.FileExists()) {
|
|
backup_otbm = map_path + nstr(converter.GetName()) + ".otgz~";
|
|
std::remove(backup_otbm.c_str());
|
|
std::rename(savefile.c_str(), backup_otbm.c_str());
|
|
}
|
|
} else {
|
|
if(converter.FileExists()) {
|
|
backup_otbm = map_path + nstr(converter.GetName()) + ".otbm~";
|
|
std::remove(backup_otbm.c_str());
|
|
std::rename(savefile.c_str(), backup_otbm.c_str());
|
|
}
|
|
|
|
converter.SetFullName(wxstr(map.housefile));
|
|
if(converter.FileExists()) {
|
|
backup_house = map_path + nstr(converter.GetName()) + ".xml~";
|
|
std::remove(backup_house.c_str());
|
|
std::rename((map_path + map.housefile).c_str(), backup_house.c_str());
|
|
}
|
|
|
|
converter.SetFullName(wxstr(map.spawnmonsterfile));
|
|
if(converter.FileExists()) {
|
|
backup_spawn = map_path + nstr(converter.GetName()) + ".xml~";
|
|
std::remove(backup_spawn.c_str());
|
|
std::rename((map_path + map.spawnmonsterfile).c_str(), backup_spawn.c_str());
|
|
}
|
|
|
|
converter.SetFullName(wxstr(map.spawnnpcfile));
|
|
if(converter.FileExists()) {
|
|
backup_spawn_npc = map_path + nstr(converter.GetName()) + ".xml~";
|
|
std::remove(backup_spawn_npc.c_str());
|
|
std::rename((map_path + map.spawnnpcfile).c_str(), backup_spawn_npc.c_str());
|
|
}
|
|
}
|
|
|
|
// Save the map
|
|
{
|
|
std::string n = nstr(g_gui.GetLocalDataDirectory()) + ".saving.txt";
|
|
std::ofstream f(n.c_str(), std::ios::trunc | std::ios::out);
|
|
f <<
|
|
backup_otbm << std::endl <<
|
|
backup_house << std::endl <<
|
|
backup_spawn << std::endl <<
|
|
backup_spawn_npc << std::endl;
|
|
}
|
|
|
|
{
|
|
|
|
// Set up the Map paths
|
|
wxFileName fn = wxstr(savefile);
|
|
map.filename = fn.GetFullPath().mb_str(wxConvUTF8);
|
|
map.name = fn.GetFullName().mb_str(wxConvUTF8);
|
|
|
|
if(showdialog)
|
|
g_gui.CreateLoadBar("Saving OTBM map...");
|
|
|
|
// Perform the actual save
|
|
IOMapOTBM mapsaver(map.getVersion());
|
|
bool success = mapsaver.saveMap(map, fn);
|
|
|
|
if(showdialog)
|
|
g_gui.DestroyLoadBar();
|
|
|
|
// Check for errors...
|
|
if(!success) {
|
|
// Rename the temporary backup files back to their previous names
|
|
if(!backup_otbm.empty()) {
|
|
converter.SetFullName(wxstr(savefile));
|
|
std::string otbm_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_otbm.c_str(), std::string(otbm_filename + (save_otgz ? ".otgz" : ".otbm")).c_str());
|
|
}
|
|
|
|
if(!backup_house.empty()) {
|
|
converter.SetFullName(wxstr(map.housefile));
|
|
std::string house_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_house.c_str(), std::string(house_filename + ".xml").c_str());
|
|
}
|
|
|
|
if(!backup_spawn.empty()) {
|
|
converter.SetFullName(wxstr(map.spawnmonsterfile));
|
|
std::string spawn_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_spawn.c_str(), std::string(spawn_filename + ".xml").c_str());
|
|
}
|
|
|
|
if(!backup_spawn_npc.empty()) {
|
|
converter.SetFullName(wxstr(map.spawnnpcfile));
|
|
std::string spawnnpc_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_spawn_npc.c_str(), std::string(spawnnpc_filename + ".xml").c_str());
|
|
}
|
|
|
|
// Display the error
|
|
g_gui.PopupDialog("Error", "Could not save, unable to open target for writing.", wxOK);
|
|
}
|
|
|
|
// Remove temporary save runfile
|
|
{
|
|
std::string n = nstr(g_gui.GetLocalDataDirectory()) + ".saving.txt";
|
|
std::remove(n.c_str());
|
|
}
|
|
|
|
// If failure, don't run the rest of the function
|
|
if(!success)
|
|
return;
|
|
}
|
|
|
|
|
|
// Move to permanent backup
|
|
if(!save_as && g_settings.getInteger(Config::ALWAYS_MAKE_BACKUP)) {
|
|
// Move temporary backups to their proper files
|
|
time_t t = time(nullptr);
|
|
tm* current_time = localtime(&t);
|
|
ASSERT(current_time);
|
|
|
|
std::ostringstream date;
|
|
date << (1900 + current_time->tm_year);
|
|
if(current_time->tm_mon < 9)
|
|
date << "-" << "0" << current_time->tm_mon+1;
|
|
else
|
|
date << "-" << current_time->tm_mon+1;
|
|
date << "-" << current_time->tm_mday;
|
|
date << "-" << current_time->tm_hour;
|
|
date << "-" << current_time->tm_min;
|
|
date << "-" << current_time->tm_sec;
|
|
|
|
if(!backup_otbm.empty()) {
|
|
converter.SetFullName(wxstr(savefile));
|
|
std::string otbm_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_otbm.c_str(), std::string(otbm_filename + "." + date.str() + (save_otgz ? ".otgz" : ".otbm")).c_str());
|
|
}
|
|
|
|
if(!backup_house.empty()) {
|
|
converter.SetFullName(wxstr(map.housefile));
|
|
std::string house_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_house.c_str(), std::string(house_filename + "." + date.str() + ".xml").c_str());
|
|
}
|
|
|
|
if(!backup_spawn.empty()) {
|
|
converter.SetFullName(wxstr(map.spawnmonsterfile));
|
|
std::string spawn_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_spawn.c_str(), std::string(spawn_filename + "." + date.str() + ".xml").c_str());
|
|
}
|
|
|
|
if(!backup_spawn_npc.empty()) {
|
|
converter.SetFullName(wxstr(map.spawnnpcfile));
|
|
std::string spawnnpc_filename = map_path + nstr(converter.GetName());
|
|
std::rename(backup_spawn_npc.c_str(), std::string(spawnnpc_filename + "." + date.str() + ".xml").c_str());
|
|
}
|
|
} else {
|
|
// Delete the temporary files
|
|
std::remove(backup_otbm.c_str());
|
|
std::remove(backup_house.c_str());
|
|
std::remove(backup_spawn.c_str());
|
|
std::remove(backup_spawn_npc.c_str());
|
|
}
|
|
|
|
map.clearChanges();
|
|
}
|
|
|
|
bool Editor::importMiniMap(FileName filename, int import, int import_x_offset, int import_y_offset, int import_z_offset)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Editor::exportMiniMap(FileName filename, int floor /*= GROUND_LAYER*/, bool displaydialog)
|
|
{
|
|
return map.exportMinimap(filename, floor, displaydialog);
|
|
}
|
|
|
|
bool Editor::exportSelectionAsMiniMap(FileName directory, wxString fileName)
|
|
{
|
|
if(!directory.Exists() || !directory.IsDirWritable())
|
|
return false;
|
|
|
|
int min_x = MAP_MAX_WIDTH + 1, min_y = MAP_MAX_HEIGHT + 1, min_z = MAP_MAX_LAYER + 1;
|
|
int max_x = 0, max_y = 0, max_z = 0;
|
|
|
|
const TileSet& tiles = selection.getTiles();
|
|
for(Tile* tile : tiles) {
|
|
if(tile->empty())
|
|
continue;
|
|
|
|
Position pos = tile->getPosition();
|
|
|
|
if(pos.x < min_x)
|
|
min_x = pos.x;
|
|
if(pos.x > max_x)
|
|
max_x = pos.x;
|
|
|
|
if(pos.y < min_y)
|
|
min_y = pos.y;
|
|
if(pos.y > max_y)
|
|
max_y = pos.y;
|
|
|
|
if(pos.z < min_z)
|
|
min_z = pos.z;
|
|
if(pos.z > max_z)
|
|
max_z = pos.z;
|
|
}
|
|
|
|
int numtiles = (max_x - min_x) * (max_y - min_y);
|
|
int minimap_width = max_x - min_x + 1;
|
|
int minimap_height = max_y - min_y + 1;
|
|
|
|
if(numtiles == 0)
|
|
return false;
|
|
|
|
if(minimap_width > 2048 || minimap_height > 2048) {
|
|
g_gui.PopupDialog("Error", "Minimap size greater than 2048px.", wxOK);
|
|
return false;
|
|
}
|
|
|
|
int tiles_iterated = 0;
|
|
|
|
for(int z = min_z; z <= max_z; z++) {
|
|
uint8_t* pixels = newd uint8_t[minimap_width * minimap_height * 3]; // 3 bytes per pixel
|
|
memset(pixels, 0, minimap_width * minimap_height * 3);
|
|
|
|
for(Tile* tile : tiles) {
|
|
if(tile->getZ() != z)
|
|
continue;
|
|
|
|
++tiles_iterated;
|
|
if(tiles_iterated % 8192 == 0)
|
|
g_gui.SetLoadDone(int(tiles_iterated / double(tiles.size()) * 90.0));
|
|
|
|
if(tile->empty())
|
|
continue;
|
|
|
|
uint8_t color = 0;
|
|
|
|
for(Item* item : tile->items) {
|
|
if(item->getMiniMapColor() != 0) {
|
|
color = item->getMiniMapColor();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(color == 0 && tile->hasGround())
|
|
color = tile->ground->getMiniMapColor();
|
|
|
|
uint32_t index = ((tile->getY() - min_y) * minimap_width + (tile->getX() - min_x)) * 3;
|
|
|
|
pixels[index] = (uint8_t)(int(color / 36) % 6 * 51); // red
|
|
pixels[index + 1] = (uint8_t)(int(color / 6) % 6 * 51); // green
|
|
pixels[index + 2] = (uint8_t)(color % 6 * 51); // blue
|
|
}
|
|
|
|
FileName file(fileName + "_" + i2ws(z) + ".png");
|
|
file.Normalize(wxPATH_NORM_ALL, directory.GetFullPath());
|
|
wxImage* image = newd wxImage(minimap_width, minimap_height, pixels, true);
|
|
image->SaveFile(file.GetFullPath(), wxBITMAP_TYPE_PNG);
|
|
image->Destroy();
|
|
delete[] pixels;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Editor::importMap(FileName filename, int import_x_offset, int import_y_offset, ImportType house_import_type, ImportType spawn_import_type, ImportType spawn_npc_import_type)
|
|
{
|
|
selection.clear();
|
|
actionQueue->clear();
|
|
|
|
Map imported_map;
|
|
bool loaded = imported_map.open(nstr(filename.GetFullPath()));
|
|
|
|
if(!loaded) {
|
|
g_gui.PopupDialog("Error", "Error loading map!\n" + imported_map.getError(), wxOK | wxICON_INFORMATION);
|
|
return false;
|
|
}
|
|
g_gui.ListDialog("Warning", imported_map.getWarnings());
|
|
|
|
Position offset(import_x_offset, import_y_offset, 0);
|
|
|
|
bool resizemap = false;
|
|
bool resize_asked = false;
|
|
int newsize_x = map.getWidth(), newsize_y = map.getHeight();
|
|
int discarded_tiles = 0;
|
|
|
|
g_gui.CreateLoadBar("Merging maps...");
|
|
|
|
std::map<uint32_t, uint32_t> town_id_map;
|
|
std::map<uint32_t, uint32_t> house_id_map;
|
|
|
|
if(house_import_type != IMPORT_DONT) {
|
|
for(TownMap::iterator tit = imported_map.towns.begin(); tit != imported_map.towns.end();) {
|
|
Town* imported_town = tit->second;
|
|
Town* current_town = map.towns.getTown(imported_town->getID());
|
|
|
|
Position oldexit = imported_town->getTemplePosition();
|
|
Position newexit = oldexit + offset;
|
|
if(newexit.isValid()) {
|
|
imported_town->setTemplePosition(newexit);
|
|
}
|
|
|
|
switch(house_import_type) {
|
|
case IMPORT_MERGE: {
|
|
town_id_map[imported_town->getID()] = imported_town->getID();
|
|
if(current_town) {
|
|
++tit;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case IMPORT_SMART_MERGE: {
|
|
if(current_town) {
|
|
// Compare and insert/merge depending on parameters
|
|
if(current_town->getName() == imported_town->getName() && current_town->getID() == imported_town->getID()) {
|
|
// Just add to map
|
|
town_id_map[imported_town->getID()] = current_town->getID();
|
|
++tit;
|
|
continue;
|
|
} else {
|
|
// Conflict! Find a newd id and replace old
|
|
uint32_t new_id = map.towns.getEmptyID();
|
|
imported_town->setID(new_id);
|
|
town_id_map[imported_town->getID()] = new_id;
|
|
}
|
|
} else {
|
|
town_id_map[imported_town->getID()] = imported_town->getID();
|
|
}
|
|
break;
|
|
}
|
|
case IMPORT_INSERT: {
|
|
// Find a newd id and replace old
|
|
uint32_t new_id = map.towns.getEmptyID();
|
|
imported_town->setID(new_id);
|
|
town_id_map[imported_town->getID()] = new_id;
|
|
break;
|
|
}
|
|
case IMPORT_DONT: {
|
|
++tit;
|
|
continue; // Should never happend..?
|
|
break; // Continue or break ?
|
|
}
|
|
}
|
|
|
|
map.towns.addTown(imported_town);
|
|
|
|
#ifdef __VISUALC__ // C++0x compliance to some degree :)
|
|
tit = imported_map.towns.erase(tit);
|
|
#else // Bulky, slow way
|
|
TownMap::iterator tmp_iter = tit;
|
|
++tmp_iter;
|
|
uint32_t next_key = 0;
|
|
if(tmp_iter != imported_map.towns.end()) {
|
|
next_key = tmp_iter->first;
|
|
}
|
|
imported_map.towns.erase(tit);
|
|
if(next_key != 0) {
|
|
tit = imported_map.towns.find(next_key);
|
|
} else {
|
|
tit = imported_map.towns.end();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
for(HouseMap::iterator hit = imported_map.houses.begin(); hit != imported_map.houses.end();) {
|
|
House* imported_house = hit->second;
|
|
House* current_house = map.houses.getHouse(imported_house->id);
|
|
imported_house->townid = town_id_map[imported_house->townid];
|
|
|
|
Position oldexit = imported_house->getExit();
|
|
imported_house->setExit(nullptr, Position()); // Reset it
|
|
|
|
switch(house_import_type) {
|
|
case IMPORT_MERGE: {
|
|
house_id_map[imported_house->id] = imported_house->id;
|
|
if(current_house) {
|
|
++hit;
|
|
Position newexit = oldexit + offset;
|
|
if(newexit.isValid()) current_house->setExit(&map, newexit);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case IMPORT_SMART_MERGE: {
|
|
if(current_house) {
|
|
// Compare and insert/merge depending on parameters
|
|
if(current_house->name == imported_house->name && current_house->townid == imported_house->townid) {
|
|
// Just add to map
|
|
house_id_map[imported_house->id] = current_house->id;
|
|
++hit;
|
|
Position newexit = oldexit + offset;
|
|
if(newexit.isValid()) imported_house->setExit(&map, newexit);
|
|
continue;
|
|
} else {
|
|
// Conflict! Find a newd id and replace old
|
|
uint32_t new_id = map.houses.getEmptyID();
|
|
house_id_map[imported_house->id] = new_id;
|
|
imported_house->id = new_id;
|
|
}
|
|
} else {
|
|
house_id_map[imported_house->id] = imported_house->id;
|
|
}
|
|
break;
|
|
}
|
|
case IMPORT_INSERT: {
|
|
// Find a newd id and replace old
|
|
uint32_t new_id = map.houses.getEmptyID();
|
|
house_id_map[imported_house->id] = new_id;
|
|
imported_house->id = new_id;
|
|
break;
|
|
}
|
|
case IMPORT_DONT: {
|
|
++hit;
|
|
Position newexit = oldexit + offset;
|
|
if(newexit.isValid()) imported_house->setExit(&map, newexit);
|
|
continue; // Should never happend..?
|
|
break;
|
|
}
|
|
}
|
|
|
|
Position newexit = oldexit + offset;
|
|
if(newexit.isValid()) imported_house->setExit(&map, newexit);
|
|
map.houses.addHouse(imported_house);
|
|
|
|
#ifdef __VISUALC__ // C++0x compliance to some degree :)
|
|
hit = imported_map.houses.erase(hit);
|
|
#else // Bulky, slow way
|
|
HouseMap::iterator tmp_iter = hit;
|
|
++tmp_iter;
|
|
uint32_t next_key = 0;
|
|
if(tmp_iter != imported_map.houses.end()) {
|
|
next_key = tmp_iter->first;
|
|
}
|
|
imported_map.houses.erase(hit);
|
|
if(next_key != 0) {
|
|
hit = imported_map.houses.find(next_key);
|
|
} else {
|
|
hit = imported_map.houses.end();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Monster spawn import
|
|
std::map<Position, SpawnMonster*> spawn_monster_map;
|
|
if(spawn_import_type != IMPORT_DONT) {
|
|
for(SpawnNpcPositionList::iterator siter = imported_map.spawnsMonster.begin(); siter != imported_map.spawnsMonster.end();) {
|
|
Position oldSpawnMonsterPos = *siter;
|
|
Position newSpawnMonsterPos = *siter + offset;
|
|
switch(spawn_import_type) {
|
|
case IMPORT_SMART_MERGE:
|
|
case IMPORT_INSERT:
|
|
case IMPORT_MERGE: {
|
|
Tile* imported_tile = imported_map.getTile(oldSpawnMonsterPos);
|
|
if(imported_tile) {
|
|
ASSERT(imported_tile->spawnMonster);
|
|
spawn_monster_map[newSpawnMonsterPos] = imported_tile->spawnMonster;
|
|
|
|
SpawnNpcPositionList::iterator next = siter;
|
|
bool cont = true;
|
|
Position next_spawn_monster;
|
|
|
|
++next;
|
|
if(next == imported_map.spawnsMonster.end())
|
|
cont = false;
|
|
else
|
|
next_spawn_monster = *next;
|
|
imported_map.spawnsMonster.erase(siter);
|
|
if(cont)
|
|
siter = imported_map.spawnsMonster.find(next_spawn_monster);
|
|
else
|
|
siter = imported_map.spawnsMonster.end();
|
|
}
|
|
break;
|
|
}
|
|
case IMPORT_DONT: {
|
|
++siter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Npc spawn import
|
|
std::map<Position, SpawnNpc*> spawn_npc_map;
|
|
if(spawn_npc_import_type != IMPORT_DONT) {
|
|
for(SpawnNpcPositionList::iterator siter = imported_map.spawnsNpc.begin(); siter != imported_map.spawnsNpc.end();) {
|
|
Position oldSpawnNpcPos = *siter;
|
|
Position newSpawnNpcPos = *siter + offset;
|
|
switch(spawn_npc_import_type) {
|
|
case IMPORT_SMART_MERGE:
|
|
case IMPORT_INSERT:
|
|
case IMPORT_MERGE: {
|
|
Tile* importedTile = imported_map.getTile(oldSpawnNpcPos);
|
|
if(importedTile) {
|
|
ASSERT(importedTile->spawnNpc);
|
|
spawn_npc_map[newSpawnNpcPos] = importedTile->spawnNpc;
|
|
|
|
SpawnNpcPositionList::iterator next = siter;
|
|
bool cont = true;
|
|
Position next_spawn_npc;
|
|
|
|
++next;
|
|
if(next == imported_map.spawnsNpc.end())
|
|
cont = false;
|
|
else
|
|
next_spawn_npc = *next;
|
|
imported_map.spawnsNpc.erase(siter);
|
|
if(cont)
|
|
siter = imported_map.spawnsNpc.find(next_spawn_npc);
|
|
else
|
|
siter = imported_map.spawnsNpc.end();
|
|
}
|
|
break;
|
|
}
|
|
case IMPORT_DONT: {
|
|
++siter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Plain merge of waypoints, very simple! :)
|
|
for(WaypointMap::iterator iter = imported_map.waypoints.begin(); iter != imported_map.waypoints.end(); ++iter) {
|
|
iter->second->pos += offset;
|
|
}
|
|
|
|
map.waypoints.waypoints.insert(imported_map.waypoints.begin(), imported_map.waypoints.end());
|
|
imported_map.waypoints.waypoints.clear();
|
|
|
|
|
|
uint64_t tiles_merged = 0;
|
|
uint64_t tiles_to_import = imported_map.tilecount;
|
|
for(MapIterator mit = imported_map.begin(); mit != imported_map.end(); ++mit) {
|
|
if(tiles_merged % 8092 == 0) {
|
|
g_gui.SetLoadDone(int(100.0 * tiles_merged / tiles_to_import));
|
|
}
|
|
++tiles_merged;
|
|
|
|
Tile* import_tile = (*mit)->get();
|
|
Position new_pos = import_tile->getPosition() + offset;
|
|
if(!new_pos.isValid()) {
|
|
++discarded_tiles;
|
|
continue;
|
|
}
|
|
|
|
if(!resizemap && (new_pos.x > map.getWidth() || new_pos.y > map.getHeight())) {
|
|
if(resize_asked) {
|
|
++discarded_tiles;
|
|
continue;
|
|
} else {
|
|
resize_asked = true;
|
|
int ret = g_gui.PopupDialog("Collision", "The imported tiles are outside the current map scope. Do you want to resize the map? (Else additional tiles will be removed)", wxYES | wxNO);
|
|
|
|
if(ret == wxID_YES) {
|
|
// ...
|
|
resizemap = true;
|
|
} else {
|
|
++discarded_tiles;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(new_pos.x > newsize_x) {
|
|
newsize_x = new_pos.x;
|
|
}
|
|
if(new_pos.y > newsize_y) {
|
|
newsize_y = new_pos.y;
|
|
}
|
|
|
|
imported_map.setTile(import_tile->getPosition(), nullptr);
|
|
TileLocation* location = map.createTileL(new_pos);
|
|
|
|
|
|
// Check if we should update any houses
|
|
int new_houseid = house_id_map[import_tile->getHouseID()];
|
|
House* house = map.houses.getHouse(new_houseid);
|
|
if(import_tile->isHouseTile() && house_import_type != IMPORT_DONT && house) {
|
|
// We need to notify houses of the tile moving
|
|
house->removeTile(import_tile);
|
|
import_tile->setLocation(location);
|
|
house->addTile(import_tile);
|
|
} else {
|
|
import_tile->setLocation(location);
|
|
}
|
|
|
|
if(offset != Position(0,0,0)) {
|
|
for(ItemVector::iterator iter = import_tile->items.begin(); iter != import_tile->items.end(); ++iter) {
|
|
Item* item = *iter;
|
|
if(Teleport* teleport = dynamic_cast<Teleport*>(item)) {
|
|
teleport->setDestination(teleport->getDestination() + offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
Tile* old_tile = map.getTile(new_pos);
|
|
if(old_tile) {
|
|
map.removeSpawnMonster(old_tile);
|
|
}
|
|
import_tile->spawnMonster = nullptr;
|
|
|
|
map.setTile(new_pos, import_tile, true);
|
|
}
|
|
|
|
for(std::map<Position, SpawnMonster*>::iterator spawn_monster_iter = spawn_monster_map.begin(); spawn_monster_iter != spawn_monster_map.end(); ++spawn_monster_iter) {
|
|
Position pos = spawn_monster_iter->first;
|
|
TileLocation* location = map.createTileL(pos);
|
|
Tile* tile = location->get();
|
|
if(!tile) {
|
|
tile = map.allocator(location);
|
|
map.setTile(pos, tile);
|
|
} else if(tile->spawnMonster) {
|
|
map.removeSpawnMonsterInternal(tile);
|
|
delete tile->spawnMonster;
|
|
}
|
|
tile->spawnMonster = spawn_monster_iter->second;
|
|
|
|
map.addSpawnMonster(tile);
|
|
}
|
|
|
|
for(std::map<Position, SpawnNpc*>::iterator spawn_npc_iter = spawn_npc_map.begin(); spawn_npc_iter != spawn_npc_map.end(); ++spawn_npc_iter) {
|
|
Position pos = spawn_npc_iter->first;
|
|
TileLocation* location = map.createTileL(pos);
|
|
Tile* tile = location->get();
|
|
if(!tile) {
|
|
tile = map.allocator(location);
|
|
map.setTile(pos, tile);
|
|
} else if(tile->spawnNpc) {
|
|
map.removeSpawnNpcInternal(tile);
|
|
delete tile->spawnNpc;
|
|
}
|
|
tile->spawnNpc = spawn_npc_iter->second;
|
|
|
|
map.addSpawnNpc(tile);
|
|
}
|
|
|
|
g_gui.DestroyLoadBar();
|
|
|
|
map.setWidth(newsize_x);
|
|
map.setHeight(newsize_y);
|
|
g_gui.PopupDialog("Success", "Map imported successfully, " + i2ws(discarded_tiles) + " tiles were discarded as invalid.", wxOK);
|
|
|
|
g_gui.RefreshPalettes();
|
|
g_gui.FitViewToMap();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Editor::borderizeSelection()
|
|
{
|
|
if(selection.size() == 0) {
|
|
g_gui.SetStatusText("No items selected. Can't borderize.");
|
|
}
|
|
|
|
Action* action = actionQueue->createAction(ACTION_BORDERIZE);
|
|
for(Tile* tile : selection) {
|
|
Tile* newTile = tile->deepCopy(map);
|
|
newTile->borderize(&map);
|
|
newTile->select();
|
|
action->addChange(newd Change(newTile));
|
|
}
|
|
addAction(action);
|
|
}
|
|
|
|
void Editor::borderizeMap(bool showdialog)
|
|
{
|
|
if(showdialog) {
|
|
g_gui.CreateLoadBar("Borderizing map...");
|
|
}
|
|
|
|
uint64_t tiles_done = 0;
|
|
for(TileLocation* tileLocation : map) {
|
|
if(showdialog && tiles_done % 4096 == 0) {
|
|
g_gui.SetLoadDone(static_cast<int32_t>(tiles_done / double(map.tilecount) * 100.0));
|
|
}
|
|
|
|
Tile* tile = tileLocation->get();
|
|
ASSERT(tile);
|
|
|
|
tile->borderize(&map);
|
|
++tiles_done;
|
|
}
|
|
|
|
if(showdialog) {
|
|
g_gui.DestroyLoadBar();
|
|
}
|
|
}
|
|
|
|
void Editor::randomizeSelection()
|
|
{
|
|
if(selection.size() == 0) {
|
|
g_gui.SetStatusText("No items selected. Can't randomize.");
|
|
}
|
|
|
|
Action* action = actionQueue->createAction(ACTION_RANDOMIZE);
|
|
for(Tile* tile : selection) {
|
|
Tile* newTile = tile->deepCopy(map);
|
|
GroundBrush* groundBrush = newTile->getGroundBrush();
|
|
if(groundBrush && groundBrush->isReRandomizable()) {
|
|
groundBrush->draw(&map, newTile, nullptr);
|
|
|
|
Item* oldGround = tile->ground;
|
|
Item* newGround = newTile->ground;
|
|
if(oldGround && newGround) {
|
|
newGround->setActionID(oldGround->getActionID());
|
|
newGround->setUniqueID(oldGround->getUniqueID());
|
|
}
|
|
|
|
newTile->select();
|
|
action->addChange(newd Change(newTile));
|
|
}
|
|
}
|
|
addAction(action);
|
|
}
|
|
|
|
void Editor::randomizeMap(bool showdialog)
|
|
{
|
|
if(showdialog) {
|
|
g_gui.CreateLoadBar("Randomizing map...");
|
|
}
|
|
|
|
uint64_t tiles_done = 0;
|
|
for(TileLocation* tileLocation : map) {
|
|
if(showdialog && tiles_done % 4096 == 0) {
|
|
g_gui.SetLoadDone(static_cast<int32_t>(tiles_done / double(map.tilecount) * 100.0));
|
|
}
|
|
|
|
Tile* tile = tileLocation->get();
|
|
ASSERT(tile);
|
|
|
|
GroundBrush* groundBrush = tile->getGroundBrush();
|
|
if(groundBrush) {
|
|
Item* oldGround = tile->ground;
|
|
|
|
uint16_t actionId, uniqueId;
|
|
if(oldGround) {
|
|
actionId = oldGround->getActionID();
|
|
uniqueId = oldGround->getUniqueID();
|
|
} else {
|
|
actionId = 0;
|
|
uniqueId = 0;
|
|
}
|
|
groundBrush->draw(&map, tile, nullptr);
|
|
|
|
Item* newGround = tile->ground;
|
|
if(newGround) {
|
|
newGround->setActionID(actionId);
|
|
newGround->setUniqueID(uniqueId);
|
|
}
|
|
tile->update();
|
|
}
|
|
++tiles_done;
|
|
}
|
|
|
|
if(showdialog) {
|
|
g_gui.DestroyLoadBar();
|
|
}
|
|
}
|
|
|
|
void Editor::clearInvalidHouseTiles(bool showdialog)
|
|
{
|
|
if(showdialog) {
|
|
g_gui.CreateLoadBar("Clearing invalid house tiles...");
|
|
}
|
|
|
|
Houses& houses = map.houses;
|
|
|
|
HouseMap::iterator iter = houses.begin();
|
|
while(iter != houses.end()) {
|
|
House* h = iter->second;
|
|
if(map.towns.getTown(h->townid) == nullptr) {
|
|
#ifdef __VISUALC__ // C++0x compliance to some degree :)
|
|
iter = houses.erase(iter);
|
|
#else // Bulky, slow way
|
|
HouseMap::iterator tmp_iter = iter;
|
|
++tmp_iter;
|
|
uint32_t next_key = 0;
|
|
if(tmp_iter != houses.end()) {
|
|
next_key = tmp_iter->first;
|
|
}
|
|
houses.erase(iter);
|
|
if(next_key != 0) {
|
|
iter = houses.find(next_key);
|
|
} else {
|
|
iter = houses.end();
|
|
}
|
|
#endif
|
|
} else {
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
uint64_t tiles_done = 0;
|
|
for(MapIterator map_iter = map.begin(); map_iter != map.end(); ++map_iter) {
|
|
if(showdialog && tiles_done % 4096 == 0) {
|
|
g_gui.SetLoadDone(int(tiles_done / double(map.tilecount) * 100.0));
|
|
}
|
|
|
|
Tile* tile = (*map_iter)->get();
|
|
ASSERT(tile);
|
|
if(tile->isHouseTile()) {
|
|
if(houses.getHouse(tile->getHouseID()) == nullptr) {
|
|
tile->setHouse(nullptr);
|
|
}
|
|
}
|
|
++tiles_done;
|
|
}
|
|
|
|
if(showdialog) {
|
|
g_gui.DestroyLoadBar();
|
|
}
|
|
}
|
|
|
|
void Editor::clearModifiedTileState(bool showdialog)
|
|
{
|
|
if(showdialog) {
|
|
g_gui.CreateLoadBar("Clearing modified state from all tiles...");
|
|
}
|
|
|
|
uint64_t tiles_done = 0;
|
|
for(MapIterator map_iter = map.begin(); map_iter != map.end(); ++map_iter) {
|
|
if(showdialog && tiles_done % 4096 == 0) {
|
|
g_gui.SetLoadDone(int(tiles_done / double(map.tilecount) * 100.0));
|
|
}
|
|
|
|
Tile* tile = (*map_iter)->get();
|
|
ASSERT(tile);
|
|
tile->unmodify();
|
|
++tiles_done;
|
|
}
|
|
|
|
if(showdialog) {
|
|
g_gui.DestroyLoadBar();
|
|
}
|
|
}
|
|
|
|
void Editor::moveSelection(Position offset)
|
|
{
|
|
BatchAction* batchAction = actionQueue->createBatch(ACTION_MOVE); // Our saved action batch, for undo!
|
|
Action* action;
|
|
|
|
// Remove tiles from the map
|
|
action = actionQueue->createAction(batchAction); // Our action!
|
|
bool doborders = false;
|
|
TileSet tmp_storage;
|
|
|
|
// Update the tiles with the newd positions
|
|
for(TileSet::iterator it = selection.begin(); it != selection.end(); ++it) {
|
|
// First we get the old tile and it's position
|
|
Tile* tile = (*it);
|
|
//const Position pos = tile->getPosition();
|
|
|
|
// Create the duplicate source tile, which will replace the old one later
|
|
Tile* old_src_tile = tile;
|
|
Tile* new_src_tile;
|
|
|
|
new_src_tile = old_src_tile->deepCopy(map);
|
|
|
|
Tile* tmp_storage_tile = map.allocator(tile->getLocation());
|
|
|
|
// Get all the selected items from the NEW source tile and iterate through them
|
|
// This transfers ownership to the temporary tile
|
|
ItemVector tile_selection = new_src_tile->popSelectedItems();
|
|
for(ItemVector::iterator iit = tile_selection.begin(); iit != tile_selection.end(); iit++) {
|
|
// Add the copied item to the newd destination tile,
|
|
Item* item = (*iit);
|
|
tmp_storage_tile->addItem(item);
|
|
}
|
|
// Move monster spawns
|
|
if(new_src_tile->spawnMonster && new_src_tile->spawnMonster->isSelected()) {
|
|
tmp_storage_tile->spawnMonster = new_src_tile->spawnMonster;
|
|
new_src_tile->spawnMonster = nullptr;
|
|
}
|
|
// Move monster
|
|
if(new_src_tile->monster && new_src_tile->monster->isSelected()) {
|
|
tmp_storage_tile->monster = new_src_tile->monster;
|
|
new_src_tile->monster = nullptr;
|
|
}
|
|
// Move npc
|
|
if(new_src_tile->npc && new_src_tile->npc->isSelected()) {
|
|
tmp_storage_tile->npc = new_src_tile->npc;
|
|
new_src_tile->npc = nullptr;
|
|
}
|
|
// Move npc spawns
|
|
if(new_src_tile->spawnNpc && new_src_tile->spawnNpc->isSelected()) {
|
|
tmp_storage_tile->spawnNpc = new_src_tile->spawnNpc;
|
|
new_src_tile->spawnNpc = nullptr;
|
|
}
|
|
|
|
// Move house data & tile status if ground is transferred
|
|
if(tmp_storage_tile->ground) {
|
|
tmp_storage_tile->house_id = new_src_tile->house_id;
|
|
new_src_tile->house_id = 0;
|
|
tmp_storage_tile->setMapFlags(new_src_tile->getMapFlags());
|
|
new_src_tile->setMapFlags(TILESTATE_NONE);
|
|
doborders = true;
|
|
}
|
|
|
|
tmp_storage.insert(tmp_storage_tile);
|
|
// Add the tile copy to the action
|
|
action->addChange(newd Change(new_src_tile));
|
|
}
|
|
// Commit changes to map
|
|
batchAction->addAndCommitAction(action);
|
|
|
|
// Remove old borders (and create some newd?)
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC) &&
|
|
g_settings.getInteger(Config::BORDERIZE_DRAG) &&
|
|
selection.size() < size_t(g_settings.getInteger(Config::BORDERIZE_DRAG_THRESHOLD))) {
|
|
action = actionQueue->createAction(batchAction);
|
|
TileList borderize_tiles;
|
|
// Go through all modified (selected) tiles (might be slow)
|
|
for(TileSet::iterator it = tmp_storage.begin(); it != tmp_storage.end(); ++it) {
|
|
Position pos = (*it)->getPosition();
|
|
// Go through all neighbours
|
|
Tile* t;
|
|
t = map.getTile(pos.x , pos.y , pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x-1, pos.y-1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x , pos.y-1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x+1, pos.y-1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x-1, pos.y , pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x+1, pos.y , pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x-1, pos.y+1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x , pos.y+1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
t = map.getTile(pos.x+1, pos.y+1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t);}
|
|
}
|
|
// Remove duplicates
|
|
borderize_tiles.sort();
|
|
borderize_tiles.unique();
|
|
// Do le borders!
|
|
for(TileList::iterator it = borderize_tiles.begin(); it != borderize_tiles.end(); ++it) {
|
|
Tile* tile = *it;
|
|
Tile* new_tile = (*it)->deepCopy(map);
|
|
if(doborders) new_tile->borderize(&map);
|
|
new_tile->wallize(&map);
|
|
new_tile->tableize(&map);
|
|
new_tile->carpetize(&map);
|
|
if(tile->ground && tile->ground->isSelected()) new_tile->selectGround();
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
// Commit changes to map
|
|
batchAction->addAndCommitAction(action);
|
|
}
|
|
|
|
// New action for adding the destination tiles
|
|
action = actionQueue->createAction(batchAction);
|
|
for(TileSet::iterator it = tmp_storage.begin(); it != tmp_storage.end(); ++it) {
|
|
Tile* tile = (*it);
|
|
const Position old_pos = tile->getPosition();
|
|
Position new_pos;
|
|
|
|
new_pos = old_pos - offset;
|
|
|
|
if(new_pos.z < 0 && new_pos.z > MAP_MAX_LAYER) {
|
|
delete tile;
|
|
continue;
|
|
}
|
|
// Create the duplicate dest tile, which will replace the old one later
|
|
TileLocation* location = map.createTileL(new_pos);
|
|
Tile* old_dest_tile = location->get();
|
|
Tile* new_dest_tile = nullptr;
|
|
|
|
if(g_settings.getInteger(Config::MERGE_MOVE) || !tile->ground) {
|
|
// Move items
|
|
if(old_dest_tile) {
|
|
new_dest_tile = old_dest_tile->deepCopy(map);
|
|
} else {
|
|
new_dest_tile = map.allocator(location);
|
|
}
|
|
new_dest_tile->merge(tile);
|
|
delete tile;
|
|
} else {
|
|
// Replace tile instead of just merge
|
|
tile->setLocation(location);
|
|
new_dest_tile = tile;
|
|
}
|
|
|
|
action->addChange(newd Change(new_dest_tile));
|
|
}
|
|
|
|
// Commit changes to the map
|
|
batchAction->addAndCommitAction(action);
|
|
|
|
// Create borders
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC) &&
|
|
g_settings.getInteger(Config::BORDERIZE_DRAG) &&
|
|
selection.size() < size_t(g_settings.getInteger(Config::BORDERIZE_DRAG_THRESHOLD))) {
|
|
action = actionQueue->createAction(batchAction);
|
|
TileList borderize_tiles;
|
|
// Go through all modified (selected) tiles (might be slow)
|
|
for(TileSet::iterator it = selection.begin(); it != selection.end(); it++) {
|
|
bool add_me = false; // If this tile is touched
|
|
Position pos = (*it)->getPosition();
|
|
// Go through all neighbours
|
|
Tile* t;
|
|
t = map.getTile(pos.x-1, pos.y-1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x-1, pos.y-1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x , pos.y-1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x+1, pos.y-1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x-1, pos.y , pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x+1, pos.y , pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x-1, pos.y+1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x , pos.y+1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
t = map.getTile(pos.x+1, pos.y+1, pos.z); if(t && !t->isSelected()) {borderize_tiles.push_back(t); add_me = true;}
|
|
if(add_me) borderize_tiles.push_back(*it);
|
|
}
|
|
// Remove duplicates
|
|
borderize_tiles.sort();
|
|
borderize_tiles.unique();
|
|
// Do le borders!
|
|
for(TileList::iterator it = borderize_tiles.begin(); it != borderize_tiles.end(); it++) {
|
|
Tile* tile = *it;
|
|
if(tile->ground) {
|
|
if(tile->ground->getGroundBrush()) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
|
|
if(doborders)
|
|
new_tile->borderize(&map);
|
|
|
|
new_tile->wallize(&map);
|
|
new_tile->tableize(&map);
|
|
new_tile->carpetize(&map);
|
|
if(tile->ground->isSelected())
|
|
new_tile->selectGround();
|
|
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
}
|
|
// Commit changes to map
|
|
batchAction->addAndCommitAction(action);
|
|
}
|
|
|
|
// Store the action for undo
|
|
addBatch(batchAction);
|
|
selection.updateSelectionCount();
|
|
}
|
|
|
|
void Editor::destroySelection()
|
|
{
|
|
if(selection.size() == 0) {
|
|
g_gui.SetStatusText("No selected items to delete.");
|
|
} else {
|
|
int tile_count = 0;
|
|
int item_count = 0;
|
|
PositionList tilestoborder;
|
|
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DELETE_TILES);
|
|
Action* action = actionQueue->createAction(batch);
|
|
|
|
for(TileSet::iterator it = selection.begin(); it != selection.end(); ++it) {
|
|
tile_count++;
|
|
|
|
Tile* tile = *it;
|
|
Tile* newtile = tile->deepCopy(map);
|
|
|
|
ItemVector tile_selection = newtile->popSelectedItems();
|
|
for(ItemVector::iterator iit = tile_selection.begin(); iit != tile_selection.end(); ++iit) {
|
|
++item_count;
|
|
// Delete the items from the tile
|
|
delete *iit;
|
|
}
|
|
// Monster
|
|
if(newtile->monster && newtile->monster->isSelected()) {
|
|
delete newtile->monster;
|
|
newtile->monster = nullptr;
|
|
}
|
|
|
|
if(newtile->spawnMonster && newtile->spawnMonster->isSelected()) {
|
|
delete newtile->spawnMonster;
|
|
newtile->spawnMonster = nullptr;
|
|
}
|
|
// Npc
|
|
if(newtile->npc && newtile->npc->isSelected()) {
|
|
delete newtile->npc;
|
|
newtile->npc = nullptr;
|
|
}
|
|
|
|
if(newtile->spawnNpc && newtile->spawnNpc->isSelected()) {
|
|
delete newtile->spawnNpc;
|
|
newtile->spawnNpc = nullptr;
|
|
}
|
|
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC)) {
|
|
for(int y = -1; y <= 1; y++) {
|
|
for(int x = -1; x <= 1; x++) {
|
|
tilestoborder.push_back(
|
|
Position(tile->getPosition().x + x,
|
|
tile->getPosition().y + y,
|
|
tile->getPosition().z));
|
|
}
|
|
}
|
|
}
|
|
action->addChange(newd Change(newtile));
|
|
}
|
|
|
|
batch->addAndCommitAction(action);
|
|
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC)) {
|
|
// Remove duplicates
|
|
tilestoborder.sort();
|
|
tilestoborder.unique();
|
|
|
|
action = actionQueue->createAction(batch);
|
|
for(PositionList::iterator it = tilestoborder.begin(); it != tilestoborder.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
new_tile->borderize(&map);
|
|
new_tile->wallize(&map);
|
|
new_tile->tableize(&map);
|
|
new_tile->carpetize(&map);
|
|
action->addChange(newd Change(new_tile));
|
|
} else {
|
|
Tile* new_tile = map.allocator(location);
|
|
new_tile->borderize(&map);
|
|
if(new_tile->size()) {
|
|
action->addChange(newd Change(new_tile));
|
|
} else {
|
|
delete new_tile;
|
|
}
|
|
}
|
|
}
|
|
|
|
batch->addAndCommitAction(action);
|
|
}
|
|
|
|
addBatch(batch);
|
|
wxString ss;
|
|
ss << "Deleted " << tile_count << " tile" << (tile_count > 1 ? "s" : "") << " (" << item_count << " item" << (item_count > 1? "s" : "") << ")";
|
|
g_gui.SetStatusText(ss);
|
|
}
|
|
}
|
|
|
|
// Macro to avoid useless code repetition
|
|
void doSurroundingBorders(DoodadBrush* doodad_brush, PositionList& tilestoborder, Tile* buffer_tile, Tile* new_tile)
|
|
{
|
|
if(doodad_brush->doNewBorders() && g_settings.getInteger(Config::USE_AUTOMAGIC)) {
|
|
tilestoborder.push_back(Position(new_tile->getPosition().x, new_tile->getPosition().y, new_tile->getPosition().z));
|
|
if(buffer_tile->hasGround()) {
|
|
for(int y = -1; y <= 1; y++) {
|
|
for(int x = -1; x <= 1; x++) {
|
|
tilestoborder.push_back(Position(new_tile->getPosition().x + x, new_tile->getPosition().y + y, new_tile->getPosition().z));
|
|
}
|
|
}
|
|
} else if(buffer_tile->hasWall()) {
|
|
tilestoborder.push_back(Position(new_tile->getPosition().x, new_tile->getPosition().y-1, new_tile->getPosition().z));
|
|
tilestoborder.push_back(Position(new_tile->getPosition().x-1, new_tile->getPosition().y, new_tile->getPosition().z));
|
|
tilestoborder.push_back(Position(new_tile->getPosition().x+1, new_tile->getPosition().y, new_tile->getPosition().z));
|
|
tilestoborder.push_back(Position(new_tile->getPosition().x, new_tile->getPosition().y+1, new_tile->getPosition().z));
|
|
}
|
|
}
|
|
}
|
|
|
|
void removeDuplicateWalls(Tile* buffer, Tile* tile)
|
|
{
|
|
for(ItemVector::const_iterator iter = buffer->items.begin(); iter != buffer->items.end(); ++iter) {
|
|
if((*iter)->getWallBrush()) {
|
|
tile->cleanWalls((*iter)->getWallBrush());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::drawInternal(Position offset, bool alt, bool dodraw)
|
|
{
|
|
Brush* brush = g_gui.GetCurrentBrush();
|
|
if(!brush)
|
|
return;
|
|
|
|
if(brush->isDoodad()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
BaseMap* buffer_map = g_gui.doodad_buffer_map;
|
|
|
|
Position delta_pos = offset - Position(0x8000, 0x8000, 0x8);
|
|
PositionList tilestoborder;
|
|
|
|
for(MapIterator it = buffer_map->begin(); it != buffer_map->end(); ++it) {
|
|
Tile* buffer_tile = (*it)->get();
|
|
Position pos = buffer_tile->getPosition() + delta_pos;
|
|
if(!pos.isValid()) {
|
|
continue;
|
|
}
|
|
|
|
TileLocation* location = map.createTileL(pos);
|
|
Tile* tile = location->get();
|
|
DoodadBrush* doodad_brush = brush->asDoodad();
|
|
|
|
if(doodad_brush->placeOnBlocking() || alt) {
|
|
if(tile) {
|
|
bool place = true;
|
|
if(!doodad_brush->placeOnDuplicate() && !alt) {
|
|
for(ItemVector::const_iterator iter = tile->items.begin(); iter != tile->items.end(); ++iter) {
|
|
if(doodad_brush->ownsItem(*iter)) {
|
|
place = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(place) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
removeDuplicateWalls(buffer_tile, new_tile);
|
|
doSurroundingBorders(doodad_brush, tilestoborder, buffer_tile, new_tile);
|
|
new_tile->merge(buffer_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
} else {
|
|
Tile* new_tile = map.allocator(location);
|
|
removeDuplicateWalls(buffer_tile, new_tile);
|
|
doSurroundingBorders(doodad_brush, tilestoborder, buffer_tile, new_tile);
|
|
new_tile->merge(buffer_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
} else {
|
|
if(tile && !tile->isBlocking()) {
|
|
bool place = true;
|
|
if(!doodad_brush->placeOnDuplicate() && !alt) {
|
|
for(ItemVector::const_iterator iter = tile->items.begin(); iter != tile->items.end(); ++iter) {
|
|
if(doodad_brush->ownsItem(*iter)) {
|
|
place = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(place) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
removeDuplicateWalls(buffer_tile, new_tile);
|
|
doSurroundingBorders(doodad_brush, tilestoborder, buffer_tile, new_tile);
|
|
new_tile->merge(buffer_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
batch->addAndCommitAction(action);
|
|
|
|
if(tilestoborder.size() > 0) {
|
|
Action* action = actionQueue->createAction(batch);
|
|
|
|
// Remove duplicates
|
|
tilestoborder.sort();
|
|
tilestoborder.unique();
|
|
|
|
for(PositionList::const_iterator it = tilestoborder.begin(); it != tilestoborder.end(); ++it) {
|
|
Tile* tile = map.getTile(*it);
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
new_tile->borderize(&map);
|
|
new_tile->wallize(&map);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
batch->addAndCommitAction(action);
|
|
}
|
|
addBatch(batch, 2);
|
|
} else if(brush->isHouseExit()) {
|
|
HouseExitBrush* house_exit_brush = brush->asHouseExit();
|
|
if(!house_exit_brush->canDraw(&map, offset))
|
|
return;
|
|
|
|
House* house = map.houses.getHouse(house_exit_brush->getHouseID());
|
|
if(!house)
|
|
return;
|
|
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
action->addChange(Change::Create(house, offset));
|
|
batch->addAndCommitAction(action);
|
|
addBatch(batch, 2);
|
|
} else if(brush->isWaypoint()) {
|
|
WaypointBrush* waypoint_brush = brush->asWaypoint();
|
|
if(!waypoint_brush->canDraw(&map, offset))
|
|
return;
|
|
|
|
Waypoint* waypoint = map.waypoints.getWaypoint(waypoint_brush->getWaypoint());
|
|
if(!waypoint || waypoint->pos == offset)
|
|
return;
|
|
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
action->addChange(Change::Create(waypoint, offset));
|
|
batch->addAndCommitAction(action);
|
|
addBatch(batch, 2);
|
|
} else if(brush->isWall()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
// This will only occur with a size 0, when clicking on a tile (not drawing)
|
|
Tile* tile = map.getTile(offset);
|
|
Tile* new_tile = nullptr;
|
|
if(tile) {
|
|
new_tile = tile->deepCopy(map);
|
|
} else {
|
|
new_tile = map.allocator(map.createTileL(offset));
|
|
}
|
|
|
|
if(dodraw) {
|
|
bool b = true;
|
|
brush->asWall()->draw(&map, new_tile, &b);
|
|
} else {
|
|
brush->asWall()->undraw(&map, new_tile);
|
|
}
|
|
action->addChange(newd Change(new_tile));
|
|
batch->addAndCommitAction(action);
|
|
addBatch(batch, 2);
|
|
} else if(brush->isSpawnMonster() || brush->isMonster()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
|
|
Tile* tile = map.getTile(offset);
|
|
Tile* new_tile = nullptr;
|
|
if(tile) {
|
|
new_tile = tile->deepCopy(map);
|
|
} else {
|
|
new_tile = map.allocator(map.createTileL(offset));
|
|
}
|
|
int param;
|
|
if(brush->isMonster()) {
|
|
param = g_gui.GetSpawnMonsterTime();
|
|
} else {
|
|
param = g_gui.GetBrushSize();
|
|
}
|
|
if(dodraw) {
|
|
brush->draw(&map, new_tile, ¶m);
|
|
} else {
|
|
brush->undraw(&map, new_tile);
|
|
}
|
|
action->addChange(newd Change(new_tile));
|
|
batch->addAndCommitAction(action);
|
|
addBatch(batch, 2);
|
|
} else if(brush->isSpawnNpc() || brush->isNpc()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
|
|
Tile* tile = map.getTile(offset);
|
|
Tile* new_tile = nullptr;
|
|
if(tile) {
|
|
new_tile = tile->deepCopy(map);
|
|
} else {
|
|
new_tile = map.allocator(map.createTileL(offset));
|
|
}
|
|
int param;
|
|
if(brush->isNpc()) {
|
|
param = g_gui.GetSpawnNpcTime();
|
|
} else {
|
|
param = g_gui.GetBrushSize();
|
|
}
|
|
if(dodraw) {
|
|
brush->draw(&map, new_tile, ¶m);
|
|
} else {
|
|
brush->undraw(&map, new_tile);
|
|
}
|
|
action->addChange(newd Change(new_tile));
|
|
batch->addAndCommitAction(action);
|
|
addBatch(batch, 2);
|
|
}
|
|
}
|
|
|
|
void Editor::drawInternal(const PositionVector& tilestodraw, bool alt, bool dodraw)
|
|
{
|
|
Brush* brush = g_gui.GetCurrentBrush();
|
|
if(!brush)
|
|
return;
|
|
|
|
#ifdef __DEBUG__
|
|
if(brush->isGround() || brush->isWall()) {
|
|
// Wrong function, end call
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
Action* action = actionQueue->createAction(ACTION_DRAW);
|
|
|
|
if(brush->isOptionalBorder()) {
|
|
// We actually need to do borders, but on the same tiles we draw to
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
if(dodraw) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
brush->draw(&map, new_tile);
|
|
new_tile->borderize(&map);
|
|
action->addChange(newd Change(new_tile));
|
|
} else if(!dodraw && tile->hasOptionalBorder()) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
brush->undraw(&map, new_tile);
|
|
new_tile->borderize(&map);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
brush->draw(&map, new_tile);
|
|
new_tile->borderize(&map);
|
|
if(new_tile->size() == 0) {
|
|
delete new_tile;
|
|
continue;
|
|
}
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
} else {
|
|
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
if(dodraw) {
|
|
brush->draw(&map, new_tile, &alt);
|
|
} else {
|
|
brush->undraw(&map, new_tile);
|
|
}
|
|
action->addChange(newd Change(new_tile));
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
brush->draw(&map, new_tile, &alt);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
}
|
|
addAction(action, 2);
|
|
}
|
|
|
|
|
|
void Editor::drawInternal(const PositionVector& tilestodraw, PositionVector& tilestoborder, bool alt, bool dodraw)
|
|
{
|
|
Brush* brush = g_gui.GetCurrentBrush();
|
|
if(!brush)
|
|
return;
|
|
|
|
if(brush->isGround() || brush->isEraser()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC)) {
|
|
new_tile->cleanBorders();
|
|
}
|
|
if(dodraw)
|
|
if(brush->isGround() && alt) {
|
|
std::pair<bool, GroundBrush*> param;
|
|
if(replace_brush) {
|
|
param.first = false;
|
|
param.second = replace_brush;
|
|
} else {
|
|
param.first = true;
|
|
param.second = nullptr;
|
|
}
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile, ¶m);
|
|
} else {
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile, nullptr);
|
|
}
|
|
else {
|
|
g_gui.GetCurrentBrush()->undraw(&map, new_tile);
|
|
tilestoborder.push_back(*it);
|
|
}
|
|
action->addChange(newd Change(new_tile));
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
if(brush->isGround() && alt) {
|
|
std::pair<bool, GroundBrush*> param;
|
|
if(replace_brush) {
|
|
param.first = false;
|
|
param.second = replace_brush;
|
|
} else {
|
|
param.first = true;
|
|
param.second = nullptr;
|
|
}
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile, ¶m);
|
|
} else {
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile, nullptr);
|
|
}
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
|
|
// Commit changes to map
|
|
batch->addAndCommitAction(action);
|
|
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC)) {
|
|
// Do borders!
|
|
action = actionQueue->createAction(batch);
|
|
for(PositionVector::const_iterator it = tilestoborder.begin(); it != tilestoborder.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
if(brush->isEraser()) {
|
|
new_tile->wallize(&map);
|
|
new_tile->tableize(&map);
|
|
new_tile->carpetize(&map);
|
|
}
|
|
new_tile->borderize(&map);
|
|
action->addChange(newd Change(new_tile));
|
|
} else {
|
|
Tile* new_tile = map.allocator(location);
|
|
if(brush->isEraser()) {
|
|
// There are no carpets/tables/walls on empty tiles...
|
|
//new_tile->wallize(map);
|
|
//new_tile->tableize(map);
|
|
//new_tile->carpetize(map);
|
|
}
|
|
new_tile->borderize(&map);
|
|
if(new_tile->size() > 0) {
|
|
action->addChange(newd Change(new_tile));
|
|
} else {
|
|
delete new_tile;
|
|
}
|
|
}
|
|
}
|
|
batch->addAndCommitAction(action);
|
|
}
|
|
|
|
addBatch(batch, 2);
|
|
} else if(brush->isTable() || brush->isCarpet()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
if(dodraw)
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile, nullptr);
|
|
else
|
|
g_gui.GetCurrentBrush()->undraw(&map, new_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile, nullptr);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
|
|
// Commit changes to map
|
|
batch->addAndCommitAction(action);
|
|
|
|
// Do borders!
|
|
action = actionQueue->createAction(batch);
|
|
for(PositionVector::const_iterator it = tilestoborder.begin(); it != tilestoborder.end(); ++it) {
|
|
Tile* tile = map.getTile(*it);
|
|
if(brush->isTable()) {
|
|
if(tile && tile->hasTable()) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
new_tile->tableize(&map);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
} else if(brush->isCarpet()) {
|
|
if(tile && tile->hasCarpet()) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
new_tile->carpetize(&map);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
}
|
|
batch->addAndCommitAction(action);
|
|
|
|
addBatch(batch, 2);
|
|
} else if(brush->isWall()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
|
|
if(alt && dodraw) {
|
|
// This is exempt from USE_AUTOMAGIC
|
|
g_gui.doodad_buffer_map->clear();
|
|
BaseMap* draw_map = g_gui.doodad_buffer_map;
|
|
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
new_tile->cleanWalls(brush->isWall());
|
|
g_gui.GetCurrentBrush()->draw(draw_map, new_tile);
|
|
draw_map->setTile(*it, new_tile, true);
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
g_gui.GetCurrentBrush()->draw(draw_map, new_tile);
|
|
draw_map->setTile(*it, new_tile, true);
|
|
}
|
|
}
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
// Get the correct tiles from the draw map instead of the editor map
|
|
Tile* tile = draw_map->getTile(*it);
|
|
if(tile) {
|
|
tile->wallize(draw_map);
|
|
action->addChange(newd Change(tile));
|
|
}
|
|
}
|
|
draw_map->clear(false);
|
|
// Commit
|
|
batch->addAndCommitAction(action);
|
|
} else {
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
// Wall cleaning is exempt from automagic
|
|
new_tile->cleanWalls(brush->isWall());
|
|
if(dodraw)
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile);
|
|
else
|
|
g_gui.GetCurrentBrush()->undraw(&map, new_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
|
|
// Commit changes to map
|
|
batch->addAndCommitAction(action);
|
|
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC)) {
|
|
// Do borders!
|
|
action = actionQueue->createAction(batch);
|
|
for(PositionVector::const_iterator it = tilestoborder.begin(); it != tilestoborder.end(); ++it) {
|
|
Tile* tile = map.getTile(*it);
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
new_tile->wallize(&map);
|
|
//if(*tile == *new_tile) delete new_tile;
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
batch->addAndCommitAction(action);
|
|
}
|
|
}
|
|
|
|
actionQueue->addBatch(batch, 2);
|
|
} else if(brush->isDoor()) {
|
|
BatchAction* batch = actionQueue->createBatch(ACTION_DRAW);
|
|
Action* action = actionQueue->createAction(batch);
|
|
DoorBrush* door_brush = brush->asDoor();
|
|
|
|
// Loop is kind of redundant since there will only ever be one index.
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
// Wall cleaning is exempt from automagic
|
|
if(brush->isWall())
|
|
new_tile->cleanWalls(brush->asWall());
|
|
if(dodraw)
|
|
door_brush->draw(&map, new_tile, &alt);
|
|
else
|
|
door_brush->undraw(&map, new_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
door_brush->draw(&map, new_tile, &alt);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
|
|
// Commit changes to map
|
|
batch->addAndCommitAction(action);
|
|
|
|
if(g_settings.getInteger(Config::USE_AUTOMAGIC)) {
|
|
// Do borders!
|
|
action = actionQueue->createAction(batch);
|
|
for(PositionVector::const_iterator it = tilestoborder.begin(); it != tilestoborder.end(); ++it) {
|
|
Tile* tile = map.getTile(*it);
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
new_tile->wallize(&map);
|
|
//if(*tile == *new_tile) delete new_tile;
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
batch->addAndCommitAction(action);
|
|
}
|
|
|
|
addBatch(batch, 2);
|
|
} else {
|
|
Action* action = actionQueue->createAction(ACTION_DRAW);
|
|
for(PositionVector::const_iterator it = tilestodraw.begin(); it != tilestodraw.end(); ++it) {
|
|
TileLocation* location = map.createTileL(*it);
|
|
Tile* tile = location->get();
|
|
if(tile) {
|
|
Tile* new_tile = tile->deepCopy(map);
|
|
if(dodraw)
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile);
|
|
else
|
|
g_gui.GetCurrentBrush()->undraw(&map, new_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
} else if(dodraw) {
|
|
Tile* new_tile = map.allocator(location);
|
|
g_gui.GetCurrentBrush()->draw(&map, new_tile);
|
|
action->addChange(newd Change(new_tile));
|
|
}
|
|
}
|
|
addAction(action, 2);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Live!
|
|
|
|
bool Editor::IsLiveClient() const
|
|
{
|
|
return live_client != nullptr;
|
|
}
|
|
|
|
bool Editor::IsLiveServer() const
|
|
{
|
|
return live_server != nullptr;
|
|
}
|
|
|
|
bool Editor::IsLive() const
|
|
{
|
|
return IsLiveClient() || IsLiveServer();
|
|
}
|
|
|
|
bool Editor::IsLocal() const
|
|
{
|
|
return !IsLive();
|
|
}
|
|
|
|
LiveClient* Editor::GetLiveClient() const
|
|
{
|
|
return live_client;
|
|
}
|
|
|
|
LiveServer* Editor::GetLiveServer() const
|
|
{
|
|
return live_server;
|
|
}
|
|
|
|
LiveSocket& Editor::GetLive() const
|
|
{
|
|
if(live_server)
|
|
return *live_server;
|
|
return *live_client;
|
|
}
|
|
|
|
LiveServer* Editor::StartLiveServer()
|
|
{
|
|
ASSERT(IsLocal());
|
|
live_server = newd LiveServer(*this);
|
|
|
|
delete actionQueue;
|
|
actionQueue = newd NetworkedActionQueue(*this);
|
|
|
|
return live_server;
|
|
}
|
|
|
|
void Editor::BroadcastNodes(DirtyList& dirtyList)
|
|
{
|
|
if(IsLiveClient()) {
|
|
live_client->sendChanges(dirtyList);
|
|
} else {
|
|
live_server->broadcastNodes(dirtyList);
|
|
}
|
|
}
|
|
|
|
void Editor::CloseLiveServer()
|
|
{
|
|
ASSERT(IsLive());
|
|
if(live_client) {
|
|
live_client->close();
|
|
|
|
delete live_client;
|
|
live_client = nullptr;
|
|
}
|
|
|
|
if(live_server) {
|
|
live_server->close();
|
|
|
|
delete live_server;
|
|
live_server = nullptr;
|
|
|
|
delete actionQueue;
|
|
actionQueue = newd ActionQueue(*this);
|
|
}
|
|
|
|
NetworkConnection& connection = NetworkConnection::getInstance();
|
|
connection.stop();
|
|
}
|
|
|
|
void Editor::QueryNode(int ndx, int ndy, bool underground)
|
|
{
|
|
ASSERT(live_client);
|
|
live_client->queryNode(ndx, ndy, underground);
|
|
}
|
|
|
|
void Editor::SendNodeRequests()
|
|
{
|
|
if(live_client) {
|
|
live_client->sendNodeRequests();
|
|
}
|
|
}
|
|
|