diff --git a/forms/prefabframe.ui b/forms/prefabframe.ui new file mode 100644 index 00000000..9753dd43 --- /dev/null +++ b/forms/prefabframe.ui @@ -0,0 +1,95 @@ + + + PrefabFrame + + + + 0 + 0 + 400 + 149 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Frame + + + QFrame::Panel + + + + QLayout::SetMinimumSize + + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + :/icons/delete.ico:/icons/delete.ico + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/include/config.h b/include/config.h index 165bab60..f8e86fac 100644 --- a/include/config.h +++ b/include/config.h @@ -157,6 +157,7 @@ public: this->enableFloorNumber = false; this->createMapTextFile = false; this->enableTripleLayerMetatiles = false; + this->prefabFilepath = QString(); this->customScripts.clear(); this->readKeys.clear(); } @@ -193,6 +194,8 @@ public: bool getTripleLayerMetatilesEnabled(); void setCustomScripts(QList scripts); QList getCustomScripts(); + void setPrefabFilepath(QString filepath); + QString getPrefabFilepath(); protected: virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; @@ -217,6 +220,7 @@ private: bool enableTripleLayerMetatiles; QList customScripts; QStringList readKeys; + QString prefabFilepath; }; extern ProjectConfig projectConfig; diff --git a/include/ui/currentselectedmetatilespixmapitem.h b/include/ui/currentselectedmetatilespixmapitem.h index 76c52d55..5e4bd275 100644 --- a/include/ui/currentselectedmetatilespixmapitem.h +++ b/include/ui/currentselectedmetatilespixmapitem.h @@ -18,4 +18,6 @@ public: void setMap(Map *map) { this->map = map; } }; +QPixmap drawMetatileSelection(MetatileSelection selection, Map *map); + #endif // CURRENTSELECTEDMETATILESPIXMAPITEM_H diff --git a/include/ui/prefab.h b/include/ui/prefab.h new file mode 100644 index 00000000..a33a6947 --- /dev/null +++ b/include/ui/prefab.h @@ -0,0 +1,31 @@ +#ifndef PREFAB_H +#define PREFAB_H + +#include "ui/metatileselector.h" +#include "map.h" + +#include +#include + +struct PrefabItem +{ + QString name; + QString primaryTileset; + QString secondaryTileset; + MetatileSelection selection; +}; + +class Prefab +{ +public: + void initPrefabUI(QWidget *prefabWidget, QLabel *emptyPrefabLabel, QString primaryTileset, QString secondaryTileset, Map *map); + +private: + QList items; + void loadPrefabs(); + QList getPrefabsForTilesets(QString primaryTileset, QString secondaryTileset); +}; + +extern Prefab prefab; + +#endif // PREFAB_H diff --git a/include/ui/prefabframe.h b/include/ui/prefabframe.h new file mode 100644 index 00000000..404e536f --- /dev/null +++ b/include/ui/prefabframe.h @@ -0,0 +1,22 @@ +#ifndef PREFABFRAME_H +#define PREFABFRAME_H + +#include + +namespace Ui { +class PrefabFrame; +} + +class PrefabFrame : public QFrame +{ + Q_OBJECT + +public: + explicit PrefabFrame(QWidget *parent = nullptr); + ~PrefabFrame(); + +public: + Ui::PrefabFrame *ui; +}; + +#endif // PREFABFRAME_H diff --git a/porymap.pro b/porymap.pro index 21417d3e..d37b915d 100644 --- a/porymap.pro +++ b/porymap.pro @@ -45,6 +45,7 @@ SOURCES += src/core/block.cpp \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ src/ui/overlay.cpp \ + src/ui/prefab.cpp \ src/ui/regionmaplayoutpixmapitem.cpp \ src/ui/regionmapentriespixmapitem.cpp \ src/ui/cursortilerect.cpp \ @@ -80,6 +81,7 @@ SOURCES += src/core/block.cpp \ src/ui/shortcut.cpp \ src/ui/shortcutseditor.cpp \ src/ui/multikeyedit.cpp \ + src/ui/prefabframe.cpp \ src/ui/preferenceeditor.cpp \ src/ui/regionmappropertiesdialog.cpp \ src/ui/colorpicker.cpp \ @@ -126,6 +128,7 @@ HEADERS += include/core/block.h \ include/ui/collisionpixmapitem.h \ include/ui/connectionpixmapitem.h \ include/ui/currentselectedmetatilespixmapitem.h \ + include/ui/prefabframe.h \ include/ui/regionmaplayoutpixmapitem.h \ include/ui/regionmapentriespixmapitem.h \ include/ui/cursortilerect.h \ @@ -163,6 +166,7 @@ HEADERS += include/core/block.h \ include/ui/shortcut.h \ include/ui/shortcutseditor.h \ include/ui/multikeyedit.h \ + include/ui/prefab.h \ include/ui/preferenceeditor.h \ include/ui/regionmappropertiesdialog.h \ include/ui/colorpicker.h \ @@ -176,6 +180,7 @@ HEADERS += include/core/block.h \ FORMS += forms/mainwindow.ui \ forms/eventpropertiesframe.ui \ + forms/prefabframe.ui \ forms/tileseteditor.ui \ forms/paletteeditor.ui \ forms/regionmapeditor.ui \ diff --git a/src/config.cpp b/src/config.cpp index 0ed1e081..16872460 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -503,6 +503,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { this->customScripts.append(script); } } + } else if (key == "prefabs_filepath") { + this->prefabFilepath = value; } else { logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); } @@ -540,6 +542,7 @@ QMap ProjectConfig::getKeyValueMap() { map.insert("create_map_text_file", QString::number(this->createMapTextFile)); map.insert("enable_triple_layer_metatiles", QString::number(this->enableTripleLayerMetatiles)); map.insert("custom_scripts", this->customScripts.join(",")); + map.insert("prefabs_filepath", this->prefabFilepath); return map; } @@ -732,6 +735,15 @@ QList ProjectConfig::getCustomScripts() { return this->customScripts; } +void ProjectConfig::setPrefabFilepath(QString filepath) { + this->prefabFilepath = filepath; + this->save(); +} + +QString ProjectConfig::getPrefabFilepath() { + return this->prefabFilepath; +} + ShortcutsConfig shortcutsConfig; QString ShortcutsConfig::getConfigFilepath() { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c686110c..bdf4a72e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -16,6 +16,7 @@ #include "flowlayout.h" #include "shortcut.h" #include "mapparser.h" +#include "prefab.h" #include #include @@ -552,6 +553,12 @@ bool MainWindow::openProject(QString dir) { } if (success) { + prefab.initPrefabUI( + ui->scrollAreaWidgetContents_Prefabs, + ui->label_prefabHelp, + editor->ui->comboBox_PrimaryTileset->currentText(), + editor->ui->comboBox_SecondaryTileset->currentText(), + editor->map); for (auto action : this->registeredActions) { this->ui->menuTools->removeAction(action); } diff --git a/src/ui/currentselectedmetatilespixmapitem.cpp b/src/ui/currentselectedmetatilespixmapitem.cpp index 80714760..35e52037 100644 --- a/src/ui/currentselectedmetatilespixmapitem.cpp +++ b/src/ui/currentselectedmetatilespixmapitem.cpp @@ -2,8 +2,7 @@ #include "imageproviders.h" #include -void CurrentSelectedMetatilesPixmapItem::draw() { - MetatileSelection selection = metatileSelector->getMetatileSelection(); +QPixmap drawMetatileSelection(MetatileSelection selection, Map *map) { int width = selection.dimensions.x() * 16; int height = selection.dimensions.y() * 16; QImage image(width, height, QImage::Format_RGBA8888); @@ -27,5 +26,10 @@ void CurrentSelectedMetatilesPixmapItem::draw() { } painter.end(); - setPixmap(QPixmap::fromImage(image)); + return QPixmap::fromImage(image); +} + +void CurrentSelectedMetatilesPixmapItem::draw() { + MetatileSelection selection = metatileSelector->getMetatileSelection(); + setPixmap(drawMetatileSelection(selection, this->map)); } diff --git a/src/ui/prefab.cpp b/src/ui/prefab.cpp new file mode 100644 index 00000000..56463fdb --- /dev/null +++ b/src/ui/prefab.cpp @@ -0,0 +1,116 @@ + +#include "prefab.h" +#include "prefabframe.h" +#include "ui_prefabframe.h" +#include "parseutil.h" +#include "currentselectedmetatilespixmapitem.h" + +#include +#include +#include +#include +#include +#include +#include + + +void Prefab::loadPrefabs() { + this->items.clear(); + QString filepath = projectConfig.getPrefabFilepath(); + if (filepath.isEmpty()) return; + + ParseUtil parser; + QJsonDocument prefabDoc; + if (!QFile::exists(filepath) || !parser.tryParseJsonFile(&prefabDoc, filepath)) { + QString relativePath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath); + if (!parser.tryParseJsonFile(&prefabDoc, relativePath)) { + logError(QString("Failed to prefab data from %1").arg(filepath)); + return; + } + } + + QJsonArray prefabs = prefabDoc.array(); + if (prefabs.size() == 0) { + logWarn(QString("Prefabs array is empty or missing in %1.").arg(filepath)); + return; + } + + for (int i = 0; i < prefabs.size(); i++) { + QJsonObject prefabObj = prefabs[i].toObject(); + if (prefabObj.isEmpty()) + continue; + + int width = prefabObj["width"].toInt(); + int height = prefabObj["height"].toInt(); + if (width <= 0 || height <= 0) + continue; + + QString name = prefabObj["name"].toString(); + QString primaryTileset = prefabObj["primary_tileset"].toString(); + QString secondaryTileset = prefabObj["secondary_tileset"].toString(); + + MetatileSelection selection; + selection.dimensions = QPoint(width, height); + selection.hasCollision = true; + for (int j = 0; j < width * height; j++) { + selection.metatileItems.append(MetatileSelectionItem{false, 0}); + selection.collisionItems.append(CollisionSelectionItem{false, 0, 0}); + } + QJsonArray metatiles = prefabObj["metatiles"].toArray(); + for (int j = 0; j < metatiles.size(); j++) { + QJsonObject metatileObj = metatiles[j].toObject(); + int x = metatileObj["x"].toInt(); + int y = metatileObj["y"].toInt(); + if (x < 0 || x >= width || y < 0 || y >= height) + continue; + int index = y * width + x; + selection.metatileItems[index].enabled = true; + selection.metatileItems[index].metatileId = metatileObj["metatile_id"].toInt(); + selection.collisionItems[index].enabled = true; + selection.collisionItems[index].collision = metatileObj["collision"].toInt(); + selection.collisionItems[index].elevation = metatileObj["elevation"].toInt(); + } + + this->items.append(PrefabItem{name, primaryTileset, secondaryTileset, selection}); + } +} + +QList Prefab::getPrefabsForTilesets(QString primaryTileset, QString secondaryTileset) { + QList filteredPrefabs; + for (auto item : this->items) { + if ((item.primaryTileset.isEmpty() || item.primaryTileset == primaryTileset) && + (item.secondaryTileset.isEmpty() || item.secondaryTileset == secondaryTileset)) { + filteredPrefabs.append(item); + } + } + return filteredPrefabs; +} + +void Prefab::initPrefabUI(QWidget *prefabWidget, QLabel *emptyPrefabLabel, QString primaryTileset, QString secondaryTileset, Map *map) { + this->loadPrefabs(); + QList prefabs = this->getPrefabsForTilesets(primaryTileset, secondaryTileset); + if (prefabs.isEmpty()) { + emptyPrefabLabel->setVisible(true); + return; + } + + emptyPrefabLabel->setVisible(false); + for (auto item : this->items) { + PrefabFrame *frame = new PrefabFrame(); + frame->ui->label_Name->setText(item.name); + + auto scene = new QGraphicsScene; + scene->addPixmap(drawMetatileSelection(item.selection, map)); + scene->setSceneRect(scene->itemsBoundingRect()); + frame->ui->graphicsView_Prefab->setScene(scene); + frame->ui->graphicsView_Prefab->setFixedSize(scene->itemsBoundingRect().width() + 2, + scene->itemsBoundingRect().height() + 2); + + prefabWidget->layout()->addWidget(frame); + } + auto spacer = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding); + prefabWidget->layout()->addItem(spacer); +} + + +Prefab prefab; diff --git a/src/ui/prefabframe.cpp b/src/ui/prefabframe.cpp new file mode 100644 index 00000000..6fbe0b0a --- /dev/null +++ b/src/ui/prefabframe.cpp @@ -0,0 +1,15 @@ +#include "prefabframe.h" + +#include "ui_prefabframe.h" + +PrefabFrame::PrefabFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::PrefabFrame) +{ + ui->setupUi(this); +} + +PrefabFrame::~PrefabFrame() +{ + delete ui; +}