diff --git a/forms/tileseteditor.ui b/forms/tileseteditor.ui index edb0f306..90b0cc15 100644 --- a/forms/tileseteditor.ui +++ b/forms/tileseteditor.ui @@ -7,7 +7,7 @@ 0 0 700 - 600 + 700 @@ -198,6 +198,16 @@ false + + + + Metatile Label (Optional) + + + + + + diff --git a/include/core/metatile.h b/include/core/metatile.h index 955c4264..b1be4d75 100644 --- a/include/core/metatile.h +++ b/include/core/metatile.h @@ -3,6 +3,7 @@ #include "tile.h" #include +#include class Metatile { @@ -12,6 +13,7 @@ public: QList *tiles = nullptr; uint8_t behavior; uint8_t layerType; + QString label; Metatile *copy(); void copyInPlace(Metatile*); diff --git a/include/project.h b/include/project.h index 7669632f..da0ea2b0 100644 --- a/include/project.h +++ b/include/project.h @@ -91,6 +91,7 @@ public: void loadTilesetAssets(Tileset*); void loadTilesetTiles(Tileset*, QImage); void loadTilesetMetatiles(Tileset*); + void loadTilesetMetatileLabels(Tileset*); void saveLayoutBlockdata(Map*); void saveLayoutBorder(Map*); @@ -103,6 +104,7 @@ public: void saveMapConstantsHeader(); void saveHealLocationStruct(Map*); void saveTilesets(Tileset*, Tileset*); + void saveTilesetMetatileLabels(Tileset*, Tileset*); void saveTilesetMetatileAttributes(Tileset*); void saveTilesetMetatiles(Tileset*); void saveTilesetTilesImage(Tileset*); diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h index 6e3ed3df..1095b0be 100644 --- a/include/ui/tileseteditor.h +++ b/include/ui/tileseteditor.h @@ -73,6 +73,8 @@ private slots: void on_comboBox_metatileBehaviors_activated(const QString &arg1); + void on_lineEdit_metatileLabel_editingFinished(); + void on_comboBox_layerType_activated(int arg1); void on_actionExport_Primary_Tiles_Image_triggered(); @@ -93,6 +95,7 @@ private: void importTilesetTiles(Tileset*, bool); void importTilesetMetatiles(Tileset*, bool); void refresh(); + void saveMetatileLabel(); Ui::TilesetEditor *ui; History metatileHistory; TilesetEditorMetatileSelector *metatileSelector = nullptr; diff --git a/src/core/metatile.cpp b/src/core/metatile.cpp index e239085d..c35a7fcf 100644 --- a/src/core/metatile.cpp +++ b/src/core/metatile.cpp @@ -12,6 +12,7 @@ Metatile* Metatile::copy() { copy->behavior = this->behavior; copy->layerType = this->layerType; copy->tiles = new QList; + copy->label = this->label; for (Tile tile : *this->tiles) { copy->tiles->append(tile); } @@ -21,6 +22,7 @@ Metatile* Metatile::copy() { void Metatile::copyInPlace(Metatile *other) { this->behavior = other->behavior; this->layerType = other->layerType; + this->label = other->label; for (int i = 0; i < this->tiles->length(); i++) { (*this->tiles)[i] = other->tiles->at(i); } diff --git a/src/editor.cpp b/src/editor.cpp index fb062005..06361e30 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -349,8 +349,14 @@ void Editor::onHoveredMovementPermissionCleared() { } void Editor::onHoveredMetatileSelectionChanged(uint16_t metatileId) { - QString message = QString("Metatile: 0x%1") - .arg(QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper()); + Metatile *metatile = Tileset::getMetatile(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary); + QString message; + QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper(); + if (metatile && metatile->label.size() != 0) { + message = QString("Metatile: 0x%1 \"%2\"").arg(hexString, metatile->label); + } else { + message = QString("Metatile: 0x%1").arg(hexString); + } this->ui->statusBar->showMessage(message); } diff --git a/src/project.cpp b/src/project.cpp index 477ba1a3..bd0c74aa 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -621,6 +621,7 @@ void Project::saveHealLocationStruct(Map *map) { } void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { + saveTilesetMetatileLabels(primaryTileset, secondaryTileset); saveTilesetMetatileAttributes(primaryTileset); saveTilesetMetatileAttributes(secondaryTileset); saveTilesetMetatiles(primaryTileset); @@ -631,6 +632,90 @@ void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { saveTilesetPalettes(secondaryTileset, false); } +void Project::saveTilesetMetatileLabels(Tileset *primaryTileset, Tileset *secondaryTileset) { + QString filepath = root + "/include/constants/metatile_labels.h"; + QString originalText = readTextFile(filepath); + + QString primaryPrefix = QString("METATILE_%1_").arg(QString(primaryTileset->name).replace("gTileset_", "")); + QString secondaryPrefix = QString("METATILE_%1_").arg(QString(secondaryTileset->name).replace("gTileset_", "")); + + QMap defines; + bool definesFileModified = false; + if (!originalText.isNull()) { + defines = readCDefines(originalText, (QStringList() << "METATILE_")); + + // Purge old entries from the file. + QStringList definesToRemove; + for (QString defineName : defines.keys()) { + if (defineName.startsWith(primaryPrefix) || defineName.startsWith(secondaryPrefix)) { + definesToRemove << defineName; + } + } + for (QString defineName : definesToRemove) { + defines.remove(defineName); + definesFileModified = true; + } + } + + // Add the new labels. + for (int i = 0; i < primaryTileset->metatiles->size(); i++) { + Metatile *metatile = primaryTileset->metatiles->at(i); + if (metatile->label.size() != 0) { + QString defineName = QString("%1%2").arg(primaryPrefix, metatile->label); + defines.insert(defineName, i); + definesFileModified = true; + } + } + for (int i = 0; i < secondaryTileset->metatiles->size(); i++) { + Metatile *metatile = secondaryTileset->metatiles->at(i); + if (metatile->label.size() != 0) { + QString defineName = QString("%1%2").arg(secondaryPrefix, metatile->label); + defines.insert(defineName, i + Project::num_tiles_primary); + definesFileModified = true; + } + } + + if (!definesFileModified) { + return; + } + + auto getTilesetFromLabel = [](QString labelName) { + return QRegularExpression("METATILE_(?[A-Za-z0-9]+)_").match(labelName).captured("tileset"); + }; + + QString outputText = "#ifndef GUARD_METATILE_LABELS_H\n"; + outputText += "#define GUARD_METATILE_LABELS_H\n"; + + for (int i = 0; i < defines.size();) { + QString defineName = defines.keys()[i]; + QString currentTileset = getTilesetFromLabel(defineName); + outputText += QString("\n// gTileset_%1\n").arg(currentTileset); + + int j = 0, longestLength = 0; + QMap definesOut; + + // Setup for pretty formatting. + while (i + j < defines.size() && getTilesetFromLabel(defines.keys()[i + j]) == currentTileset) { + defineName = defines.keys()[i + j]; + if (defineName.size() > longestLength) + longestLength = defineName.size(); + definesOut.insert(defineName, defines[defineName]); + j++; + } + for (QString defineName : definesOut.keys()) { + int value = defines[defineName]; + QString line = QString("#define %1 0x%2\n") + .arg(defineName, -1 * longestLength) + .arg(QString("%1").arg(value, 3, 16, QChar('0')).toUpper()); + outputText += line; + } + i += j; + } + + outputText += "\n#endif // GUARD_METATILE_LABELS_H\n"; + saveTextFile(filepath, outputText); +} + void Project::saveTilesetMetatileAttributes(Tileset *tileset) { QFile attrs_file(tileset->metatile_attrs_path); if (attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { @@ -1040,6 +1125,7 @@ void Project::loadTilesetAssets(Tileset* tileset) { QImage image = QImage(tileset->tilesImagePath); this->loadTilesetTiles(tileset, image); this->loadTilesetMetatiles(tileset); + this->loadTilesetMetatileLabels(tileset); // palettes QList> *palettes = new QList>; @@ -1142,6 +1228,30 @@ void Project::loadTilesetMetatiles(Tileset* tileset) { } } +void Project::loadTilesetMetatileLabels(Tileset* tileset) { + QString filepath = root + "/include/constants/metatile_labels.h"; + QString text = readTextFile(filepath); + + if (!text.isNull()) { + QString tilesetPrefix = QString("METATILE_%1_").arg(QString(tileset->name).replace("gTileset_", "")); + QMap labels = readCDefines(text, QStringList() << tilesetPrefix); + + for (QString labelName : labels.keys()) { + int metatileId = labels[labelName]; + // subtract Project::num_tiles_primary from secondary metatiles + Metatile *metatile = Tileset::getMetatile(metatileId - (tileset->is_secondary == "TRUE" ? Project::num_tiles_primary : 0), tileset, nullptr); + if (metatile) { + metatile->label = labelName.replace(tilesetPrefix, ""); + } else { + QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper(); + logError(QString("Metatile 0x%1 cannot be found in tileset '%2'").arg(hexString, tileset->name)); + } + } + } else { + logError(QString("Failed to read C defines file: '%1'").arg(filepath)); + } +} + Blockdata* Project::readBlockdata(QString path) { Blockdata *blockdata = new Blockdata; QFile file(path); diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 50dd760c..85b94c35 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -61,6 +61,11 @@ void TilesetEditor::init(Project *project, QString primaryTilesetLabel, QString this->ui->spinBox_paletteSelector->setMinimum(0); this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1); + //only allow characters valid for a symbol + QRegExp expression("[_A-Za-z0-9]*$"); + QRegExpValidator *validator = new QRegExpValidator(expression); + this->ui->lineEdit_metatileLabel->setValidator(validator); + this->initMetatileSelector(); this->initMetatileLayersItem(); this->initTileSelector(); @@ -179,8 +184,14 @@ void TilesetEditor::initMetatileLayersItem() { } void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) { - QString message = QString("Metatile: 0x%1") - .arg(QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper()); + Metatile *metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset); + QString message; + QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper(); + if (metatile && metatile->label.size() != 0) { + message = QString("Metatile: 0x%1 \"%2\"").arg(hexString, metatile->label); + } else { + message = QString("Metatile: 0x%1").arg(hexString); + } this->ui->statusbar->showMessage(message); } @@ -193,6 +204,7 @@ void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) { this->metatileLayersItem->setMetatile(metatile); this->metatileLayersItem->draw(); this->ui->comboBox_metatileBehaviors->setCurrentIndex(this->ui->comboBox_metatileBehaviors->findData(this->metatile->behavior)); + this->ui->lineEdit_metatileLabel->setText(this->metatile->label); this->ui->comboBox_layerType->setCurrentIndex(this->ui->comboBox_layerType->findData(this->metatile->layerType)); } @@ -317,6 +329,22 @@ void TilesetEditor::on_comboBox_metatileBehaviors_activated(const QString &metat } } +void TilesetEditor::on_lineEdit_metatileLabel_editingFinished() +{ + saveMetatileLabel(); +} + +void TilesetEditor::saveMetatileLabel() +{ + // Only commit if the field has changed. + if (this->metatile && this->metatile->label != this->ui->lineEdit_metatileLabel->text()) { + Metatile *prevMetatile = this->metatile->copy(); + this->metatile->label = this->ui->lineEdit_metatileLabel->text(); + MetatileHistoryItem *commit = new MetatileHistoryItem(metatileSelector->getSelectedMetatile(), prevMetatile, this->metatile->copy()); + metatileHistory.push(commit); + } +} + void TilesetEditor::on_comboBox_layerType_activated(int layerType) { if (this->metatile) { @@ -329,6 +357,8 @@ void TilesetEditor::on_comboBox_layerType_activated(int layerType) void TilesetEditor::on_actionSave_Tileset_triggered() { + saveMetatileLabel(); + this->project->saveTilesets(this->primaryTileset, this->secondaryTileset); emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name); if (this->paletteEditor) { @@ -680,7 +710,7 @@ void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary) msgBox.exec(); return; } -\ + // TODO: This is crude because it makes a history entry for every newly-imported metatile. // Revisit this when tiles and num metatiles are added to tileset editory history. int metatileIdBase = primary ? 0 : Project::getNumMetatilesPrimary();