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
723 lines
18 KiB
C++
723 lines
18 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 "gui.h" // loadbar
|
|
|
|
#include "map.h"
|
|
|
|
#include <sstream>
|
|
|
|
Map::Map() : BaseMap(),
|
|
width(512),
|
|
height(512),
|
|
houses(*this),
|
|
has_changed(false),
|
|
unnamed(false),
|
|
waypoints(*this)
|
|
{
|
|
// 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)
|
|
{
|
|
if(file == filename)
|
|
return true; // Do not reopen ourselves!
|
|
|
|
tilecount = 0;
|
|
|
|
IOMapOTBM maploader(getVersion());
|
|
|
|
bool success = maploader.loadMap(*this, wxstr(file));
|
|
|
|
mapVersion = maploader.version;
|
|
|
|
warnings = maploader.getWarnings();
|
|
|
|
if(!success) {
|
|
error = maploader.getError();
|
|
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, GROUND_LAYER);
|
|
if(old) {
|
|
y -= 2;
|
|
Tile* new_ = getTile(x, y, GROUND_LAYER);
|
|
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 == nullptr && 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;
|
|
}
|
|
|
|
/* TODO
|
|
|
|
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)
|
|
g_gui.CreateLoadBar("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 = nullptr;
|
|
}
|
|
|
|
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 = nullptr;
|
|
|
|
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) {
|
|
g_gui.SetLoadDone(int(tiles_done / double(getTileCount()) * 100.0));
|
|
}
|
|
}
|
|
|
|
if(showdialog)
|
|
g_gui.DestroyLoadBar();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Map::cleanInvalidTiles(bool showdialog)
|
|
{
|
|
if(showdialog)
|
|
g_gui.CreateLoadBar("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(g_items.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) {
|
|
g_gui.SetLoadDone(int(tiles_done / double(getTileCount()) * 100.0));
|
|
}
|
|
}
|
|
|
|
if(showdialog)
|
|
g_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::setSpawnMonsterFilename(const std::string& new_spawnmonsterfile)
|
|
{
|
|
spawnmonsterfile = new_spawnmonsterfile;
|
|
unnamed = false;
|
|
}
|
|
|
|
void Map::setSpawnNpcFilename(const std::string& new_spawnnpcfile)
|
|
{
|
|
spawnnpcfile = new_spawnnpcfile;
|
|
unnamed = false;
|
|
}
|
|
|
|
bool Map::addSpawnMonster(Tile* tile)
|
|
{
|
|
SpawnMonster* spawnMonster = tile->spawnMonster;
|
|
if(spawnMonster) {
|
|
int z = tile->getZ();
|
|
int start_x = tile->getX() - spawnMonster->getSize();
|
|
int start_y = tile->getY() - spawnMonster->getSize();
|
|
int end_x = tile->getX() + spawnMonster->getSize();
|
|
int end_y = tile->getY() + spawnMonster->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();
|
|
}
|
|
}
|
|
spawnsMonster.addSpawnMonster(tile);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Map::removeSpawnMonsterInternal(Tile* tile)
|
|
{
|
|
SpawnMonster* spawnMonster = tile->spawnMonster;
|
|
ASSERT(spawnMonster);
|
|
|
|
int z = tile->getZ();
|
|
int start_x = tile->getX() - spawnMonster->getSize();
|
|
int start_y = tile->getY() - spawnMonster->getSize();
|
|
int end_x = tile->getX() + spawnMonster->getSize();
|
|
int end_y = tile->getY() + spawnMonster->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 != nullptr && ctile_loc->getSpawnMonsterCount() > 0)
|
|
ctile_loc->decreaseSpawnMonsterCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Map::removeSpawnMonster(Tile* tile)
|
|
{
|
|
if(tile->spawnMonster) {
|
|
removeSpawnMonsterInternal(tile);
|
|
spawnsMonster.removeSpawnMonster(tile);
|
|
}
|
|
}
|
|
|
|
SpawnMonsterList Map::getSpawnMonsterList(Tile* where)
|
|
{
|
|
SpawnMonsterList list;
|
|
TileLocation* tile_loc = where->getLocation();
|
|
if(tile_loc) {
|
|
if(tile_loc->getSpawnMonsterCount() > 0) {
|
|
uint32_t found = 0;
|
|
if(where->spawnMonster) {
|
|
++found;
|
|
list.push_back(where->spawnMonster);
|
|
}
|
|
|
|
// Scans the border tiles in an expanding square around the original monster 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->getSpawnMonsterCount()) {
|
|
for(int x = start_x; x <= end_x; ++x) {
|
|
Tile* tile = getTile(x, start_y, z);
|
|
if(tile && tile->spawnMonster) {
|
|
list.push_back(tile->spawnMonster);
|
|
++found;
|
|
}
|
|
tile = getTile(x, end_y, z);
|
|
if(tile && tile->spawnMonster) {
|
|
list.push_back(tile->spawnMonster);
|
|
++found;
|
|
}
|
|
}
|
|
|
|
for(int y = start_y + 1; y < end_y; ++y) {
|
|
Tile* tile = getTile(start_x, y, z);
|
|
if(tile && tile->spawnMonster) {
|
|
list.push_back(tile->spawnMonster);
|
|
++found;
|
|
}
|
|
tile = getTile(end_x, y, z);
|
|
if(tile && tile->spawnMonster) {
|
|
list.push_back(tile->spawnMonster);
|
|
++found;
|
|
}
|
|
}
|
|
--start_x, --start_y;
|
|
++end_x, ++end_y;
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
bool Map::addSpawnNpc(Tile* tile)
|
|
{
|
|
SpawnNpc* spawnNpc = tile->spawnNpc;
|
|
if(spawnNpc) {
|
|
int z = tile->getZ();
|
|
int start_x = tile->getX() - spawnNpc->getSize();
|
|
int start_y = tile->getY() - spawnNpc->getSize();
|
|
int end_x = tile->getX() + spawnNpc->getSize();
|
|
int end_y = tile->getY() + spawnNpc->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->increaseSpawnNpcCount();
|
|
}
|
|
}
|
|
spawnsNpc.addSpawnNpc(tile);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Map::removeSpawnNpcInternal(Tile* tile)
|
|
{
|
|
SpawnNpc* spawnNpc = tile->spawnNpc;
|
|
ASSERT(spawnNpc);
|
|
|
|
int z = tile->getZ();
|
|
int start_x = tile->getX() - spawnNpc->getSize();
|
|
int start_y = tile->getY() - spawnNpc->getSize();
|
|
int end_x = tile->getX() + spawnNpc->getSize();
|
|
int end_y = tile->getY() + spawnNpc->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 != nullptr && ctile_loc->getSpawnNpcCount() > 0)
|
|
ctile_loc->decreaseSpawnNpcCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Map::removeSpawnNpc(Tile* tile)
|
|
{
|
|
if(tile->spawnNpc) {
|
|
removeSpawnNpcInternal(tile);
|
|
spawnsNpc.removeSpawnNpc(tile);
|
|
}
|
|
}
|
|
|
|
SpawnNpcList Map::getSpawnNpcList(Tile* where)
|
|
{
|
|
SpawnNpcList listNpc;
|
|
TileLocation* tile_loc = where->getLocation();
|
|
if(tile_loc) {
|
|
if(tile_loc->getSpawnNpcCount() > 0) {
|
|
uint32_t found = 0;
|
|
if(where->spawnNpc) {
|
|
++found;
|
|
listNpc.push_back(where->spawnNpc);
|
|
}
|
|
|
|
// Scans the border tiles in an expanding square around the original spawnNpc
|
|
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->getSpawnNpcCount()) {
|
|
for(int x = start_x; x <= end_x; ++x) {
|
|
Tile* tile = getTile(x, start_y, z);
|
|
if(tile && tile->spawnNpc) {
|
|
listNpc.push_back(tile->spawnNpc);
|
|
++found;
|
|
}
|
|
tile = getTile(x, end_y, z);
|
|
if(tile && tile->spawnNpc) {
|
|
listNpc.push_back(tile->spawnNpc);
|
|
++found;
|
|
}
|
|
}
|
|
|
|
for(int y = start_y + 1; y < end_y; ++y) {
|
|
Tile* tile = getTile(start_x, y, z);
|
|
if(tile && tile->spawnNpc) {
|
|
listNpc.push_back(tile->spawnNpc);
|
|
++found;
|
|
}
|
|
tile = getTile(end_x, y, z);
|
|
if(tile && tile->spawnNpc) {
|
|
listNpc.push_back(tile->spawnNpc);
|
|
++found;
|
|
}
|
|
}
|
|
--start_x, --start_y;
|
|
++end_x, ++end_y;
|
|
}
|
|
}
|
|
}
|
|
return listNpc;
|
|
}
|
|
|
|
bool Map::exportMinimap(FileName filename, int floor /*= GROUND_LAYER*/, bool displaydialog)
|
|
{
|
|
uint8_t* pic = nullptr;
|
|
|
|
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() == nullptr || (*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)
|
|
g_gui.SetLoadDone(int(tiles_iterated / double(tilecount) * 90.0));
|
|
|
|
if(tile->empty() || tile->getZ() != floor)
|
|
continue;
|
|
|
|
//std::cout << "Pixel : " << (tile->getY() - min_y) * width + (tile->getX() - min_x) << std::endl;
|
|
uint32_t 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()) {
|
|
delete[] pic;
|
|
return false;
|
|
}
|
|
// Store the magic number
|
|
fh.addRAW("BM");
|
|
|
|
// Store the file size
|
|
// We need to predict how large it will be
|
|
uint32_t 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) {
|
|
g_gui.SetLoadDone(90 + int((minimap_height-y) / double(minimap_height) * 10.0));
|
|
}
|
|
}
|
|
|
|
delete[] pic;
|
|
//fclose(file);
|
|
fh.close();
|
|
}
|
|
catch(...)
|
|
{
|
|
delete[] pic;
|
|
}
|
|
|
|
return true;
|
|
}
|