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;
+}