diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index ca3a5c2a..dbe3bc20 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -561,8 +561,8 @@ 0 0 - 545 - 587 + 522 + 601 @@ -876,8 +876,8 @@ 0 0 - 256 - 74 + 259 + 70 @@ -1064,10 +1064,10 @@ - 8 + 0 0 - 221 - 328 + 263 + 338 @@ -1344,8 +1344,8 @@ 0 0 - 371 - 643 + 385 + 652 @@ -1617,8 +1617,8 @@ 0 0 - 430 - 534 + 432 + 554 @@ -2531,8 +2531,8 @@ 0 0 - 818 - 539 + 829 + 543 @@ -2748,6 +2748,7 @@ + @@ -2976,6 +2977,11 @@ Cursor Tile Outline + + + New Tileset + + diff --git a/forms/newmappopup.ui b/forms/newmappopup.ui index 9cab8872..50208a48 100644 --- a/forms/newmappopup.ui +++ b/forms/newmappopup.ui @@ -7,25 +7,22 @@ 0 0 410 - 508 + 515 New Map Options + + false + false - - - 0 - 0 - - QFrame::StyledPanel @@ -39,13 +36,6 @@ 12 - - - - Name - - - @@ -196,20 +186,6 @@ - - - - Allow Biking - - - - - - - Allow Escape Rope - - - @@ -217,6 +193,13 @@ + + + + Allow Biking + + + @@ -224,6 +207,13 @@ + + + + Allow Escape Rope + + + @@ -231,6 +221,13 @@ + + + + Name + + + @@ -249,7 +246,7 @@ 0 0 410 - 21 + 22 diff --git a/forms/newtilesetdialog.ui b/forms/newtilesetdialog.ui new file mode 100644 index 00000000..c0aa791d --- /dev/null +++ b/forms/newtilesetdialog.ui @@ -0,0 +1,222 @@ + + + NewTilesetDialog + + + + 0 + 0 + 400 + 190 + + + + + 0 + 0 + + + + Add new Tileset + + + + + 0 + 0 + 400 + 190 + + + + + 0 + 0 + + + + + 10 + + + 10 + + + 10 + + + 10 + + + 6 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 380 + 135 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 380 + 129 + + + + + 6 + + + 10 + + + 10 + + + 10 + + + + + Name + + + + + + + + + + Type + + + + + + + + Primary + + + + + Secondary + + + + + + + + Path + + + + + + + false + + + + + + + Symbol Name + + + + + + + false + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + + + buttonBox + accepted() + NewTilesetDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NewTilesetDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/include/core/tileset.h b/include/core/tileset.h index ce735e35..37a9ee78 100644 --- a/include/core/tileset.h +++ b/include/core/tileset.h @@ -35,6 +35,10 @@ public: static Metatile* getMetatile(int, Tileset*, Tileset*); static QList> getBlockPalettes(Tileset*, Tileset*); static QList getPalette(int, Tileset*, Tileset*); + + bool appendToHeaders(QString headerFile, QString friendlyName); + bool appendToGraphics(QString graphicsFile, QString friendlyName, bool primary); + bool appendToMetatiles(QString metatileFile, QString friendlyName, bool primary); }; #endif // TILESET_H diff --git a/include/mainwindow.h b/include/mainwindow.h index 4fbb2d22..84ba958d 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -17,6 +17,7 @@ #include "tileseteditor.h" #include "filterchildrenproxymodel.h" #include "newmappopup.h" +#include "newtilesetdialog.h" namespace Ui { class MainWindow; @@ -52,6 +53,7 @@ private slots: void onNewMapCreated(); void on_action_NewMap_triggered(); + void on_actionNew_Tileset_triggered(); void on_action_Save_triggered(); void on_tabWidget_2_currentChanged(int index); void on_action_Exit_triggered(); diff --git a/include/project.h b/include/project.h index 89306895..ee431a3c 100644 --- a/include/project.h +++ b/include/project.h @@ -90,6 +90,10 @@ public: void saveMapConstantsHeader(); void saveHealLocationStruct(Map*); void saveTilesets(Tileset*, Tileset*); + void saveTilesetMetatileAttributes(Tileset*); + void saveTilesetMetatiles(Tileset*); + void saveTilesetTilesImage(Tileset*); + void saveTilesetPalettes(Tileset*, bool); QList* parseAsm(QString text); QStringList getSongNames(); @@ -129,10 +133,6 @@ public: static int getNumPalettesPrimary(); static int getNumPalettesTotal(); private: - void saveTilesetMetatileAttributes(Tileset*); - void saveTilesetMetatiles(Tileset*); - void saveTilesetTilesImage(Tileset*); - void saveTilesetPalettes(Tileset*, bool); void updateMapLayout(Map*); void readCDefinesSorted(QString, QStringList, QStringList*); void readCDefinesSorted(QString, QStringList, QStringList*, QString, int); diff --git a/include/ui/newtilesetdialog.h b/include/ui/newtilesetdialog.h new file mode 100644 index 00000000..a292c67e --- /dev/null +++ b/include/ui/newtilesetdialog.h @@ -0,0 +1,32 @@ +#ifndef NEWTILESETDIALOG_H +#define NEWTILESETDIALOG_H + +#include +#include "project.h" + +namespace Ui { +class NewTilesetDialog; +} + +class NewTilesetDialog : public QDialog +{ + Q_OBJECT + +public: + explicit NewTilesetDialog(Project *project, QWidget *parent = nullptr); + ~NewTilesetDialog(); + QString path; + QString fullSymbolName; + QString friendlyName; + bool isSecondary; + +private slots: + void NameOrSecondaryChanged(); + void SecondaryChanged(); + +private: + Ui::NewTilesetDialog *ui; + Project *project = nullptr; +}; + +#endif // NEWTILESETDIALOG_H diff --git a/porymap.pro b/porymap.pro index cfd02755..eaf6da10 100644 --- a/porymap.pro +++ b/porymap.pro @@ -60,7 +60,8 @@ SOURCES += src/core/block.cpp \ src/mainwindow.cpp \ src/project.cpp \ src/settings.cpp \ - src/log.cpp + src/log.cpp \ + src/ui/newtilesetdialog.cpp HEADERS += include/core/block.h \ include/core/blockdata.h \ @@ -109,14 +110,16 @@ HEADERS += include/core/block.h \ include/mainwindow.h \ include/project.h \ include/settings.h \ - include/log.h + include/log.h \ + include/ui/newtilesetdialog.h FORMS += forms/mainwindow.ui \ forms/eventpropertiesframe.ui \ forms/tileseteditor.ui \ forms/paletteeditor.ui \ forms/newmappopup.ui \ - forms/aboutporymap.ui + forms/aboutporymap.ui \ + forms/newtilesetdialog.ui RESOURCES += \ resources/images.qrc diff --git a/resources/images.qrc b/resources/images.qrc index 2955b156..1f4fbf12 100644 --- a/resources/images.qrc +++ b/resources/images.qrc @@ -30,5 +30,6 @@ icons/sort_number.ico icons/collapse_all.ico icons/expand_all.ico + images/blank_tileset.png diff --git a/resources/images/blank_tileset.png b/resources/images/blank_tileset.png new file mode 100644 index 00000000..f71b1459 Binary files /dev/null and b/resources/images/blank_tileset.png differ diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index 430f2582..710eedfb 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -1,6 +1,7 @@ #include "tileset.h" #include "metatile.h" #include "project.h" +#include "log.h" #include #include @@ -86,3 +87,70 @@ QList Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset } return paletteTable; } + +bool Tileset::appendToHeaders(QString headerFile, QString friendlyName){ + QFile file(headerFile); + if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) { + logError(QString("Could not write to file \"%1\"").arg(headerFile)); + return false; + } + QString dataString = "\r\n\t.align 2\r\n"; + dataString.append(QString("%1::\r\n").arg(this->name)); + dataString.append(QString("\t.byte %1 @ is compressed\r\n").arg(this->is_compressed)); + dataString.append(QString("\t.byte %1 @ is secondary\r\n").arg(this->is_secondary)); + dataString.append(QString("\t.byte %1\r\n").arg(this->padding)); + dataString.append(QString("\t.4byte gTilesetTiles_%1\r\n").arg(friendlyName)); + dataString.append(QString("\t.4byte gTilesetPalettes_%1\r\n").arg(friendlyName)); + dataString.append(QString("\t.4byte gMetatiles_%1\r\n").arg(friendlyName)); + dataString.append(QString("\t.4byte gMetatileAttributes_%1\r\n").arg(friendlyName)); + dataString.append("\t.4byte NULL\r\n"); + file.write(dataString.toUtf8()); + file.flush(); + file.close(); + return true; +} + +bool Tileset::appendToGraphics(QString graphicsFile, QString friendlyName, bool primary) { + int startPaletteId = primary ? 0 : Project::getNumPalettesPrimary(); + int endPaletteId = primary ? Project::getNumPalettesPrimary() : Project::getNumPalettesTotal(); + QString primaryString = primary ? "primary" : "secondary"; + QFile file(graphicsFile); + if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) { + logError(QString("Could not write to file \"%1\"").arg(graphicsFile)); + return false; + } + QString dataString = "\r\n\t.align 2\r\n"; + dataString.append(QString("gTilesetPalettes_%1::\r\n").arg(friendlyName)); + for(int i = startPaletteId; i < endPaletteId; ++i) { + QString paletteString; + paletteString.sprintf("%02d.gbapal", i); + dataString.append(QString("\t.incbin \"data/tilesets/%1/%2/palettes/%3\"\r\n").arg(primaryString, friendlyName.toLower(), paletteString)); + + } + dataString.append("\r\n\t.align 2\r\n"); + dataString.append(QString("gTilesetTiles_%1::\r\n").arg(friendlyName)); + dataString.append(QString("\t.incbin \"data/tilesets/%1/%2/tiles.4bpp.lz\"\r\n").arg(primaryString, friendlyName.toLower())); + file.write(dataString.toUtf8()); + file.flush(); + file.close(); + return true; +} + +bool Tileset::appendToMetatiles(QString metatileFile, QString friendlyName, bool primary) { + QString primaryString = primary ? "primary" : "secondary"; + QFile file(metatileFile); + if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) { + logError(QString("Could not write to file \"%1\"").arg(metatileFile)); + return false; + } + QString dataString = "\r\n\t.align 1\r\n"; + dataString.append(QString("gMetatiles_%1::\r\n").arg(friendlyName)); + dataString.append(QString("\t.incbin \"data/tilesets/%1/%2/metatiles.bin\"\r\n").arg(primaryString, friendlyName.toLower())); + dataString.append(QString("\r\n\t.align 1\r\n")); + dataString.append(QString("gMetatileAttributes_%1::\r\n").arg(friendlyName)); + dataString.append(QString("\t.incbin \"data/tilesets/%1/%2/metatile_attributes.bin\"").arg(primaryString, friendlyName.toLower())); + file.write(dataString.toUtf8()); + file.flush(); + file.close(); + return true; +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 42ee5900..bd2586c7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -10,6 +10,7 @@ #include "currentselectedmetatilespixmapitem.h" #include "customattributestable.h" + #include #include #include @@ -852,6 +853,97 @@ void MainWindow::on_action_NewMap_triggered() { openNewMapPopupWindow(MapSortOrder::Group, 0); } +void MainWindow::on_actionNew_Tileset_triggered() { + NewTilesetDialog *createTilesetDialog = new NewTilesetDialog(editor->project, this); + if(createTilesetDialog->exec() == QDialog::Accepted){ + if(createTilesetDialog->friendlyName.isEmpty()) { + logError(QString("Tried to create a directory with an empty name.")); + QMessageBox msgBox(this); + msgBox.setText("Failed to add new tileset."); + QString message = QString("The given name was empty."); + msgBox.setInformativeText(message); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + QString fullDirectoryPath = editor->project->root + createTilesetDialog->path; + QDir directory; + if(directory.exists(fullDirectoryPath)) { + logError(QString("Could not create tileset, could not create directory \"%1\", it already exists.").arg(fullDirectoryPath)); + QMessageBox msgBox(this); + msgBox.setText("Failed to add new tileset."); + QString message = QString("The tileset already exists, view porymap.log for a complete description of the error."); + msgBox.setInformativeText(message); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + directory.mkdir(fullDirectoryPath); + directory.mkdir(fullDirectoryPath + "/palettes"); + Tileset *newSet = new Tileset(); + newSet->name = createTilesetDialog->fullSymbolName; + newSet->tilesImagePath = fullDirectoryPath + "/tiles.png"; + newSet->metatiles_path = fullDirectoryPath + "/metatiles.bin"; + newSet->metatile_attrs_path = fullDirectoryPath + "/metatile_attributes.bin"; + newSet->is_secondary = createTilesetDialog->isSecondary ? "TRUE" : "FALSE"; + int numMetaTiles = createTilesetDialog->isSecondary ? (Project::getNumTilesTotal() - Project::getNumTilesPrimary()) : Project::getNumTilesPrimary(); + QImage *tilesImage = new QImage(":/images/blank_tileset.png"); + editor->project->loadTilesetTiles(newSet, *tilesImage); + newSet->metatiles = new QList(); + for(int i = 0; i < numMetaTiles; ++i) { + Metatile *mt = new Metatile(); + for(int j = 0; j < 8; ++j){ + Tile *tile = new Tile(); + //Create a checkerboard-style dummy tileset + if(((i / 8) % 2) == 0) + tile->tile = ((i % 2) == 0) ? 1 : 2; + else + tile->tile = ((i % 2) == 1) ? 1 : 2; + tile->xflip = false; + tile->yflip = false; + tile->palette = 0; + mt->tiles->append(*tile); + } + mt->behavior = 0; + mt->layerType = 0; + + newSet->metatiles->append(mt); + } + newSet->palettes = new QList>(); + newSet->palettePaths = *new QList(); + for(int i = 0; i < 16; ++i) { + QList *currentPal = new QList(); + for(int i = 0; i < 16;++i) { + currentPal->append(qRgb(0,0,0)); + } + newSet->palettes->append(*currentPal); + QString fileName; + fileName.sprintf("%02d.pal", i); + newSet->palettePaths.append(fullDirectoryPath+"/palettes/" + fileName); + } + (*newSet->palettes)[0][1] = qRgb(255,0,255); + newSet->is_compressed = "TRUE"; + newSet->padding = "0"; + editor->project->saveTilesetTilesImage(newSet); + editor->project->saveTilesetMetatiles(newSet); + editor->project->saveTilesetMetatileAttributes(newSet); + editor->project->saveTilesetPalettes(newSet, !createTilesetDialog->isSecondary); + + //append to tileset specific files + + newSet->appendToHeaders(editor->project->root + "/data/tilesets/headers.inc", createTilesetDialog->friendlyName); + newSet->appendToGraphics(editor->project->root + "/data/tilesets/graphics.inc", createTilesetDialog->friendlyName, !createTilesetDialog->isSecondary); + newSet->appendToMetatiles(editor->project->root + "/data/tilesets/metatiles.inc", createTilesetDialog->friendlyName, !createTilesetDialog->isSecondary); + if(!createTilesetDialog->isSecondary) { + this->ui->comboBox_PrimaryTileset->addItem(createTilesetDialog->fullSymbolName); + } else { + this->ui->comboBox_SecondaryTileset->addItem(createTilesetDialog->fullSymbolName); + } + } +} + void MainWindow::onTilesetChanged(QString mapName) { setMap(mapName); diff --git a/src/ui/newtilesetdialog.cpp b/src/ui/newtilesetdialog.cpp new file mode 100644 index 00000000..a2d5f3f4 --- /dev/null +++ b/src/ui/newtilesetdialog.cpp @@ -0,0 +1,40 @@ +#include "newtilesetdialog.h" +#include "ui_newtilesetdialog.h" +#include +#include "project.h" + +NewTilesetDialog::NewTilesetDialog(Project* project, QWidget *parent) : + QDialog(parent), + ui(new Ui::NewTilesetDialog) +{ + ui->setupUi(this); + this->setFixedSize(this->width(), this->height()); + this->project = project; + //only allow characters valid for a symbol + QRegExp expression("[-_.A-Za-z0-9]+$"); + QRegExpValidator *validator = new QRegExpValidator(expression); + this->ui->nameLineEdit->setValidator(validator); + + connect(this->ui->nameLineEdit, &QLineEdit::textChanged, this, &NewTilesetDialog::NameOrSecondaryChanged); + connect(this->ui->typeComboBox, &QComboBox::currentTextChanged, this, &NewTilesetDialog::SecondaryChanged); + //connect(this->ui->toolButton, &QToolButton::clicked, this, &NewTilesetDialog::ChangeFilePath); + this->SecondaryChanged(); +} + +NewTilesetDialog::~NewTilesetDialog() +{ + delete ui; +} + +void NewTilesetDialog::SecondaryChanged(){ + this->isSecondary = (this->ui->typeComboBox->currentIndex() == 1); + NameOrSecondaryChanged(); +} + +void NewTilesetDialog::NameOrSecondaryChanged() { + this->friendlyName = this->ui->nameLineEdit->text(); + this->fullSymbolName = "gTileset_" + this->friendlyName; + this->ui->symbolNameLineEdit->setText(this->fullSymbolName); + this->path = QString("/data/tilesets/") + (this->isSecondary ? "secondary/" : "primary/") + this->friendlyName.toLower(); + this->ui->pathLineEdit->setText(this->path); +}