diff --git a/include/config.h b/include/config.h index 2a14c399..e265df28 100644 --- a/include/config.h +++ b/include/config.h @@ -109,6 +109,8 @@ public: void setUsePoryScript(bool usePoryScript); bool getUsePoryScript(); void setProjectDir(QString projectDir); + void setUseCustomBorderSize(bool enable); + bool getUseCustomBorderSize(); protected: QString getConfigFilepath(); void parseConfigKeyValue(QString key, QString value); @@ -119,6 +121,7 @@ private: QString projectDir; bool useEncounterJson; bool usePoryScript; + bool useCustomBorderSize; }; extern ProjectConfig projectConfig; diff --git a/include/core/map.h b/include/core/map.h index 7f9ff989..8be435a8 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -14,6 +14,9 @@ #include #include +#define DEFAULT_BORDER_WIDTH 2 +#define DEFAULT_BORDER_HEIGHT 2 + class Map : public QObject { Q_OBJECT @@ -58,6 +61,8 @@ public: static QString bgEventsLabelFromName(QString mapName); int getWidth(); int getHeight(); + int getBorderWidth(); + int getBorderHeight(); QPixmap render(bool ignoreCache, MapLayout * fromLayout = nullptr); QPixmap renderCollision(qreal opacity, bool ignoreCache); bool blockChanged(int, Blockdata*); diff --git a/include/core/maplayout.h b/include/core/maplayout.h index df074785..c2c71d8e 100644 --- a/include/core/maplayout.h +++ b/include/core/maplayout.h @@ -15,6 +15,8 @@ public: QString name; QString width; QString height; + QString border_width; + QString border_height; QString border_path; QString blockdata_path; QString tileset_primary_label; diff --git a/src/config.cpp b/src/config.cpp index 50e5ca83..da684887 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -356,12 +356,18 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { if (!ok) { logWarn(QString("Invalid config value for use_encounter_json: '%1'. Must be 0 or 1.").arg(value)); } - } else if(key == "use_poryscript") { + } else if (key == "use_poryscript") { bool ok; this->usePoryScript = value.toInt(&ok); - if(!ok) { + if (!ok) { logWarn(QString("Invalid config value for use_poryscript: '%1'. Must be 0 or 1.").arg(value)); } + } else if (key == "use_custom_border_size") { + bool ok; + this->useCustomBorderSize = value.toInt(&ok); + if (!ok) { + logWarn(QString("Invalid config value for use_custom_border_size: '%1'. Must be 0 or 1.").arg(value)); + } } else { logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); } @@ -372,6 +378,7 @@ QMap ProjectConfig::getKeyValueMap() { map.insert("base_game_version", baseGameVersionMap.value(this->baseGameVersion)); map.insert("use_encounter_json", QString::number(this->useEncounterJson)); map.insert("use_poryscript", QString::number(this->usePoryScript)); + map.insert("use_custom_border_size", QString::number(this->useCustomBorderSize)); return map; } @@ -401,6 +408,7 @@ void ProjectConfig::onNewConfigFileCreated() { this->baseGameVersion = static_cast(baseGameVersionComboBox->currentData().toInt()); } } + this->useCustomBorderSize = this->baseGameVersion == BaseGameVersion::pokefirered; this->useEncounterJson = true; this->usePoryScript = false; } @@ -435,3 +443,12 @@ void ProjectConfig::setUsePoryScript(bool usePoryScript) { bool ProjectConfig::getUsePoryScript() { return this->usePoryScript; } + +void ProjectConfig::setUseCustomBorderSize(bool enable) { + this->useCustomBorderSize = enable; + this->save(); +} + +bool ProjectConfig::getUseCustomBorderSize() { + return this->useCustomBorderSize; +} diff --git a/src/core/map.cpp b/src/core/map.cpp index f9df72fd..c4e88dd4 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -59,6 +59,14 @@ int Map::getHeight() { return layout->height.toInt(nullptr, 0); } +int Map::getBorderWidth() { + return layout->border_width.toInt(nullptr, 0); +} + +int Map::getBorderHeight() { + return layout->border_height.toInt(nullptr, 0); +} + bool Map::blockChanged(int i, Blockdata *cache) { if (!cache) return true; @@ -197,8 +205,8 @@ QPixmap Map::render(bool ignoreCache = false, MapLayout * fromLayout) { QPixmap Map::renderBorder() { bool changed_any = false; - int width_ = 2; - int height_ = 2; + int width_ = getBorderWidth(); + int height_ = getBorderHeight(); if (layout->border_image.isNull()) { layout->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); changed_any = true; diff --git a/src/editor.cpp b/src/editor.cpp index b52f81b7..d424254f 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1335,8 +1335,8 @@ void Editor::displayMapBorder() { borderItems.clear(); QPixmap pixmap = map->renderBorder(); - for (int y = -6; y < map->getHeight() + 6; y += 2) - for (int x = -6; x < map->getWidth() + 6; x += 2) { + for (int y = -6; y < map->getHeight() + 6; y += map->getBorderHeight()) + for (int x = -6; x < map->getWidth() + 6; x += map->getBorderWidth()) { QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); item->setX(x * 16); item->setY(y * 16); diff --git a/src/project.cpp b/src/project.cpp index a7a2ccfe..ab840edb 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -8,6 +8,7 @@ #include "tile.h" #include "tileset.h" #include "imageexport.h" +#include "map.h" #include #include @@ -489,6 +490,11 @@ bool Project::readMapLayouts() { "border_filepath", "blockdata_filepath", }; + bool useCustomBorderSize = projectConfig.getUseCustomBorderSize(); + if (useCustomBorderSize) { + requiredFields.append("border_width"); + requiredFields.append("border_height"); + } for (int i = 0; i < layouts.size(); i++) { QJsonObject layoutObj = layouts[i].toObject(); if (layoutObj.isEmpty()) @@ -520,6 +526,23 @@ bool Project::readMapLayouts() { return false; } layout->height = QString::number(lheight); + if (useCustomBorderSize) { + int bwidth = layoutObj["border_width"].toInt(); + if (bwidth <= 0) { // 0 is an expected border width/height that should be handled, GF used it for the RS layouts in FRLG + logWarn(QString("Invalid layout 'border_width' value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead.").arg(bwidth).arg(i).arg(layoutsFilepath).arg(DEFAULT_BORDER_WIDTH)); + bwidth = DEFAULT_BORDER_WIDTH; + } + layout->border_width = QString::number(bwidth); + int bheight = layoutObj["border_height"].toInt(); + if (bheight <= 0) { + logWarn(QString("Invalid layout 'border_height value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead.").arg(bheight).arg(i).arg(layoutsFilepath).arg(DEFAULT_BORDER_HEIGHT)); + bheight = DEFAULT_BORDER_HEIGHT; + } + layout->border_height = QString::number(bheight); + } else { + layout->border_width = QString::number(DEFAULT_BORDER_WIDTH); + layout->border_height = QString::number(DEFAULT_BORDER_HEIGHT); + } layout->tileset_primary_label = layoutObj["primary_tileset"].toString(); if (layout->tileset_primary_label.isEmpty()) { logError(QString("Missing 'primary_tileset' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); @@ -536,7 +559,7 @@ bool Project::readMapLayouts() { return false; } layout->blockdata_path = layoutObj["blockdata_filepath"].toString(); - if (layout->border_path.isEmpty()) { + if (layout->blockdata_path.isEmpty()) { logError(QString("Missing 'blockdata_filepath' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; } @@ -563,6 +586,7 @@ void Project::saveMapLayouts() { QJsonObject layoutsObj; layoutsObj["layouts_table_label"] = layoutsLabel; + bool useCustomBorderSize = projectConfig.getUseCustomBorderSize(); QJsonArray layoutsArr; for (QString layoutId : mapLayoutsTableMaster) { MapLayout *layout = mapLayouts.value(layoutId); @@ -571,6 +595,10 @@ void Project::saveMapLayouts() { layoutObj["name"] = layout->name; layoutObj["width"] = layout->width.toInt(nullptr, 0); layoutObj["height"] = layout->height.toInt(nullptr, 0); + if (useCustomBorderSize) { + layoutObj["border_width"] = layout->border_width.toInt(nullptr, 0); + layoutObj["border_height"] = layout->border_height.toInt(nullptr, 0); + } layoutObj["primary_tileset"] = layout->tileset_primary_label; layoutObj["secondary_tileset"] = layout->tileset_secondary_label; layoutObj["border_filepath"] = layout->border_path; @@ -1034,7 +1062,7 @@ bool Project::loadMapBorder(Map *map) { QString path = QString("%1/%2").arg(root).arg(map->layout->border_path); map->layout->border = readBlockdata(path); - int borderLength = 4; + int borderLength = map->getBorderWidth() * map->getBorderHeight(); if (map->layout->border->blocks->count() != borderLength) { logWarn(QString("Layout border blockdata length %1 must be %2. Resizing border blockdata.") .arg(map->layout->border->blocks->count()) @@ -1046,10 +1074,17 @@ bool Project::loadMapBorder(Map *map) { void Project::setNewMapBorder(Map *map) { Blockdata *blockdata = new Blockdata; - blockdata->addBlock(qint16(0x01D4)); - blockdata->addBlock(qint16(0x01D5)); - blockdata->addBlock(qint16(0x01DC)); - blockdata->addBlock(qint16(0x01DD)); + if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { + blockdata->addBlock(qint16(0x0014)); + blockdata->addBlock(qint16(0x0015)); + blockdata->addBlock(qint16(0x001C)); + blockdata->addBlock(qint16(0x001D)); + } else { + blockdata->addBlock(qint16(0x01D4)); + blockdata->addBlock(qint16(0x01D5)); + blockdata->addBlock(qint16(0x01DC)); + blockdata->addBlock(qint16(0x01DD)); + } map->layout->border = blockdata; } @@ -1136,6 +1171,10 @@ void Project::saveMap(Map *map) { newLayoutObj["name"] = map->layout->name; newLayoutObj["width"] = map->layout->width.toInt(); newLayoutObj["height"] = map->layout->height.toInt(); + if (projectConfig.getUseCustomBorderSize()) { + newLayoutObj["border_width"] = map->layout->border_width.toInt(); + newLayoutObj["border_height"] = map->layout->border_height.toInt(); + } newLayoutObj["primary_tileset"] = map->layout->tileset_primary_label; newLayoutObj["secondary_tileset"] = map->layout->tileset_secondary_label; newLayoutObj["border_filepath"] = map->layout->border_path; diff --git a/src/ui/bordermetatilespixmapitem.cpp b/src/ui/bordermetatilespixmapitem.cpp index 2093eabe..f5f4d2c4 100644 --- a/src/ui/bordermetatilespixmapitem.cpp +++ b/src/ui/bordermetatilespixmapitem.cpp @@ -9,9 +9,9 @@ void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) int x = static_cast(pos.x()) / 16; int y = static_cast(pos.y()) / 16; - for (int i = 0; i < selectionDimensions.x() && (i + x) < 2; i++) { - for (int j = 0; j < selectionDimensions.y() && (j + y) < 2; j++) { - int blockIndex = (j + y) * 2 + (i + x); + for (int i = 0; i < selectionDimensions.x() && (i + x) < map->getBorderWidth(); i++) { + for (int j = 0; j < selectionDimensions.y() && (j + y) < map->getBorderHeight(); j++) { + int blockIndex = (j + y) * map->getBorderWidth() + (i + x); uint16_t tile = selectedMetatiles->at(j * selectionDimensions.x() + i); (*map->layout->border->blocks)[blockIndex].tile = tile; } @@ -22,15 +22,15 @@ void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) } void BorderMetatilesPixmapItem::draw() { - QImage image(32, 32, QImage::Format_RGBA8888); + QImage image(16 * map->getBorderWidth(), 16 * map->getBorderHeight(), QImage::Format_RGBA8888); QPainter painter(&image); QVector *blocks = map->layout->border->blocks; - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { + for (int i = 0; i < map->getBorderWidth(); i++) { + for (int j = 0; j < map->getBorderHeight(); j++) { int x = i * 16; int y = j * 16; - int index = j * 2 + i; + int index = j * map->getBorderWidth() + i; QImage metatile_image = getMetatileImage(blocks->value(index).tile, map->layout->tileset_primary, map->layout->tileset_secondary); QPoint metatile_origin = QPoint(x, y); painter.drawImage(metatile_origin, metatile_image); diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index 9191a73f..8323278a 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -154,6 +154,8 @@ void NewMapPopup::on_pushButton_NewMap_Accept_clicked() { layout->name = QString("%1_Layout").arg(newMap->name); layout->width = QString::number(this->ui->spinBox_NewMap_Width->value()); layout->height = QString::number(this->ui->spinBox_NewMap_Height->value()); + layout->border_width = QString::number(DEFAULT_BORDER_WIDTH); + layout->border_height = QString::number(DEFAULT_BORDER_HEIGHT); layout->tileset_primary_label = this->ui->comboBox_NewMap_Primary_Tileset->currentText(); layout->tileset_secondary_label = this->ui->comboBox_NewMap_Secondary_Tileset->currentText(); layout->border_path = QString("data/layouts/%1/border.bin").arg(newMapName);