403 lines
11 KiB
C++
403 lines
11 KiB
C++
![]() |
#include "regionmapeditor.h"
|
||
|
|
||
|
#include <QByteArray>
|
||
|
#include <QFile>
|
||
|
#include <QDebug>
|
||
|
#include <QRegularExpression>
|
||
|
|
||
|
#include <QImage>
|
||
|
|
||
|
|
||
|
|
||
|
// TODO: add logging / config stuff
|
||
|
|
||
|
// IN: ROUTE_101 ... OUT: MAP_ROUTE101
|
||
|
// eg. SOUTHERN_ISLAND -> MAP_SOUTHERN_ISLAND -> SouthernIsland(n) -> SouthernIsland_Exterior
|
||
|
// MT_CHIMNEY -> MAP_MT_CHIMNEY -> MtChimney(y)
|
||
|
// ROUTE_101 -> MAP_ROUTE101 -> Route101(y)
|
||
|
// TODO: move this maybe? would I be able to call it from this file if it was in map.cpp?
|
||
|
QString RegionMap::mapSecToMapConstant(QString mapSectionName) {
|
||
|
//
|
||
|
QString mapConstantName = "MAP_";
|
||
|
QString sectionNameTemp = mapSectionName.replace("ROUTE_","ROUTE");
|
||
|
mapConstantName += sectionNameTemp;
|
||
|
return mapConstantName;
|
||
|
}
|
||
|
|
||
|
// TODO: verify these are in the correct order
|
||
|
// also TODO: read this from the project somehow
|
||
|
QMap<QString, QList<struct CityMapPosition>> RegionMap::ruby_city_maps_ = QMap<QString, QList<struct CityMapPosition>>({
|
||
|
{"LavaridgeTown", {
|
||
|
{"lavaridge_0.bin", 5, 3},
|
||
|
}},
|
||
|
{"FallarborTown", {
|
||
|
{"fallarbor_0.bin", 3, 0},
|
||
|
}},
|
||
|
{"FortreeCity", {
|
||
|
{"fortree_0.bin", 12, 0},
|
||
|
}},
|
||
|
{"SlateportCity", {
|
||
|
{"slateport_0.bin", 8, 10},
|
||
|
{"slateport_1.bin", 8, 11},
|
||
|
}},
|
||
|
{"RustboroCity", {
|
||
|
{"rustboro_0.bin", 0, 5},
|
||
|
{"rustboro_1.bin", 0, 6},
|
||
|
}},
|
||
|
{"PacifidlogTown", {
|
||
|
{"pacifidlog_0.bin", 17, 10},
|
||
|
}},
|
||
|
{"MauvilleCity", {
|
||
|
{"mauville_0.bin", 8, 6},
|
||
|
{"mauville_1.bin", 9, 6},
|
||
|
}},
|
||
|
{"OldaleTown", {
|
||
|
{"oldale_0.bin", 4, 9},
|
||
|
}},
|
||
|
{"LilycoveCity", {
|
||
|
{"lilycove_0.bin", 18, 3},
|
||
|
{"lilycove_1.bin", 19, 3},
|
||
|
}},
|
||
|
{"LittlerootTown", {
|
||
|
{"littleroot_0.bin", 4, 11},
|
||
|
}},
|
||
|
{"DewfordTown", {
|
||
|
{"dewford_0.bin", 2, 14},
|
||
|
}},
|
||
|
{"SootopolisCity", {
|
||
|
{"sootopolis_0.bin", 21, 7},
|
||
|
}},
|
||
|
{"EverGrandeCity", {
|
||
|
{"ever_grande_0.bin", 27, 8},
|
||
|
{"ever_grande_1.bin", 27, 9},
|
||
|
}},
|
||
|
{"VerdanturfTown", {
|
||
|
{"verdanturf_0.bin", 4, 6},
|
||
|
}},
|
||
|
{"MossdeepCity", {
|
||
|
{"mossdeep_0.bin", 24, 5},
|
||
|
{"mossdeep_1.bin", 25, 5},
|
||
|
}},
|
||
|
{"PetalburgCity", {
|
||
|
{"petalburg_0.bin", 1, 9},
|
||
|
}},
|
||
|
});
|
||
|
|
||
|
void RegionMap::init(Project *pro) {
|
||
|
QString path = pro->root;
|
||
|
//
|
||
|
// TODO: in the future, allow these to be adjustable (and save values)
|
||
|
// possibly use a config file?
|
||
|
layout_width_ = 28;
|
||
|
layout_height_ = 15;
|
||
|
|
||
|
img_width_ = layout_width_ + 4;
|
||
|
img_height_ = layout_height_ + 5;
|
||
|
|
||
|
//city_map_squares_path = QString();
|
||
|
temp_path = path;// delete this
|
||
|
region_map_bin_path = path + "/graphics/pokenav/region_map_map.bin";
|
||
|
region_map_png_path = path + "/graphics/pokenav/region_map.png";
|
||
|
region_map_layout_path = path + "/src/data/region_map_layout.h";
|
||
|
|
||
|
readBkgImgBin();
|
||
|
readLayout(pro->mapConstantsToMapNames);
|
||
|
readCityMaps();
|
||
|
|
||
|
//tryGetMap();
|
||
|
|
||
|
//saveBkgImgBin();
|
||
|
//saveLayout();
|
||
|
test(pro->mapConstantsToMapNames);
|
||
|
}
|
||
|
|
||
|
// as of now, this needs to be called first because it initializes all the
|
||
|
// RegionMapSquare s in the list
|
||
|
// TODO: if the tileId is not valid for the provided image, make sure it does not crash
|
||
|
void RegionMap::readBkgImgBin() {
|
||
|
QFile binFile(region_map_bin_path);
|
||
|
if (!binFile.open(QIODevice::ReadOnly)) return;
|
||
|
|
||
|
QByteArray mapBinData = binFile.readAll();
|
||
|
binFile.close();
|
||
|
|
||
|
// the two is because lines are skipped for some reason
|
||
|
// (maybe that is because there could be multiple layers?)
|
||
|
// background image is also 32x20
|
||
|
for (int m = 0; m < img_height_; m++) {
|
||
|
for (int n = 0; n < img_width_; n++) {
|
||
|
RegionMapSquare square;// =
|
||
|
square.tile_img_id = mapBinData.at(n + m * img_width_ * 2);
|
||
|
map_squares.append(square);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RegionMap::saveBkgImgBin() {
|
||
|
QByteArray data(4096,0);// use a constant here? maybe read the original size?
|
||
|
|
||
|
for (int m = 0; m < img_height_; m++) {
|
||
|
for (int n = 0; n < img_width_; n++) {
|
||
|
data[n + m * img_width_ * 2] = map_squares[n + m * img_width_].tile_img_id;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QFile file(region_map_bin_path);
|
||
|
if (!file.open(QIODevice::WriteOnly)) return;
|
||
|
file.write(data);
|
||
|
file.close();
|
||
|
}
|
||
|
|
||
|
// done
|
||
|
void RegionMap::readLayout(QMap<QString, QString> *qmap) {
|
||
|
QFile file(region_map_layout_path);
|
||
|
if (!file.open(QIODevice::ReadOnly)) return;
|
||
|
|
||
|
QMap<QString, QString> * abbr = new QMap<QString, QString>;
|
||
|
|
||
|
QString line, text;
|
||
|
QStringList *captured = new QStringList;
|
||
|
|
||
|
QTextStream in(&file);
|
||
|
while (!in.atEnd()) {
|
||
|
line = in.readLine();
|
||
|
if (line.startsWith("#define")) {
|
||
|
QStringList split = line.split(QRegularExpression("\\s+"));
|
||
|
abbr->insert(split[2].replace("MAPSEC_",""), split[1]);
|
||
|
} else {
|
||
|
text += line.remove(" ");
|
||
|
}
|
||
|
}
|
||
|
QRegularExpression re("{(.*?)}");
|
||
|
*captured = re.match(text).captured(1).split(",");
|
||
|
captured->removeAll({});
|
||
|
|
||
|
// replace abbreviations with names
|
||
|
for (int i = 0; i < captured->length(); i++) {
|
||
|
QString value = (*captured)[i];
|
||
|
if (value.startsWith("R(")) {// routes are different
|
||
|
captured->replace(i, QString("ROUTE_%1").arg(value.mid(2,3)));
|
||
|
} else {
|
||
|
captured->replace(i, abbr->key(value));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TODO: improve this?
|
||
|
for (int m = 0, i = 0; m < layout_height_; m++) {
|
||
|
for (int n = 0; n < layout_width_; n++) {
|
||
|
i = img_index_(n,m);
|
||
|
QString secname = (*captured)[layout_index_(n,m)];
|
||
|
if (secname != "NOTHING") map_squares[i].has_map = true;
|
||
|
map_squares[i].map_name = qmap->value(mapSecToMapConstant(secname));
|
||
|
map_squares[i].x = n;
|
||
|
map_squares[i].y = m;
|
||
|
}
|
||
|
}
|
||
|
mapname_abbr = abbr;
|
||
|
layout_map_names = captured;
|
||
|
file.close();
|
||
|
}
|
||
|
|
||
|
// does it matter that it doesn't save in order?
|
||
|
// do i need to use a QList<Pair> ??
|
||
|
void RegionMap::saveLayout() {
|
||
|
//
|
||
|
QString layout_text = "";
|
||
|
QString mapsec = "MAPSEC_";
|
||
|
QString define = "#define ";
|
||
|
QString array_start = "static const u8 sRegionMapLayout[] =\n{";
|
||
|
QString array_close = "\n};\n";
|
||
|
QString tab = " ";
|
||
|
|
||
|
for (QString key : mapname_abbr->keys()) {
|
||
|
layout_text += define + mapname_abbr->value(key) + tab + mapsec + key + "\n";
|
||
|
}
|
||
|
|
||
|
layout_text += "\n" + array_start;// + + array_close;//oops
|
||
|
|
||
|
//qDebug() << *layout_map_names;
|
||
|
int cnt = 0;
|
||
|
for (QString s : *layout_map_names) {
|
||
|
//
|
||
|
if (!(cnt % layout_width_)) {
|
||
|
layout_text += "\n" + tab;
|
||
|
}
|
||
|
if (s.startsWith("ROUTE_")) {
|
||
|
layout_text += QString("R(%1)").arg(s.replace("ROUTE_","")) + ", ";
|
||
|
} else {
|
||
|
layout_text += mapname_abbr->value(s) + ", ";
|
||
|
}
|
||
|
cnt++;
|
||
|
}
|
||
|
|
||
|
//layout_text.
|
||
|
layout_text += array_close;
|
||
|
|
||
|
QFile file(region_map_layout_path);
|
||
|
if (!file.open(QIODevice::WriteOnly)) return;
|
||
|
file.write(layout_text.toUtf8());
|
||
|
file.close();
|
||
|
}
|
||
|
|
||
|
void RegionMap::readCityMaps() {
|
||
|
//
|
||
|
//for (int m = 0; m < layout_height_; m++) {
|
||
|
// QString tester;
|
||
|
// for (int n = 0; n < layout_width_; n++) {
|
||
|
// tester += (QString::number(img_index_(n,m)).rightJustified(3, '.') + " ");
|
||
|
// }
|
||
|
// qDebug() << tester;
|
||
|
//}
|
||
|
//for (auto map : map_squares) {
|
||
|
for (int map = 0; map < map_squares.size(); map++) {
|
||
|
//
|
||
|
if (map_squares[map].has_map) {
|
||
|
//
|
||
|
if (ruby_city_maps_.contains(map_squares[map].map_name)) {
|
||
|
map_squares[map].has_city_map = true;
|
||
|
//map_squares[map].city_map_name = ruby_city_maps_.value(map_squares[map].map_name)[0].tilemap;
|
||
|
QList<struct CityMapPosition> city_maps = ruby_city_maps_.value(map_squares[map].map_name);
|
||
|
for (auto city_map : city_maps) {
|
||
|
//
|
||
|
if (city_map.x == map_squares[map].x
|
||
|
&& city_map.y == map_squares[map].y)
|
||
|
//
|
||
|
map_squares[map].city_map_name = city_map.tilemap;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//done
|
||
|
QString RegionMap::newAbbr(QString mapname) {
|
||
|
QString abbr;
|
||
|
QStringList words = mapname.split("_");
|
||
|
|
||
|
if (words.length() == 1) {
|
||
|
abbr = (words[0] + "_____X").left(6);
|
||
|
} else {
|
||
|
abbr = (words.front() + "___X").left(4) + "_" + words.back().at(0);
|
||
|
}
|
||
|
|
||
|
// to guarantee unique abbreviations (up to 14)
|
||
|
QString extra_chars = "23456789BCDEF";
|
||
|
int count = 0;
|
||
|
while ((*mapname_abbr).values().contains(abbr)) {
|
||
|
abbr.replace(5,1,extra_chars[count]);
|
||
|
count++;
|
||
|
}
|
||
|
return abbr;
|
||
|
}
|
||
|
|
||
|
// layout coords to image index
|
||
|
int RegionMap::img_index_(int x, int y) {
|
||
|
return ((x + 1) + (y + 2) * img_width_);
|
||
|
}
|
||
|
|
||
|
// layout coords to layout index
|
||
|
int RegionMap::layout_index_(int x, int y) {
|
||
|
return (x + y * layout_width_);
|
||
|
}
|
||
|
|
||
|
// img coords to layout index?
|
||
|
// img coords to img index?
|
||
|
|
||
|
void RegionMap::test(QMap<QString, QString>* qmap) {
|
||
|
//
|
||
|
bool debug_rmap = false;
|
||
|
|
||
|
if (debug_rmap) {
|
||
|
for (auto square : map_squares) {
|
||
|
qDebug() << "(" << square.x << "," << square.y << ")"
|
||
|
<< square.tile_img_id
|
||
|
<< square.has_map
|
||
|
<< square.map_name
|
||
|
<< square.has_city_map
|
||
|
<< square.city_map_name
|
||
|
;
|
||
|
//if (qmap->contains(mapSecToMapConstant(square.map_name)))
|
||
|
// extras += qmap->value(mapSecToMapConstant(square.map_name)) + " ";
|
||
|
//else
|
||
|
// extras += "nothing ";
|
||
|
}
|
||
|
|
||
|
QPixmap png(region_map_png_path);
|
||
|
//png.load(region_map_png_path);
|
||
|
qDebug() << "png num 8x8 tiles" << QString("0x%1").arg((png.width()/8) * (png.height() / 8), 2, 16, QChar('0'));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
int RegionMap::width() {
|
||
|
return this->img_width_;
|
||
|
}
|
||
|
|
||
|
int RegionMap::height() {
|
||
|
return this->img_height_;
|
||
|
}
|
||
|
|
||
|
// TODO: remove +2 and put elsewhere
|
||
|
QSize RegionMap::imgSize() {
|
||
|
return QSize(img_width_ * 8 + 2, img_height_ * 8 + 2);
|
||
|
}
|
||
|
|
||
|
// TODO: rename to getTileIdAt()?
|
||
|
unsigned RegionMap::getTileId(int x, int y) {
|
||
|
//
|
||
|
return map_squares[x + y * img_width_].tile_img_id;
|
||
|
//qDebug() << x << y;
|
||
|
//return 0;
|
||
|
}
|
||
|
|
||
|
// sidenote: opening the map from MAPSEC_x will not always be right
|
||
|
// there needs to be a mapsections to mapname QMap
|
||
|
// otherwie, look for the first map with right substring
|
||
|
// mapConstantsToMapNames [MAP_ROUTE106] = "Route106"
|
||
|
// eg. SOUTHERN_ISLAND -> MAP_SOUTHERN_ISLAND -> SouthernIsland(n) -> SouthernIsland_Exterior
|
||
|
// MT_CHIMNEY -> MAP_MT_CHIMNEY -> MtChimney(y)
|
||
|
// ROUTE_101 -> MAP_ROUTE101 -> Route101(y)
|
||
|
// (or synchronize these for consistency in the repos :: underscore / no underscore)
|
||
|
|
||
|
// TODO: change debugs to logs
|
||
|
void RegionMap::save() {
|
||
|
//
|
||
|
qDebug() << "saving region map image tilemap at" << region_map_bin_path << "\n"
|
||
|
;//<< "saving region map layout at" << region_map_layout_path << "\n";
|
||
|
|
||
|
saveBkgImgBin();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|