diff --git a/forms/tileseteditor.ui b/forms/tileseteditor.ui
index db1dec68..735606d2 100644
--- a/forms/tileseteditor.ui
+++ b/forms/tileseteditor.ui
@@ -383,6 +383,9 @@
File
+
+
+
@@ -395,6 +398,16 @@
Ctrl+S
+
+
+ Import Primary Tiles
+
+
+
+
+ Import Secondary Tiles
+
+
diff --git a/include/core/tileset.h b/include/core/tileset.h
index c3d55b1d..aea9f458 100644
--- a/include/core/tileset.h
+++ b/include/core/tileset.h
@@ -21,6 +21,8 @@ public:
QString callback_label;
QString metatile_attrs_label;
QString metatile_attrs_path;
+ QString tilesImagePath;
+ QImage tilesImage;
QList *tiles = nullptr;
QList *metatiles = nullptr;
diff --git a/include/project.h b/include/project.h
index 4aa8d35e..21acd40a 100644
--- a/include/project.h
+++ b/include/project.h
@@ -74,6 +74,8 @@ public:
void readMapsWithConnections();
void loadMapTilesets(Map*);
void loadTilesetAssets(Tileset*);
+ void loadTilesetTiles(Tileset*, QImage);
+ void loadTilesetMetatiles(Tileset*);
void saveBlockdata(Map*);
void saveMapBorder(Map*);
@@ -133,6 +135,7 @@ private:
void saveMapConnections(Map*);
void saveTilesetMetatileAttributes(Tileset*);
void saveTilesetMetatiles(Tileset*);
+ void saveTilesetTilesImage(Tileset*);
void updateMapsWithConnections(Map*);
void saveMapsWithConnections();
void saveMapLayoutsTable();
diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h
index c179bfd0..9b6d6e67 100644
--- a/include/ui/tileseteditor.h
+++ b/include/ui/tileseteditor.h
@@ -41,12 +41,18 @@ private slots:
void on_actionSave_Tileset_triggered();
+ void on_actionImport_Primary_Tiles_triggered();
+
+ void on_actionImport_Secondary_Tiles_triggered();
+
private:
void initMetatileSelector();
void initTileSelector();
void initSelectedTileItem();
void initMetatileLayersItem();
void drawSelectedTile();
+ void importTilesetTiles(Tileset*, bool);
+ void refresh();
Ui::TilesetEditor *ui;
TilesetEditorMetatileSelector *metatileSelector = nullptr;
TilesetEditorTileSelector *tileSelector = nullptr;
diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp
index 5bb51e23..f62135ad 100644
--- a/src/core/tileset.cpp
+++ b/src/core/tileset.cpp
@@ -24,6 +24,8 @@ Tileset* Tileset::copy() {
copy->callback_label = this->callback_label;
copy->metatile_attrs_label = this->metatile_attrs_label;
copy->metatile_attrs_path = this->metatile_attrs_path;
+ copy->tilesImage = this->tilesImage.copy();
+ copy->tilesImagePath = this->tilesImagePath;
copy->tiles = new QList;
for (QImage tile : *this->tiles) {
copy->tiles->append(tile.copy());
diff --git a/src/editor.cpp b/src/editor.cpp
index 878e4a70..08f0c766 100644
--- a/src/editor.cpp
+++ b/src/editor.cpp
@@ -965,7 +965,6 @@ void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)
{
if (map->layout->tileset_primary_label != tilesetLabel || forceLoad)
{
- qDebug() << "updatePrimaryTileset";
map->layout->tileset_primary_label = tilesetLabel;
map->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad);
emit tilesetChanged(map->name);
@@ -974,7 +973,7 @@ void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)
void Editor::updateSecondaryTileset(QString tilesetLabel, bool forceLoad)
{
- if (map->layout->tileset_secondary_label != tilesetLabel)
+ if (map->layout->tileset_secondary_label != tilesetLabel || forceLoad)
{
map->layout->tileset_secondary_label = tilesetLabel;
map->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 83e0dccb..7f64fdca 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1244,7 +1244,7 @@ void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected)
void MainWindow::on_actionTileset_Editor_triggered()
{
if (!this->tilesetEditor) {
- this->tilesetEditor = new TilesetEditor(this->editor->project, this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label);
+ this->tilesetEditor = new TilesetEditor(this->editor->project, this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label, this);
connect(this->tilesetEditor, SIGNAL(tilesetsSaved(QString, QString)), this, SLOT(onTilesetsSaved(QString, QString)));
}
diff --git a/src/project.cpp b/src/project.cpp
index 2bc67c03..714868fa 100644
--- a/src/project.cpp
+++ b/src/project.cpp
@@ -581,6 +581,8 @@ void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
saveTilesetMetatileAttributes(secondaryTileset);
saveTilesetMetatiles(primaryTileset);
saveTilesetMetatiles(secondaryTileset);
+ saveTilesetTilesImage(primaryTileset);
+ saveTilesetTilesImage(secondaryTileset);
}
void Project::saveTilesetMetatileAttributes(Tileset *tileset) {
@@ -619,6 +621,11 @@ void Project::saveTilesetMetatiles(Tileset *tileset) {
}
}
+void Project::saveTilesetTilesImage(Tileset *tileset) {
+ qDebug() << QString("saving tiles png to '%1'").arg(tileset->tilesImagePath);
+ tileset->tilesImage.save(tileset->tilesImagePath);
+}
+
void Project::loadMapTilesets(Map* map) {
if (map->layout->has_unsaved_changes) {
return;
@@ -841,22 +848,59 @@ void Project::loadTilesetAssets(Tileset* tileset) {
tileset->metatile_attrs_path = dir_path + "/metatile_attributes.bin";
}
- // tiles
tiles_path = fixGraphicPath(tiles_path);
- QImage *image = new QImage(tiles_path);
- //image->setColor(0, qRgb(0xff, 0, 0)); // debug
+ tileset->tilesImagePath = tiles_path;
+ QImage image = QImage(tileset->tilesImagePath);
+ this->loadTilesetTiles(tileset, image);
+ this->loadTilesetMetatiles(tileset);
+ // palettes
+ QList> *palettes = new QList>;
+ for (int i = 0; i < palette_paths->length(); i++) {
+ QString path = palette_paths->value(i);
+ // the palettes are not compressed. this should never happen. it's only a precaution.
+ path = path.replace(QRegExp("\\.lz$"), "");
+ // TODO default to .pal (JASC-PAL)
+ // just use .gbapal for now
+ QFile file(path);
+ QList palette;
+ if (file.open(QIODevice::ReadOnly)) {
+ QByteArray data = file.readAll();
+ for (int j = 0; j < 16; j++) {
+ uint16_t word = data[j*2] & 0xff;
+ word += (data[j*2 + 1] & 0xff) << 8;
+ int red = word & 0x1f;
+ int green = (word >> 5) & 0x1f;
+ int blue = (word >> 10) & 0x1f;
+ QRgb color = qRgb(red * 8, green * 8, blue * 8);
+ palette.append(color);
+ }
+ } else {
+ for (int j = 0; j < 16; j++) {
+ palette.append(qRgb(j * 16, j * 16, j * 16));
+ }
+ qDebug() << QString("Could not open palette path '%1'").arg(path);
+ }
+
+ palettes->append(palette);
+ }
+ tileset->palettes = palettes;
+}
+
+void Project::loadTilesetTiles(Tileset *tileset, QImage image) {
QList *tiles = new QList;
int w = 8;
int h = 8;
- for (int y = 0; y < image->height(); y += h)
- for (int x = 0; x < image->width(); x += w) {
- QImage tile = image->copy(x, y, w, h);
+ for (int y = 0; y < image.height(); y += h)
+ for (int x = 0; x < image.width(); x += w) {
+ QImage tile = image.copy(x, y, w, h);
tiles->append(tile);
}
+ tileset->tilesImage = image;
tileset->tiles = tiles;
+}
- // metatiles
+void Project::loadTilesetMetatiles(Tileset* tileset) {
QFile metatiles_file(tileset->metatiles_path);
if (metatiles_file.open(QIODevice::ReadOnly)) {
QByteArray data = metatiles_file.readAll();
@@ -902,38 +946,6 @@ void Project::loadTilesetAssets(Tileset* tileset) {
} else {
qDebug() << QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path);
}
-
- // palettes
- QList> *palettes = new QList>;
- for (int i = 0; i < palette_paths->length(); i++) {
- QString path = palette_paths->value(i);
- // the palettes are not compressed. this should never happen. it's only a precaution.
- path = path.replace(QRegExp("\\.lz$"), "");
- // TODO default to .pal (JASC-PAL)
- // just use .gbapal for now
- QFile file(path);
- QList palette;
- if (file.open(QIODevice::ReadOnly)) {
- QByteArray data = file.readAll();
- for (int j = 0; j < 16; j++) {
- uint16_t word = data[j*2] & 0xff;
- word += (data[j*2 + 1] & 0xff) << 8;
- int red = word & 0x1f;
- int green = (word >> 5) & 0x1f;
- int blue = (word >> 10) & 0x1f;
- QRgb color = qRgb(red * 8, green * 8, blue * 8);
- palette.append(color);
- }
- } else {
- for (int j = 0; j < 16; j++) {
- palette.append(qRgb(j * 16, j * 16, j * 16));
- }
- qDebug() << QString("Could not open palette path '%1'").arg(path);
- }
-
- palettes->append(palette);
- }
- tileset->palettes = palettes;
}
Blockdata* Project::readBlockdata(QString path) {
diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp
index 033d94c9..606fdc93 100644
--- a/src/ui/tileseteditor.cpp
+++ b/src/ui/tileseteditor.cpp
@@ -1,6 +1,9 @@
#include "tileseteditor.h"
#include "ui_tileseteditor.h"
#include "imageproviders.h"
+#include
+#include
+#include
TilesetEditor::TilesetEditor(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel, QWidget *parent) :
QMainWindow(parent),
@@ -47,7 +50,10 @@ void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTi
Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
this->primaryTileset = primaryTileset->copy();
this->secondaryTileset = secondaryTileset->copy();
+ this->refresh();
+}
+void TilesetEditor::refresh() {
this->metatileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
this->tileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
this->metatileLayersItem->setTilesets(this->primaryTileset, this->secondaryTileset);
@@ -209,3 +215,77 @@ void TilesetEditor::on_actionSave_Tileset_triggered()
emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
this->ui->statusbar->showMessage(QString("Saved primary and secondary Tilesets!"), 5000);
}
+
+void TilesetEditor::on_actionImport_Primary_Tiles_triggered()
+{
+ this->importTilesetTiles(this->primaryTileset, true);
+}
+
+void TilesetEditor::on_actionImport_Secondary_Tiles_triggered()
+{
+ this->importTilesetTiles(this->secondaryTileset, false);
+}
+
+void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
+ QString descriptor = primary ? "primary" : "secondary";
+ QString descriptorCaps = primary ? "Primary" : "Secondary";
+
+ QString filepath = QFileDialog::getOpenFileName(
+ this,
+ QString("Import %1 Tileset Tiles Image").arg(descriptorCaps),
+ this->project->root,
+ "Image Files (*.png)");
+ if (filepath.isEmpty()) {
+ return;
+ }
+
+ qDebug() << QString("Importing %1 tileset tiles '%2'").arg(descriptor).arg(filepath);
+
+ // Validate image dimensions.
+ QImage image = QImage(filepath);
+ if (image.width() == 0 || image.height() == 0 || image.width() % 8 != 0 || image.height() % 8 != 0) {
+ QMessageBox msgBox(this);
+ msgBox.setText("Failed to import tiles.");
+ msgBox.setInformativeText(QString("The image dimensions (%1 x %2) are invalid. Width and height must be multiples of 8 pixels.")
+ .arg(image.width())
+ .arg(image.height()));
+ msgBox.setDefaultButton(QMessageBox::Ok);
+ msgBox.setIcon(QMessageBox::Icon::Critical);
+ msgBox.exec();
+ return;
+ }
+
+ // Validate image is properly indexed to 16 colors.
+ if (image.colorCount() != 16) {
+ QMessageBox msgBox(this);
+ msgBox.setText("Failed to import tiles.");
+ msgBox.setInformativeText(QString("The image must be indexed and contain 16 total colors. The provided image has %1 indexed colors.")
+ .arg(image.colorCount()));
+ msgBox.setDefaultButton(QMessageBox::Ok);
+ msgBox.setIcon(QMessageBox::Icon::Critical);
+ msgBox.exec();
+ return;
+ }
+
+ // Validate total number of tiles in image.
+ int numTilesWide = image.width() / 16;
+ int numTilesHigh = image.height() / 16;
+ int totalTiles = numTilesHigh * numTilesWide;
+ int maxAllowedTiles = primary ? Project::getNumTilesPrimary() : Project::getNumTilesTotal() - Project::getNumTilesPrimary();
+ if (totalTiles > maxAllowedTiles) {
+ QMessageBox msgBox(this);
+ msgBox.setText("Failed to import tiles.");
+ msgBox.setInformativeText(QString("The maximum number of tiles allowed in the %1 tileset is %2, but the provided image contains %3 total tiles.")
+ .arg(descriptor)
+ .arg(maxAllowedTiles)
+ .arg(totalTiles));
+ msgBox.setDefaultButton(QMessageBox::Ok);
+ msgBox.setIcon(QMessageBox::Icon::Critical);
+ msgBox.exec();
+ return;
+ }
+
+ this->project->loadTilesetTiles(tileset, image);
+ this->project->loadTilesetMetatiles(tileset);
+ this->refresh();
+}