2019-01-07 19:46:27 +00:00
|
|
|
#include "regionmap.h"
|
2019-01-14 00:27:28 +00:00
|
|
|
#include "log.h"
|
2019-01-15 22:06:18 +00:00
|
|
|
#include "config.h"
|
2018-12-28 18:26:18 +00:00
|
|
|
|
|
|
|
#include <QByteArray>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QRegularExpression>
|
|
|
|
#include <QImage>
|
2019-01-28 18:47:20 +00:00
|
|
|
#include <math.h>
|
2018-12-28 18:26:18 +00:00
|
|
|
|
|
|
|
void RegionMap::init(Project *pro) {
|
|
|
|
QString path = pro->root;
|
2019-01-05 04:04:14 +00:00
|
|
|
this->project = pro;
|
2019-01-14 00:27:28 +00:00
|
|
|
|
2019-01-15 22:06:18 +00:00
|
|
|
QSize dimensions = porymapConfig.getRegionMapDimensions();
|
|
|
|
img_width_ = dimensions.width();
|
|
|
|
img_height_ = dimensions.height();
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-01-15 22:06:18 +00:00
|
|
|
layout_width_ = img_width_ - this->padLeft - this->padRight;
|
|
|
|
layout_height_ = img_height_ - this->padTop - this->padBottom;
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-01-14 00:27:28 +00:00
|
|
|
region_map_bin_path = path + "/graphics/pokenav/region_map_map.bin";
|
|
|
|
region_map_png_path = path + "/graphics/pokenav/region_map.png";
|
|
|
|
region_map_entries_path = path + "/src/data/region_map/region_map_entries.h";
|
2019-01-05 04:04:14 +00:00
|
|
|
region_map_layout_bin_path = path + "/graphics/pokenav/region_map_section_layout.bin";
|
2019-01-14 00:27:28 +00:00
|
|
|
city_map_tiles_path = path + "/graphics/pokenav/zoom_tiles.png";
|
2018-12-28 18:26:18 +00:00
|
|
|
|
|
|
|
readBkgImgBin();
|
2019-01-05 04:04:14 +00:00
|
|
|
readLayout();
|
2018-12-28 18:26:18 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 18:47:20 +00:00
|
|
|
void RegionMap::save() {
|
|
|
|
logInfo("Saving region map data.");
|
2019-03-25 04:10:57 +00:00
|
|
|
saveTileImages();
|
2019-01-28 18:47:20 +00:00
|
|
|
saveBkgImgBin();
|
|
|
|
saveLayout();
|
|
|
|
porymapConfig.setRegionMapDimensions(this->img_width_, this->img_height_);
|
|
|
|
}
|
|
|
|
|
2019-03-25 04:10:57 +00:00
|
|
|
void RegionMap::saveTileImages() {
|
|
|
|
QFile backgroundTileFile(pngPath());
|
|
|
|
if (backgroundTileFile.open(QIODevice::ReadOnly)) {
|
|
|
|
QByteArray imageData = backgroundTileFile.readAll();
|
|
|
|
QImage pngImage = QImage::fromData(imageData);
|
|
|
|
this->region_map_png_path = project->root + "/graphics/pokenav/region_map.png";
|
|
|
|
pngImage.save(pngPath());
|
|
|
|
}
|
|
|
|
QFile cityTileFile(cityTilesPath());
|
|
|
|
if (cityTileFile.open(QIODevice::ReadOnly)) {
|
|
|
|
QByteArray imageData = cityTileFile.readAll();
|
|
|
|
QImage cityTilesImage = QImage::fromData(imageData);
|
|
|
|
this->city_map_tiles_path = project->root + "/graphics/pokenav/zoom_tiles.png";
|
|
|
|
cityTilesImage.save(cityTilesPath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 18:26:18 +00:00
|
|
|
void RegionMap::readBkgImgBin() {
|
|
|
|
QFile binFile(region_map_bin_path);
|
|
|
|
if (!binFile.open(QIODevice::ReadOnly)) return;
|
|
|
|
|
|
|
|
QByteArray mapBinData = binFile.readAll();
|
|
|
|
binFile.close();
|
|
|
|
|
2019-01-14 00:27:28 +00:00
|
|
|
if (mapBinData.size() < img_height_ * img_width_) {
|
|
|
|
logError(QString("The region map tilemap at %1 is too small.").arg(region_map_bin_path));
|
|
|
|
return;
|
|
|
|
}
|
2018-12-28 18:26:18 +00:00
|
|
|
for (int m = 0; m < img_height_; m++) {
|
|
|
|
for (int n = 0; n < img_width_; n++) {
|
2019-01-14 00:27:28 +00:00
|
|
|
RegionMapSquare square;
|
2018-12-28 18:26:18 +00:00
|
|
|
square.tile_img_id = mapBinData.at(n + m * img_width_ * 2);
|
|
|
|
map_squares.append(square);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegionMap::saveBkgImgBin() {
|
2019-01-28 18:47:20 +00:00
|
|
|
QByteArray data(pow(img_width_ * 2, 2),0);
|
2018-12-28 18:26:18 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-01-05 04:04:14 +00:00
|
|
|
void RegionMap::readLayout() {
|
|
|
|
QFile file(region_map_entries_path);
|
2018-12-28 18:26:18 +00:00
|
|
|
if (!file.open(QIODevice::ReadOnly)) return;
|
|
|
|
|
2019-01-05 04:04:14 +00:00
|
|
|
QString line;
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-01-05 04:04:14 +00:00
|
|
|
QMap<QString, QString> *qmap = new QMap<QString, QString>;
|
2018-12-28 18:26:18 +00:00
|
|
|
|
|
|
|
QTextStream in(&file);
|
2019-01-22 20:06:49 +00:00
|
|
|
in.setCodec("UTF-8");
|
2018-12-28 18:26:18 +00:00
|
|
|
while (!in.atEnd()) {
|
|
|
|
line = in.readLine();
|
2019-01-05 04:04:14 +00:00
|
|
|
if (line.startsWith("static const u8")) {
|
|
|
|
QRegularExpression reBefore("sMapName_(.*)\\[");
|
|
|
|
QRegularExpression reAfter("_\\(\"(.*)\"");
|
|
|
|
QString const_name = reBefore.match(line).captured(1);
|
|
|
|
QString full_name = reAfter.match(line).captured(1);
|
2019-01-14 00:27:28 +00:00
|
|
|
sMapNames.append(const_name);
|
|
|
|
sMapNamesMap.insert(const_name, full_name);
|
2019-01-05 04:04:14 +00:00
|
|
|
} else if (line.contains("MAPSEC")) {
|
|
|
|
QRegularExpression reBefore("\\[(.*)\\]");
|
|
|
|
QRegularExpression reAfter("{(.*)}");
|
|
|
|
QStringList entry = reAfter.match(line).captured(1).remove(" ").split(",");
|
|
|
|
QString mapsec = reBefore.match(line).captured(1);
|
|
|
|
QString insertion = entry[4].remove("sMapName_");
|
2019-01-15 22:06:18 +00:00
|
|
|
qmap->insert(mapsec, sMapNamesMap.value(insertion));
|
|
|
|
mapSecToMapEntry[mapsec] = {
|
|
|
|
// x y width height name
|
|
|
|
entry[0].toInt(), entry[1].toInt(), entry[2].toInt(), entry[3].toInt(), insertion
|
|
|
|
};
|
2018-12-28 18:26:18 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-05 04:04:14 +00:00
|
|
|
file.close();
|
|
|
|
|
2019-01-15 22:06:18 +00:00
|
|
|
project->mapSecToMapHoverName = qmap;
|
2019-01-05 04:04:14 +00:00
|
|
|
|
|
|
|
QFile binFile(region_map_layout_bin_path);
|
|
|
|
if (!binFile.open(QIODevice::ReadOnly)) return;
|
|
|
|
QByteArray mapBinData = binFile.readAll();
|
|
|
|
binFile.close();
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-01-28 18:47:20 +00:00
|
|
|
for (int y = 0; y < layout_height_; y++) {
|
|
|
|
for (int x = 0; x < layout_width_; x++) {
|
|
|
|
int i = img_index_(x,y);
|
2019-02-25 18:31:34 +00:00
|
|
|
uint8_t id = static_cast<uint8_t>(mapBinData.at(layout_index_(x,y)));
|
|
|
|
map_squares[i].secid = id;
|
|
|
|
QString secname = project->mapSectionValueToName.value(id);
|
2019-01-05 04:04:14 +00:00
|
|
|
if (secname != "MAPSEC_NONE") map_squares[i].has_map = true;
|
|
|
|
map_squares[i].mapsec = secname;
|
2019-01-28 18:47:20 +00:00
|
|
|
map_squares[i].map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name);
|
|
|
|
map_squares[i].x = x;
|
|
|
|
map_squares[i].y = y;
|
2018-12-28 18:26:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegionMap::saveLayout() {
|
2019-01-05 04:04:14 +00:00
|
|
|
QString entries_text;
|
|
|
|
QString layout_text;
|
|
|
|
|
|
|
|
entries_text += "#ifndef GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n";
|
|
|
|
entries_text += "#define GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n\n";
|
|
|
|
|
2019-01-14 00:27:28 +00:00
|
|
|
for (auto sName : sMapNames) {
|
|
|
|
entries_text += "static const u8 sMapName_" + sName + "[] = _(\"" + sMapNamesMap.value(sName) + "\");\n";
|
2018-12-28 18:26:18 +00:00
|
|
|
}
|
|
|
|
|
2019-01-05 04:04:14 +00:00
|
|
|
entries_text += "\nconst struct RegionMapLocation gRegionMapEntries[] = {\n";
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-02-25 18:31:34 +00:00
|
|
|
for (auto sec : project->mapSectionNameToValue.keys()) {
|
2019-01-15 22:06:18 +00:00
|
|
|
if (!mapSecToMapEntry.contains(sec)) continue;
|
2019-01-05 04:04:14 +00:00
|
|
|
struct RegionMapEntry entry = mapSecToMapEntry.value(sec);
|
|
|
|
entries_text += " [" + sec + "] = {" + QString::number(entry.x) + ", " + QString::number(entry.y) + ", "
|
2019-01-14 00:27:28 +00:00
|
|
|
+ QString::number(entry.width) + ", " + QString::number(entry.height) + ", sMapName_" + entry.name + "},\n";
|
2018-12-28 18:26:18 +00:00
|
|
|
}
|
2019-01-05 04:04:14 +00:00
|
|
|
entries_text += "};\n\n#endif // GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n";
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-01-05 04:04:14 +00:00
|
|
|
project->saveTextFile(region_map_entries_path, entries_text);
|
|
|
|
|
|
|
|
QByteArray data;
|
|
|
|
for (int m = 0; m < layout_height_; m++) {
|
|
|
|
for (int n = 0; n < layout_width_; n++) {
|
|
|
|
int i = img_index_(n,m);
|
|
|
|
data.append(map_squares[i].secid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
QFile bfile(region_map_layout_bin_path);
|
|
|
|
if (!bfile.open(QIODevice::WriteOnly)) return;
|
|
|
|
bfile.write(data);
|
|
|
|
bfile.close();
|
2018-12-28 18:26:18 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 00:27:28 +00:00
|
|
|
void RegionMap::saveOptions(int id, QString sec, QString name, int x, int y) {
|
2019-01-15 22:06:18 +00:00
|
|
|
resetSquare(id);
|
|
|
|
int index = getMapSquareIndex(x + this->padLeft, y + this->padTop);
|
2019-01-05 18:02:14 +00:00
|
|
|
if (!sec.isEmpty()) {
|
2019-03-25 04:10:57 +00:00
|
|
|
// Validate the input section name.
|
|
|
|
if (!project->mapSectionNameToValue.contains(sec)) {
|
|
|
|
sec = "MAPSEC_NONE";
|
|
|
|
name = QString();
|
|
|
|
}
|
2019-01-28 18:47:20 +00:00
|
|
|
this->map_squares[index].has_map = sec == "MAPSEC_NONE" ? false : true;
|
2019-02-25 18:31:34 +00:00
|
|
|
this->map_squares[index].secid = static_cast<uint8_t>(project->mapSectionNameToValue.value(sec));
|
2019-01-14 00:27:28 +00:00
|
|
|
this->map_squares[index].mapsec = sec;
|
|
|
|
if (!name.isEmpty()) {
|
|
|
|
this->map_squares[index].map_name = name;
|
|
|
|
this->project->mapSecToMapHoverName->insert(sec, name);
|
2019-03-23 20:56:30 +00:00
|
|
|
QString sName = fix_case(sec);
|
|
|
|
sMapNamesMap.insert(sName, name);
|
2019-01-14 00:27:28 +00:00
|
|
|
if (!mapSecToMapEntry.keys().contains(sec)) {
|
|
|
|
sMapNames.append(sName);
|
2019-01-28 18:47:20 +00:00
|
|
|
struct RegionMapEntry entry = {x, y, 1, 1, sName};
|
2019-01-14 00:27:28 +00:00
|
|
|
mapSecToMapEntry.insert(sec, entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->map_squares[index].x = x;
|
|
|
|
this->map_squares[index].y = y;
|
|
|
|
this->map_squares[index].duplicated = false;
|
2019-01-05 18:02:14 +00:00
|
|
|
}
|
2019-01-05 04:04:14 +00:00
|
|
|
}
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-01-14 00:27:28 +00:00
|
|
|
void RegionMap::resetSquare(int index) {
|
|
|
|
this->map_squares[index].mapsec = "MAPSEC_NONE";
|
|
|
|
this->map_squares[index].map_name = QString();
|
|
|
|
this->map_squares[index].has_map = false;
|
2019-02-25 18:31:34 +00:00
|
|
|
this->map_squares[index].secid = static_cast<uint8_t>(project->mapSectionNameToValue.value("MAPSEC_NONE"));
|
2019-01-14 00:27:28 +00:00
|
|
|
this->map_squares[index].has_city_map = false;
|
|
|
|
this->map_squares[index].city_map_name = QString();
|
|
|
|
this->map_squares[index].duplicated = false;
|
2019-02-25 18:31:34 +00:00
|
|
|
}
|
|
|
|
|
2019-03-17 16:37:13 +00:00
|
|
|
void RegionMap::clearLayout() {
|
2019-02-25 18:31:34 +00:00
|
|
|
for (int i = 0; i < map_squares.size(); i++)
|
|
|
|
resetSquare(i);
|
|
|
|
}
|
|
|
|
|
2019-03-17 16:37:13 +00:00
|
|
|
void RegionMap::clearImage() {
|
2019-02-25 18:31:34 +00:00
|
|
|
for (int i = 0; i < map_squares.size(); i++)
|
|
|
|
this->map_squares[i].tile_img_id = 0x00;
|
2019-01-14 00:27:28 +00:00
|
|
|
}
|
2018-12-28 18:26:18 +00:00
|
|
|
|
2019-02-16 22:56:44 +00:00
|
|
|
void RegionMap::replaceSectionId(unsigned oldId, unsigned newId) {
|
|
|
|
for (auto &square : map_squares) {
|
|
|
|
if (square.secid == oldId) {
|
|
|
|
square.has_map = false;
|
|
|
|
square.secid = newId;
|
2019-02-25 18:31:34 +00:00
|
|
|
QString secname = project->mapSectionValueToName.value(newId);
|
2019-02-16 22:56:44 +00:00
|
|
|
if (secname != "MAPSEC_NONE") square.has_map = true;
|
|
|
|
square.mapsec = secname;
|
|
|
|
square.map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 00:27:28 +00:00
|
|
|
void RegionMap::resize(int newWidth, int newHeight) {
|
|
|
|
QVector<RegionMapSquare> new_squares;
|
|
|
|
|
|
|
|
for (int y = 0; y < newHeight; y++) {
|
|
|
|
for (int x = 0; x < newWidth; x++) {
|
|
|
|
RegionMapSquare square;
|
|
|
|
if (x < img_width_ && y < img_height_) {
|
|
|
|
square = map_squares[getMapSquareIndex(x, y)];
|
2019-01-15 22:06:18 +00:00
|
|
|
} else if (x < newWidth - this->padRight && y < newHeight - this->padBottom) {
|
2019-01-14 00:27:28 +00:00
|
|
|
square.tile_img_id = 0;
|
|
|
|
square.x = x;
|
|
|
|
square.y = y;
|
|
|
|
square.mapsec = "MAPSEC_NONE";
|
|
|
|
} else {
|
|
|
|
square.tile_img_id = 0;
|
|
|
|
}
|
|
|
|
new_squares.append(square);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->map_squares = new_squares;
|
|
|
|
this->img_width_ = newWidth;
|
|
|
|
this->img_height_ = newHeight;
|
2019-01-15 22:06:18 +00:00
|
|
|
this->layout_width_ = newWidth - this->padLeft - this->padRight;
|
|
|
|
this->layout_height_ = newHeight - this->padTop - this->padBottom;
|
2019-01-14 00:27:28 +00:00
|
|
|
}
|
2019-01-28 18:47:20 +00:00
|
|
|
|
|
|
|
QVector<uint8_t> RegionMap::getTiles() {
|
|
|
|
QVector<uint8_t> tileIds;
|
|
|
|
for (auto square : map_squares) {
|
|
|
|
tileIds.append(square.tile_img_id);
|
|
|
|
}
|
|
|
|
return tileIds;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegionMap::setTiles(QVector<uint8_t> tileIds) {
|
|
|
|
if (tileIds.size() != map_squares.size()) return;
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
for (uint8_t tileId : tileIds) {
|
|
|
|
map_squares[i].tile_img_id = tileId;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Layout coords to image index.
|
|
|
|
int RegionMap::img_index_(int x, int y) {
|
|
|
|
return ((x + this->padLeft) + (y + this->padTop) * img_width_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Layout coords to layout index.
|
|
|
|
int RegionMap::layout_index_(int x, int y) {
|
|
|
|
return (x + y * layout_width_);
|
|
|
|
}
|
|
|
|
|
|
|
|
int RegionMap::width() {
|
|
|
|
return this->img_width_;
|
|
|
|
}
|
|
|
|
|
|
|
|
int RegionMap::height() {
|
|
|
|
return this->img_height_;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize RegionMap::imgSize() {
|
|
|
|
return QSize(img_width_ * 8, img_height_ * 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned RegionMap::getTileId(int x, int y) {
|
|
|
|
return map_squares.at(x + y * img_width_).tile_img_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString RegionMap::pngPath() {
|
|
|
|
return this->region_map_png_path;
|
|
|
|
}
|
|
|
|
|
2019-03-25 04:10:57 +00:00
|
|
|
void RegionMap::setTemporaryPngPath(QString path) {
|
|
|
|
this->region_map_png_path = path;
|
|
|
|
}
|
|
|
|
|
2019-01-28 18:47:20 +00:00
|
|
|
QString RegionMap::cityTilesPath() {
|
|
|
|
return this->city_map_tiles_path;
|
|
|
|
}
|
|
|
|
|
2019-03-25 04:10:57 +00:00
|
|
|
void RegionMap::setTemporaryCityTilesPath(QString path) {
|
|
|
|
this->city_map_tiles_path = path;
|
|
|
|
}
|
|
|
|
|
2019-01-28 18:47:20 +00:00
|
|
|
// From x, y of image.
|
|
|
|
int RegionMap::getMapSquareIndex(int x, int y) {
|
|
|
|
int index = (x + y * img_width_);
|
|
|
|
return index < map_squares.length() ? index : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For turning a MAPSEC_NAME into a unique identifier sMapName-style variable.
|
|
|
|
// CAPS_WITH_UNDERSCORE to CamelCase
|
|
|
|
QString RegionMap::fix_case(QString caps) {
|
|
|
|
bool big = true;
|
|
|
|
QString camel;
|
|
|
|
|
|
|
|
for (auto ch : caps.remove(QRegularExpression("({.*})")).remove("MAPSEC")) {
|
|
|
|
if (ch == '_' || ch == ' ') {
|
|
|
|
big = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (big) {
|
|
|
|
camel += ch.toUpper();
|
|
|
|
big = false;
|
|
|
|
}
|
|
|
|
else camel += ch.toLower();
|
|
|
|
}
|
|
|
|
return camel;
|
|
|
|
}
|