tibia-rme/source/map.cpp
Hampus Joakim Nilsson 8dbc487ef2 Initial commit of RME 3.0.
Clean git repository since the old svn / git mess was pretty much
unrecoverable unfortunetly. I would have liked to keep the history
for posterity, but that proved to be a futile quest.
2012-06-29 16:45:30 +02:00

674 lines
16 KiB
C++

//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////
// $URL: http://svn.rebarp.se/svn/RME/trunk/source/map.hpp $
// $Id: map.hpp 310 2010-02-26 18:03:48Z admin $
#include "main.h"
#include "gui.h" // loadbar
#include "map.h"
#include <sstream>
Map::Map() : BaseMap(),
waypoints(*this),
width(512),
height(512),
houses(*this),
has_changed(false),
unnamed(false)
{
// Earliest version possible
// Caller is responsible for converting us to proper version
mapVersion.otbm = MAP_OTBM_1;
mapVersion.client = CLIENT_VERSION_NONE;
}
Map::~Map()
{
// ...
}
bool Map::open(const std::string file, bool showdialog)
{
if(file == filename)
return true; // Do not reopen ourselves!
tilecount = 0;
IOMapOTBM maploader(getVersion());
bool success = maploader.loadMap(*this, wxstr(file), showdialog);
mapVersion = maploader.version;
warnings = maploader.getWarnings();
error = maploader.getError();
if(!success)
return false;
has_changed = false;
wxFileName fn = wxstr(file);
filename = fn.GetFullPath().mb_str(wxConvUTF8);
name = fn.GetFullName().mb_str(wxConvUTF8);
// convert(getReplacementMapClassic(), true);
#if 0 // This will create a replacement map out of one of SO's template files
std::ofstream out("templateshit.cpp");
out << "\tConversionMap replacement_map;\n";
int count = 0;
out << "\tstd::vector<uint16_t> veckey, vecval;\n\n";
for(int x = 20; ; x += 2)
{
int y = 22;
Tile* old = getTile(x, y, 7);
if(old)
{
y -= 2;
Tile* new_ = getTile(x, y, 7);
if(new_)
{
if(old->ground || old->items.size())
{
out << "\tvecval.clear();\n";
if(new_->ground)
out << "\tvecval.push_back(" << new_->ground->getID() << ");\n";
for(ItemVector::iterator iter = new_->items.begin(); iter != new_->items.end(); ++iter)
out << "\tvecval.push_back(" << (*iter)->getID() << ");\n";
if(old->ground && old->items.empty()) // Single item
out << "\treplacement_map.stm[" << old->ground->getID() << "] = vecval;\n\n";
else if(old->ground == NULL && old->items.size() == 1) // Single item
out << "\treplacement_map.stm[" << old->items.front()->getID() << "] = vecval;\n\n";
else
{
// Many items
out << "\tveckey.clear();\n";
if(old->ground)
out << "\tveckey.push_back(" << old->ground->getID() << ");\n";
for(ItemVector::iterator iter = old->items.begin(); iter != old->items.end(); ++iter)
out << "\tveckey.push_back(" << (*iter)->getID() << ");\n";
out << "\tstd::sort(veckey.begin(), veckey.end());\n";
out << "\treplacement_map.mtm[veckey] = vecval;\n\n";
}
}
}
} else {
break;
}
}
out.close();
#endif
return true;
}
bool Map::convert(MapVersion to, bool showdialog)
{
if(mapVersion.client == to.client)
{
// Only OTBM version differs
// No changes necessary
mapVersion = to;
return true;
}
if(to.otbm == MAP_OTBM_4 && to.client < CLIENT_VERSION_850)
return false;
if(mapVersion.client >= CLIENT_VERSION_760 && to.client < CLIENT_VERSION_760)
convert(getReplacementMapFrom760To740(), showdialog);
if(mapVersion.client < CLIENT_VERSION_810 && to.client >= CLIENT_VERSION_810)
convert(getReplacementMapFrom800To810(), showdialog);
if(mapVersion.client == CLIENT_VERSION_854_BAD && to.client >= CLIENT_VERSION_854)
convert(getReplacementMapFrom854To854(), showdialog);
mapVersion = to;
return true;
}
bool Map::convert(const ConversionMap& rm, bool showdialog)
{
if(showdialog)
gui.CreateLoadBar(wxT("Converting map ..."));
uint64_t tiles_done = 0;
std::vector<uint16_t> id_list;
//std::ofstream conversions("converted_items.txt");
for(MapIterator miter = begin(); miter != end(); ++miter)
{
Tile* tile = (*miter)->get();
ASSERT(tile);
if(tile->size() == 0)
continue;
// id_list try MTM conversion
id_list.clear();
if(tile->ground)
id_list.push_back(tile->ground->getID());
for(ItemVector::const_iterator item_iter = tile->items.begin(); item_iter != tile->items.end(); ++item_iter)
if((*item_iter)->isBorder())
id_list.push_back((*item_iter)->getID());
std::sort(id_list.begin(), id_list.end());
ConversionMap::MTM::const_iterator cfmtm = rm.mtm.end();
while(id_list.size())
{
cfmtm = rm.mtm.find(id_list);
if(cfmtm != rm.mtm.end())
break;
id_list.pop_back();
}
// Keep track of how many items have been inserted at the bottom
size_t inserted_items = 0;
if(cfmtm != rm.mtm.end())
{
const std::vector<uint16_t>& v = cfmtm->first;
if(tile->ground && std::find(v.begin(), v.end(), tile->ground->getID()) != v.end())
{
delete tile->ground;
tile->ground = NULL;
}
for(ItemVector::iterator item_iter = tile->items.begin(); item_iter != tile->items.end(); )
{
if(std::find(v.begin(), v.end(), (*item_iter)->getID()) != v.end())
{
delete *item_iter;
item_iter = tile->items.erase(item_iter);
}
else
++item_iter;
}
const std::vector<uint16_t>& new_items = cfmtm->second;
for(std::vector<uint16_t>::const_iterator iit = new_items.begin(); iit != new_items.end(); ++iit)
{
Item* item = Item::Create(*iit);
if(item->isGroundTile())
tile->ground = item;
else
{
tile->items.insert(tile->items.begin(), item);
++inserted_items;
}
}
}
if(tile->ground)
{
ConversionMap::STM::const_iterator cfstm = rm.stm.find(tile->ground->getID());
if(cfstm != rm.stm.end())
{
uint16_t aid = tile->ground->getActionID();
uint16_t uid = tile->ground->getUniqueID();
delete tile->ground;
tile->ground = NULL;
const std::vector<uint16_t>& v = cfstm->second;
//conversions << "Converted " << tile->getX() << ":" << tile->getY() << ":" << tile->getZ() << " " << id << " -> ";
for(std::vector<uint16_t>::const_iterator iit = v.begin(); iit != v.end(); ++iit)
{
Item* item = Item::Create(*iit);
//conversions << *iit << " ";
if(item->isGroundTile())
{
item->setActionID(aid);
item->setUniqueID(uid);
tile->addItem(item);
}
else
{
tile->items.insert(tile->items.begin(), item);
++inserted_items;
}
}
//conversions << std::endl;
}
}
for(ItemVector::iterator replace_item_iter = tile->items.begin() + inserted_items; replace_item_iter != tile->items.end(); )
{
uint16_t id = (*replace_item_iter)->getID();
ConversionMap::STM::const_iterator cf = rm.stm.find(id);
if(cf != rm.stm.end())
{
//uint16_t aid = (*replace_item_iter)->getActionID();
//uint16_t uid = (*replace_item_iter)->getUniqueID();
delete *replace_item_iter;
replace_item_iter = tile->items.erase(replace_item_iter);
const std::vector<uint16_t>& v = cf->second;
for(std::vector<uint16_t>::const_iterator iit = v.begin(); iit != v.end(); ++iit)
{
replace_item_iter = tile->items.insert(replace_item_iter, Item::Create(*iit));
//conversions << "Converted " << tile->getX() << ":" << tile->getY() << ":" << tile->getZ() << " " << id << " -> " << *iit << std::endl;
++replace_item_iter;
}
}
else
++replace_item_iter;
}
++tiles_done;
if(showdialog && tiles_done % 0x10000 == 0) {
gui.SetLoadDone(int(tiles_done / double(getTileCount()) * 100.0));
}
}
if(showdialog)
gui.DestroyLoadBar();
return true;
}
void Map::cleanInvalidTiles(bool showdialog)
{
if(showdialog)
gui.CreateLoadBar(wxT("Removing invalid tiles..."));
uint64_t tiles_done = 0;
for(MapIterator miter = begin(); miter != end(); ++miter)
{
Tile* tile = (*miter)->get();
ASSERT(tile);
if(tile->size() == 0)
continue;
for(ItemVector::iterator item_iter = tile->items.begin(); item_iter != tile->items.end();)
{
if(item_db.typeExists((*item_iter)->getID()))
++item_iter;
else
{
delete *item_iter;
item_iter = tile->items.erase(item_iter);
}
}
++tiles_done;
if(showdialog && tiles_done % 0x10000 == 0) {
gui.SetLoadDone(int(tiles_done / double(getTileCount()) * 100.0));
}
}
if(showdialog)
gui.DestroyLoadBar();
}
MapVersion Map::getVersion() const
{
return mapVersion;
}
bool Map::hasChanged() const
{
return has_changed;
}
bool Map::doChange()
{
bool doupdate = !has_changed;
has_changed = true;
return doupdate;
}
bool Map::clearChanges()
{
bool doupdate = has_changed;
has_changed = false;
return doupdate;
}
bool Map::hasFile() const
{
return filename != "";
}
void Map::setWidth(int new_width)
{
if(new_width > 65000)
width = 65000;
else if(new_width < 64)
width = 64;
else
width = new_width;
}
void Map::setHeight(int new_height)
{
if(new_height > 65000)
height = 65000;
else if(new_height < 64)
height = 64;
else
height = new_height;
}
void Map::setMapDescription(const std::string& new_description)
{
description = new_description;
}
void Map::setHouseFilename(const std::string& new_housefile)
{
housefile = new_housefile;
unnamed = false;
}
void Map::setSpawnFilename(const std::string& new_spawnfile)
{
spawnfile = new_spawnfile;
unnamed = false;
}
bool Map::addSpawn(Tile* tile)
{
Spawn* spawn = tile->spawn;
if(spawn)
{
int z = tile->getZ();
int start_x = tile->getX() - spawn->getSize();
int start_y = tile->getY() - spawn->getSize();
int end_x = tile->getX() + spawn->getSize();
int end_y = tile->getY() + spawn->getSize();
for(int y = start_y; y <= end_y; ++y)
{
for(int x = start_x; x <= end_x; ++x)
{
TileLocation* ctile_loc = createTileL(x, y, z);
ctile_loc->increaseSpawnCount();
}
}
spawns.addSpawn(tile);
return true;
}
return false;
}
void Map::removeSpawnInternal(Tile* tile)
{
Spawn* spawn = tile->spawn;
ASSERT(spawn);
int z = tile->getZ();
int start_x = tile->getX() - spawn->getSize();
int start_y = tile->getY() - spawn->getSize();
int end_x = tile->getX() + spawn->getSize();
int end_y = tile->getY() + spawn->getSize();
for(int y = start_y; y <= end_y; ++y)
{
for(int x = start_x; x <= end_x; ++x)
{
TileLocation* ctile_loc = getTileL(x, y, z);
if(ctile_loc != NULL && ctile_loc->getSpawnCount() > 0)
ctile_loc->decreaseSpawnCount();
}
}
}
void Map::removeSpawn(Tile* tile)
{
if(tile->spawn)
{
removeSpawnInternal(tile);
spawns.removeSpawn(tile);
}
}
SpawnList Map::getSpawnList(Tile* where)
{
SpawnList list;
TileLocation* tile_loc = where->getLocation();
if(tile_loc)
{
if(tile_loc->getSpawnCount() > 0)
{
uint found = 0;
if(where->spawn)
{
++found;
list.push_back(where->spawn);
}
// Scans the border tiles in an expanding square around the original spawn
int z = where->getZ();
int start_x = where->getX() - 1, end_x = where->getX() + 1;
int start_y = where->getY() - 1, end_y = where->getY() + 1;
while(found != tile_loc->getSpawnCount())
{
for(int x = start_x; x <= end_x; ++x)
{
Tile* tile = getTile(x, start_y, z);
if(tile && tile->spawn)
{
list.push_back(tile->spawn);
++found;
}
tile = getTile(x, end_y, z);
if(tile && tile->spawn)
{
list.push_back(tile->spawn);
++found;
}
}
for(int y = start_y + 1; y < end_y; ++y)
{
Tile* tile = getTile(start_x, y, z);
if(tile && tile->spawn)
{
list.push_back(tile->spawn);
++found;
}
tile = getTile(end_x, y, z);
if(tile && tile->spawn)
{
list.push_back(tile->spawn);
++found;
}
}
--start_x, --start_y;
++end_x, ++end_y;
}
}
}
return list;
}
bool Map::exportMinimap(FileName filename, int floor /*= 7*/, bool displaydialog)
{
uint8_t* pic = NULL;
try
{
int min_x = 0x10000, min_y = 0x10000;
int max_x = 0x00000, max_y = 0x00000;
if(size() == 0)
return true;
for(MapIterator mit = begin();
mit != end();
++mit)
{
if((*mit)->get() == NULL || (*mit)->empty())
continue;
Position pos = (*mit)->getPosition();
if(pos.x < min_x)
min_x = pos.x;
if(pos.y < min_y)
min_y = pos.y;
if(pos.x > max_x)
max_x = pos.x;
if(pos.y > max_y)
max_y = pos.y;
}
int minimap_width = max_x - min_x+1;
int minimap_height = max_y - min_y+1;
pic = newd uint8_t[minimap_width*minimap_height]; // 1 byte per pixel
memset(pic, 0, minimap_width*minimap_height);
int tiles_iterated = 0;
for(MapIterator mit = begin();
mit != end();
++mit)
{
Tile* tile = (*mit)->get();
++tiles_iterated;
if(tiles_iterated % 8192 == 0 && displaydialog)
gui.SetLoadDone(int(tiles_iterated / double(tilecount) * 90.0));
if(tile->empty())
continue;
if(tile->getZ() != floor)
continue;
//std::cout << "Pixel : " << (tile->getY() - min_y) * width + (tile->getX() - min_x) << std::endl;
uint pixelpos = (tile->getY() - min_y) * minimap_width + (tile->getX() - min_x);
uint8_t& pixel = pic[pixelpos];
for(ItemVector::const_reverse_iterator item_iter = tile->items.rbegin();
item_iter != tile->items.rend();
++item_iter)
{
if((*item_iter)->getMiniMapColor())
{
pixel = (*item_iter)->getMiniMapColor();
break;
}
}
if(pixel == 0)
// check ground too
if(tile->hasGround())
pixel = tile->ground->getMiniMapColor();
}
// Create a file for writing
FileWriteHandle fh(nstr(filename.GetFullPath()));
if(fh.isOpen() == false)
{
delete[] pic;
return false;
}
// Store the magic number
fh.addRAW("BM");
// Store the file size
// We need to predict how large it will be
uint file_size =
14 // header
+40 // image data header
+256*4 // color palette
+((minimap_width + 3) / 4 * 4) * height; // pixels
fh.addU32(file_size);
// Two values reserved, must always be 0.
fh.addU16(0);
fh.addU16(0);
// Bitmapdata offset
fh.addU32(14 + 40 + 256*4);
// Header size
fh.addU32(40);
// Header width/height
fh.addU32(minimap_width);
fh.addU32(minimap_height);
// Color planes
fh.addU16(1);
// bits per pixel, OT map format is 8
fh.addU16(8);
// compression type, 0 is no compression
fh.addU32(0);
// image size, 0 is valid if we use no compression
fh.addU32(0);
// horizontal/vertical resolution in pixels / meter
fh.addU32(4000);
fh.addU32(4000);
// Number of colors
fh.addU32(256);
// Important colors, 0 is all
fh.addU32(0);
// Write the color palette
for(int i = 0; i < 256; ++i)
fh.addU32(uint32_t(minimap_color[i]));
// Bitmap width must be divisible by four, calculate how much padding we need
int padding = ((minimap_width & 3) != 0? 4-(minimap_width & 3) : 0);
// Bitmap rows are saved in reverse order
for(int y = minimap_height-1; y >= 0; --y)
{
fh.addRAW(pic + y*minimap_width, minimap_width);
for(int i = 0; i < padding; ++i)
{
fh.addU8(0);
}
if(y % 100 == 0 && displaydialog)
{
gui.SetLoadDone(90 + int((minimap_height-y) / double(minimap_height) * 10.0));
}
}
delete[] pic;
//fclose(file);
fh.close();
}
catch(...)
{
delete[] pic;
}
return true;
}