Add ability to import new tiles
This commit is contained in:
parent
a77b76988b
commit
3ca284d5f3
9 changed files with 159 additions and 42 deletions
|
@ -383,6 +383,9 @@
|
||||||
<string>File</string>
|
<string>File</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="actionSave_Tileset"/>
|
<addaction name="actionSave_Tileset"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionImport_Primary_Tiles"/>
|
||||||
|
<addaction name="actionImport_Secondary_Tiles"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="menuFile"/>
|
<addaction name="menuFile"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -395,6 +398,16 @@
|
||||||
<string>Ctrl+S</string>
|
<string>Ctrl+S</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionImport_Primary_Tiles">
|
||||||
|
<property name="text">
|
||||||
|
<string>Import Primary Tiles</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionImport_Secondary_Tiles">
|
||||||
|
<property name="text">
|
||||||
|
<string>Import Secondary Tiles</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|
|
@ -21,6 +21,8 @@ public:
|
||||||
QString callback_label;
|
QString callback_label;
|
||||||
QString metatile_attrs_label;
|
QString metatile_attrs_label;
|
||||||
QString metatile_attrs_path;
|
QString metatile_attrs_path;
|
||||||
|
QString tilesImagePath;
|
||||||
|
QImage tilesImage;
|
||||||
|
|
||||||
QList<QImage> *tiles = nullptr;
|
QList<QImage> *tiles = nullptr;
|
||||||
QList<Metatile*> *metatiles = nullptr;
|
QList<Metatile*> *metatiles = nullptr;
|
||||||
|
|
|
@ -74,6 +74,8 @@ public:
|
||||||
void readMapsWithConnections();
|
void readMapsWithConnections();
|
||||||
void loadMapTilesets(Map*);
|
void loadMapTilesets(Map*);
|
||||||
void loadTilesetAssets(Tileset*);
|
void loadTilesetAssets(Tileset*);
|
||||||
|
void loadTilesetTiles(Tileset*, QImage);
|
||||||
|
void loadTilesetMetatiles(Tileset*);
|
||||||
|
|
||||||
void saveBlockdata(Map*);
|
void saveBlockdata(Map*);
|
||||||
void saveMapBorder(Map*);
|
void saveMapBorder(Map*);
|
||||||
|
@ -133,6 +135,7 @@ private:
|
||||||
void saveMapConnections(Map*);
|
void saveMapConnections(Map*);
|
||||||
void saveTilesetMetatileAttributes(Tileset*);
|
void saveTilesetMetatileAttributes(Tileset*);
|
||||||
void saveTilesetMetatiles(Tileset*);
|
void saveTilesetMetatiles(Tileset*);
|
||||||
|
void saveTilesetTilesImage(Tileset*);
|
||||||
void updateMapsWithConnections(Map*);
|
void updateMapsWithConnections(Map*);
|
||||||
void saveMapsWithConnections();
|
void saveMapsWithConnections();
|
||||||
void saveMapLayoutsTable();
|
void saveMapLayoutsTable();
|
||||||
|
|
|
@ -41,12 +41,18 @@ private slots:
|
||||||
|
|
||||||
void on_actionSave_Tileset_triggered();
|
void on_actionSave_Tileset_triggered();
|
||||||
|
|
||||||
|
void on_actionImport_Primary_Tiles_triggered();
|
||||||
|
|
||||||
|
void on_actionImport_Secondary_Tiles_triggered();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initMetatileSelector();
|
void initMetatileSelector();
|
||||||
void initTileSelector();
|
void initTileSelector();
|
||||||
void initSelectedTileItem();
|
void initSelectedTileItem();
|
||||||
void initMetatileLayersItem();
|
void initMetatileLayersItem();
|
||||||
void drawSelectedTile();
|
void drawSelectedTile();
|
||||||
|
void importTilesetTiles(Tileset*, bool);
|
||||||
|
void refresh();
|
||||||
Ui::TilesetEditor *ui;
|
Ui::TilesetEditor *ui;
|
||||||
TilesetEditorMetatileSelector *metatileSelector = nullptr;
|
TilesetEditorMetatileSelector *metatileSelector = nullptr;
|
||||||
TilesetEditorTileSelector *tileSelector = nullptr;
|
TilesetEditorTileSelector *tileSelector = nullptr;
|
||||||
|
|
|
@ -24,6 +24,8 @@ Tileset* Tileset::copy() {
|
||||||
copy->callback_label = this->callback_label;
|
copy->callback_label = this->callback_label;
|
||||||
copy->metatile_attrs_label = this->metatile_attrs_label;
|
copy->metatile_attrs_label = this->metatile_attrs_label;
|
||||||
copy->metatile_attrs_path = this->metatile_attrs_path;
|
copy->metatile_attrs_path = this->metatile_attrs_path;
|
||||||
|
copy->tilesImage = this->tilesImage.copy();
|
||||||
|
copy->tilesImagePath = this->tilesImagePath;
|
||||||
copy->tiles = new QList<QImage>;
|
copy->tiles = new QList<QImage>;
|
||||||
for (QImage tile : *this->tiles) {
|
for (QImage tile : *this->tiles) {
|
||||||
copy->tiles->append(tile.copy());
|
copy->tiles->append(tile.copy());
|
||||||
|
|
|
@ -965,7 +965,6 @@ void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)
|
||||||
{
|
{
|
||||||
if (map->layout->tileset_primary_label != tilesetLabel || forceLoad)
|
if (map->layout->tileset_primary_label != tilesetLabel || forceLoad)
|
||||||
{
|
{
|
||||||
qDebug() << "updatePrimaryTileset";
|
|
||||||
map->layout->tileset_primary_label = tilesetLabel;
|
map->layout->tileset_primary_label = tilesetLabel;
|
||||||
map->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad);
|
map->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad);
|
||||||
emit tilesetChanged(map->name);
|
emit tilesetChanged(map->name);
|
||||||
|
@ -974,7 +973,7 @@ void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)
|
||||||
|
|
||||||
void Editor::updateSecondaryTileset(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_label = tilesetLabel;
|
||||||
map->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad);
|
map->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad);
|
||||||
|
|
|
@ -1244,7 +1244,7 @@ void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected)
|
||||||
void MainWindow::on_actionTileset_Editor_triggered()
|
void MainWindow::on_actionTileset_Editor_triggered()
|
||||||
{
|
{
|
||||||
if (!this->tilesetEditor) {
|
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)));
|
connect(this->tilesetEditor, SIGNAL(tilesetsSaved(QString, QString)), this, SLOT(onTilesetsSaved(QString, QString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -581,6 +581,8 @@ void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||||
saveTilesetMetatileAttributes(secondaryTileset);
|
saveTilesetMetatileAttributes(secondaryTileset);
|
||||||
saveTilesetMetatiles(primaryTileset);
|
saveTilesetMetatiles(primaryTileset);
|
||||||
saveTilesetMetatiles(secondaryTileset);
|
saveTilesetMetatiles(secondaryTileset);
|
||||||
|
saveTilesetTilesImage(primaryTileset);
|
||||||
|
saveTilesetTilesImage(secondaryTileset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Project::saveTilesetMetatileAttributes(Tileset *tileset) {
|
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) {
|
void Project::loadMapTilesets(Map* map) {
|
||||||
if (map->layout->has_unsaved_changes) {
|
if (map->layout->has_unsaved_changes) {
|
||||||
return;
|
return;
|
||||||
|
@ -841,22 +848,59 @@ void Project::loadTilesetAssets(Tileset* tileset) {
|
||||||
tileset->metatile_attrs_path = dir_path + "/metatile_attributes.bin";
|
tileset->metatile_attrs_path = dir_path + "/metatile_attributes.bin";
|
||||||
}
|
}
|
||||||
|
|
||||||
// tiles
|
|
||||||
tiles_path = fixGraphicPath(tiles_path);
|
tiles_path = fixGraphicPath(tiles_path);
|
||||||
QImage *image = new QImage(tiles_path);
|
tileset->tilesImagePath = tiles_path;
|
||||||
//image->setColor(0, qRgb(0xff, 0, 0)); // debug
|
QImage image = QImage(tileset->tilesImagePath);
|
||||||
|
this->loadTilesetTiles(tileset, image);
|
||||||
|
this->loadTilesetMetatiles(tileset);
|
||||||
|
|
||||||
|
// palettes
|
||||||
|
QList<QList<QRgb>> *palettes = new QList<QList<QRgb>>;
|
||||||
|
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<QRgb> 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<QImage> *tiles = new QList<QImage>;
|
QList<QImage> *tiles = new QList<QImage>;
|
||||||
int w = 8;
|
int w = 8;
|
||||||
int h = 8;
|
int h = 8;
|
||||||
for (int y = 0; y < image->height(); y += h)
|
for (int y = 0; y < image.height(); y += h)
|
||||||
for (int x = 0; x < image->width(); x += w) {
|
for (int x = 0; x < image.width(); x += w) {
|
||||||
QImage tile = image->copy(x, y, w, h);
|
QImage tile = image.copy(x, y, w, h);
|
||||||
tiles->append(tile);
|
tiles->append(tile);
|
||||||
}
|
}
|
||||||
|
tileset->tilesImage = image;
|
||||||
tileset->tiles = tiles;
|
tileset->tiles = tiles;
|
||||||
|
}
|
||||||
|
|
||||||
// metatiles
|
void Project::loadTilesetMetatiles(Tileset* tileset) {
|
||||||
QFile metatiles_file(tileset->metatiles_path);
|
QFile metatiles_file(tileset->metatiles_path);
|
||||||
if (metatiles_file.open(QIODevice::ReadOnly)) {
|
if (metatiles_file.open(QIODevice::ReadOnly)) {
|
||||||
QByteArray data = metatiles_file.readAll();
|
QByteArray data = metatiles_file.readAll();
|
||||||
|
@ -902,38 +946,6 @@ void Project::loadTilesetAssets(Tileset* tileset) {
|
||||||
} else {
|
} else {
|
||||||
qDebug() << QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path);
|
qDebug() << QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// palettes
|
|
||||||
QList<QList<QRgb>> *palettes = new QList<QList<QRgb>>;
|
|
||||||
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<QRgb> 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) {
|
Blockdata* Project::readBlockdata(QString path) {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#include "tileseteditor.h"
|
#include "tileseteditor.h"
|
||||||
#include "ui_tileseteditor.h"
|
#include "ui_tileseteditor.h"
|
||||||
#include "imageproviders.h"
|
#include "imageproviders.h"
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
TilesetEditor::TilesetEditor(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel, QWidget *parent) :
|
TilesetEditor::TilesetEditor(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel, QWidget *parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
|
@ -47,7 +50,10 @@ void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTi
|
||||||
Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
|
Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
|
||||||
this->primaryTileset = primaryTileset->copy();
|
this->primaryTileset = primaryTileset->copy();
|
||||||
this->secondaryTileset = secondaryTileset->copy();
|
this->secondaryTileset = secondaryTileset->copy();
|
||||||
|
this->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TilesetEditor::refresh() {
|
||||||
this->metatileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
|
this->metatileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
|
||||||
this->tileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
|
this->tileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
|
||||||
this->metatileLayersItem->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);
|
emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
|
||||||
this->ui->statusbar->showMessage(QString("Saved primary and secondary Tilesets!"), 5000);
|
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();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue