Move metatile labels to tilesets

This commit is contained in:
GriffinR 2023-02-14 14:10:05 -05:00
parent 72ae9dfc01
commit dbd6afb0c3
8 changed files with 118 additions and 70 deletions

View file

@ -70,7 +70,6 @@ public:
uint32_t encounterType; uint32_t encounterType;
uint32_t layerType; uint32_t layerType;
uint32_t unusedAttributes; uint32_t unusedAttributes;
QString label;
uint32_t getAttributes(); uint32_t getAttributes();
void setAttributes(uint32_t data); void setAttributes(uint32_t data);
@ -121,7 +120,6 @@ inline bool operator==(const Metatile &a, const Metatile &b) {
a.encounterType == b.encounterType && a.encounterType == b.encounterType &&
a.terrainType == b.terrainType && a.terrainType == b.terrainType &&
a.unusedAttributes == b.unusedAttributes && a.unusedAttributes == b.unusedAttributes &&
a.label == b.label &&
a.tiles == b.tiles; a.tiles == b.tiles;
} }

View file

@ -5,6 +5,7 @@
#include "metatile.h" #include "metatile.h"
#include "tile.h" #include "tile.h"
#include <QImage> #include <QImage>
#include <QHash>
class Tileset class Tileset
{ {
@ -28,12 +29,15 @@ public:
QList<QImage> tiles; QList<QImage> tiles;
QList<Metatile*> metatiles; QList<Metatile*> metatiles;
QHash<int, QString> metatileLabels;
QList<QList<QRgb>> palettes; QList<QList<QRgb>> palettes;
QList<QList<QRgb>> palettePreviews; QList<QList<QRgb>> palettePreviews;
static Tileset* getMetatileTileset(int, Tileset*, Tileset*); static Tileset* getMetatileTileset(int, Tileset*, Tileset*);
static Tileset* getTileTileset(int, Tileset*, Tileset*); static Tileset* getTileTileset(int, Tileset*, Tileset*);
static Metatile* getMetatile(int, Tileset*, Tileset*); static Metatile* getMetatile(int, Tileset*, Tileset*);
static QString getMetatileLabel(int, Tileset *, Tileset *, bool * isAlternateLabel = nullptr);
static bool setMetatileLabel(int, QString, Tileset *, Tileset *);
static QList<QList<QRgb>> getBlockPalettes(Tileset*, Tileset*, bool useTruePalettes = false); static QList<QList<QRgb>> getBlockPalettes(Tileset*, Tileset*, bool useTruePalettes = false);
static QList<QRgb> getPalette(int, Tileset*, Tileset*, bool useTruePalettes = false); static QList<QRgb> getPalette(int, Tileset*, Tileset*, bool useTruePalettes = false);
static bool metatileIsValid(uint16_t metatileId, Tileset *, Tileset *); static bool metatileIsValid(uint16_t metatileId, Tileset *, Tileset *);

View file

@ -127,7 +127,7 @@ private:
void importTilesetTiles(Tileset*, bool); void importTilesetTiles(Tileset*, bool);
void importTilesetMetatiles(Tileset*, bool); void importTilesetMetatiles(Tileset*, bool);
void refresh(); void refresh();
void saveMetatileLabel(); void commitMetatileLabel();
void closeEvent(QCloseEvent*); void closeEvent(QCloseEvent*);
void countMetatileUsage(); void countMetatileUsage();
void countTileUsage(); void countTileUsage();
@ -146,6 +146,7 @@ private:
Map *map = nullptr; Map *map = nullptr;
Metatile *metatile = nullptr; Metatile *metatile = nullptr;
Metatile *copiedMetatile = nullptr; Metatile *copiedMetatile = nullptr;
QString copiedMetatileLabel;
int paletteId; int paletteId;
bool tileXFlip; bool tileXFlip;
bool tileYFlip; bool tileYFlip;

View file

@ -20,6 +20,7 @@ Tileset::Tileset(const Tileset &other)
tilesImagePath(other.tilesImagePath), tilesImagePath(other.tilesImagePath),
tilesImage(other.tilesImage.copy()), tilesImage(other.tilesImage.copy()),
palettePaths(other.palettePaths), palettePaths(other.palettePaths),
metatileLabels(other.metatileLabels),
palettes(other.palettes), palettes(other.palettes),
palettePreviews(other.palettePreviews) palettePreviews(other.palettePreviews)
{ {
@ -44,6 +45,7 @@ Tileset &Tileset::operator=(const Tileset &other) {
tilesImagePath = other.tilesImagePath; tilesImagePath = other.tilesImagePath;
tilesImage = other.tilesImage.copy(); tilesImage = other.tilesImage.copy();
palettePaths = other.palettePaths; palettePaths = other.palettePaths;
metatileLabels = other.metatileLabels;
palettes = other.palettes; palettes = other.palettes;
palettePreviews = other.palettePreviews; palettePreviews = other.palettePreviews;
@ -82,13 +84,52 @@ Tileset* Tileset::getMetatileTileset(int metatileId, Tileset *primaryTileset, Ti
Metatile* Tileset::getMetatile(int metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) { Metatile* Tileset::getMetatile(int metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
Tileset *tileset = Tileset::getMetatileTileset(metatileId, primaryTileset, secondaryTileset); Tileset *tileset = Tileset::getMetatileTileset(metatileId, primaryTileset, secondaryTileset);
int index = Metatile::getIndexInTileset(metatileId);
if (!tileset) { if (!tileset) {
return nullptr; return nullptr;
} }
int index = Metatile::getIndexInTileset(metatileId);
return tileset->metatiles.value(index, nullptr); return tileset->metatiles.value(index, nullptr);
} }
// Metatile labels are stored per-tileset. When looking for a metatile label, first search in the tileset
// that the metatile belongs to. If one isn't found, search in the other tileset. Labels coming from the
// tileset that the metatile does not belong to cannot be edited via Porymap.
QString Tileset::getMetatileLabel(int metatileId, Tileset *primaryTileset, Tileset *secondaryTileset, bool * isAlternateLabel) {
Tileset *mainTileset = nullptr;
Tileset *backupTileset = nullptr;
if (isAlternateLabel) *isAlternateLabel = false;
if (metatileId < Project::getNumMetatilesPrimary()) {
mainTileset = primaryTileset;
backupTileset = secondaryTileset;
} else if (metatileId < Project::getNumMetatilesTotal()) {
mainTileset = secondaryTileset;
backupTileset = primaryTileset;
}
if (mainTileset && mainTileset->metatileLabels.contains(metatileId)) {
return mainTileset->metatileLabels.value(metatileId);
} else if (backupTileset && backupTileset->metatileLabels.contains(metatileId)) {
if (isAlternateLabel) *isAlternateLabel = true;
return backupTileset->metatileLabels.value(metatileId);
}
return QString();
}
bool Tileset::setMetatileLabel(int metatileId, QString label, Tileset *primaryTileset, Tileset *secondaryTileset) {
Tileset *tileset = Tileset::getMetatileTileset(metatileId, primaryTileset, secondaryTileset);
if (!tileset)
return false;
static const QRegularExpression expression("[_A-Za-z0-9]*$");
QRegularExpressionValidator validator(expression);
int pos = 0;
if (validator.validate(label, pos) != QValidator::Acceptable)
return false;
tileset->metatileLabels[metatileId] = label;
return true;
}
bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) { bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
if (metatileId >= Project::getNumMetatilesTotal()) if (metatileId >= Project::getNumMetatilesTotal())
return false; return false;

View file

@ -932,14 +932,13 @@ void Editor::onHoveredMovementPermissionCleared() {
QString Editor::getMetatileDisplayMessage(uint16_t metatileId) { QString Editor::getMetatileDisplayMessage(uint16_t metatileId) {
Metatile *metatile = Tileset::getMetatile(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary); Metatile *metatile = Tileset::getMetatile(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary);
QString label = Tileset::getMetatileLabel(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary);
QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper(); QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper();
QString message = QString("Metatile: 0x%1").arg(hexString); QString message = QString("Metatile: 0x%1").arg(hexString);
if (metatile) { if (label.size())
if (metatile->label.size()) message += QString(" \"%1\"").arg(label);
message += QString(" \"%1\"").arg(metatile->label); if (metatile && metatile->behavior) // Skip MB_NORMAL
if (metatile->behavior) // Skip MB_NORMAL
message += QString(", Behavior: %1").arg(this->project->metatileBehaviorMapInverse.value(metatile->behavior, QString::number(metatile->behavior))); message += QString(", Behavior: %1").arg(this->project->metatileBehaviorMapInverse.value(metatile->behavior, QString::number(metatile->behavior)));
}
return message; return message;
} }

View file

@ -913,22 +913,16 @@ void Project::saveTilesetMetatileLabels(Tileset *primaryTileset, Tileset *second
} }
// Add the new labels. // Add the new labels.
for (int i = 0; i < primaryTileset->metatiles.size(); i++) { for (int metatileId : primaryTileset->metatileLabels.keys()) {
Metatile *metatile = primaryTileset->metatiles.at(i); QString defineName = QString("%1%2").arg(primaryPrefix, primaryTileset->metatileLabels.value(metatileId));
if (metatile->label.size() != 0) { defines.insert(defineName, metatileId);
QString defineName = QString("%1%2").arg(primaryPrefix, metatile->label);
defines.insert(defineName, i);
definesFileModified = true; definesFileModified = true;
} }
} for (int metatileId : secondaryTileset->metatileLabels.keys()) {
for (int i = 0; i < secondaryTileset->metatiles.size(); i++) { QString defineName = QString("%1%2").arg(secondaryPrefix, secondaryTileset->metatileLabels.value(metatileId));
Metatile *metatile = secondaryTileset->metatiles.at(i); defines.insert(defineName, metatileId);
if (metatile->label.size() != 0) {
QString defineName = QString("%1%2").arg(secondaryPrefix, metatile->label);
defines.insert(defineName, i + Project::num_tiles_primary);
definesFileModified = true; definesFileModified = true;
} }
}
if (!definesFileModified) { if (!definesFileModified) {
return; return;
@ -1521,17 +1515,10 @@ bool Project::readTilesetMetatileLabels() {
void Project::loadTilesetMetatileLabels(Tileset* tileset) { void Project::loadTilesetMetatileLabels(Tileset* tileset) {
QString tilesetPrefix = QString("METATILE_%1_").arg(QString(tileset->name).replace("gTileset_", "")); QString tilesetPrefix = QString("METATILE_%1_").arg(QString(tileset->name).replace("gTileset_", ""));
// Reverse map for faster lookup by metatile id
for (QString labelName : metatileLabelsMap[tileset->name].keys()) { for (QString labelName : metatileLabelsMap[tileset->name].keys()) {
int metatileId = metatileLabelsMap[tileset->name][labelName]; int metatileId = metatileLabelsMap[tileset->name][labelName];
// subtract Project::num_tiles_primary from secondary metatiles tileset->metatileLabels[metatileId] = labelName.replace(tilesetPrefix, "");
int offset = tileset->is_secondary ? Project::num_tiles_primary : 0;
Metatile *metatile = Tileset::getMetatile(metatileId - offset, 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 (%2) cannot be found in tileset '%3'").arg(hexString, labelName, tileset->name));
}
} }
} }

View file

@ -598,32 +598,30 @@ Metatile * MainWindow::getMetatile(int metatileId) {
} }
QString MainWindow::getMetatileLabel(int metatileId) { QString MainWindow::getMetatileLabel(int metatileId) {
Metatile * metatile = this->getMetatile(metatileId); if (!this->editor || !this->editor->map || !this->editor->map->layout)
if (!metatile || metatile->label.size() == 0)
return QString(); return QString();
return metatile->label; return Tileset::getMetatileLabel(metatileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary);
} }
void MainWindow::setMetatileLabel(int metatileId, QString label) { void MainWindow::setMetatileLabel(int metatileId, QString label) {
Metatile * metatile = this->getMetatile(metatileId); if (!this->editor || !this->editor->map || !this->editor->map->layout)
if (!metatile)
return; return;
static const QRegularExpression expression("[_A-Za-z0-9]*$"); // If the Tileset Editor is opened on this metatile we need to update the text box
QRegularExpressionValidator validator(expression); if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatileId() == metatileId){
int pos = 0; this->tilesetEditor->setMetatileLabel(label);
if (validator.validate(label, pos) != QValidator::Acceptable) {
logError(QString("Invalid metatile label %1").arg(label));
return; return;
} }
if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatileId() == metatileId) { if (!Tileset::setMetatileLabel(metatileId, label, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary)) {
this->tilesetEditor->setMetatileLabel(label); logError("Failed to set metatile label. Must be a valid metatile id and a label containing only letters, numbers, and underscores.");
} else if (metatile->label != label) { return;
metatile->label = label; }
// The user may not have the Tileset Editor open. This forcefully saves the change for them.
// If they do have the Tileset Editor open, this has the unintended side effect of saving other unsaved label changes.
if (this->editor->project) if (this->editor->project)
this->editor->project->saveTilesetMetatileLabels(this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); this->editor->project->saveTilesetMetatileLabels(this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary);
}
} }
int MainWindow::getMetatileLayerType(int metatileId) { int MainWindow::getMetatileLayerType(int metatileId) {

View file

@ -343,13 +343,11 @@ void TilesetEditor::drawSelectedTiles() {
} }
void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) { void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) {
Metatile *metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset); QString label = Tileset::getMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
QString message;
QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper(); QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper();
if (metatile && metatile->label.size() != 0) { QString message = QString("Metatile: 0x%1").arg(hexString);
message = QString("Metatile: 0x%1 \"%2\"").arg(hexString, metatile->label); if (label.size() != 0) {
} else { message += QString(" \"%1\"").arg(label);
message = QString("Metatile: 0x%1").arg(hexString);
} }
this->ui->statusbar->showMessage(message); this->ui->statusbar->showMessage(message);
} }
@ -381,7 +379,12 @@ void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) {
this->metatileLayersItem->setMetatile(metatile); this->metatileLayersItem->setMetatile(metatile);
this->metatileLayersItem->draw(); this->metatileLayersItem->draw();
this->ui->graphicsView_metatileLayers->setFixedSize(this->metatileLayersItem->pixmap().width() + 2, this->metatileLayersItem->pixmap().height() + 2); this->ui->graphicsView_metatileLayers->setFixedSize(this->metatileLayersItem->pixmap().width() + 2, this->metatileLayersItem->pixmap().height() + 2);
this->ui->lineEdit_metatileLabel->setText(this->metatile->label);
bool isAlternateLabel = false;
QString label = Tileset::getMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset, &isAlternateLabel);
this->ui->lineEdit_metatileLabel->setText(label);
this->ui->lineEdit_metatileLabel->setReadOnly(isAlternateLabel);
setComboValue(this->ui->comboBox_metatileBehaviors, this->metatile->behavior); setComboValue(this->ui->comboBox_metatileBehaviors, this->metatile->behavior);
setComboValue(this->ui->comboBox_layerType, this->metatile->layerType); setComboValue(this->ui->comboBox_layerType, this->metatile->layerType);
setComboValue(this->ui->comboBox_encounterType, this->metatile->encounterType); setComboValue(this->ui->comboBox_encounterType, this->metatile->encounterType);
@ -532,24 +535,31 @@ void TilesetEditor::on_comboBox_metatileBehaviors_currentTextChanged(const QStri
void TilesetEditor::setMetatileLabel(QString label) void TilesetEditor::setMetatileLabel(QString label)
{ {
if (this->ui->lineEdit_metatileLabel->isReadOnly())
return;
this->ui->lineEdit_metatileLabel->setText(label); this->ui->lineEdit_metatileLabel->setText(label);
saveMetatileLabel(); commitMetatileLabel();
} }
void TilesetEditor::on_lineEdit_metatileLabel_editingFinished() void TilesetEditor::on_lineEdit_metatileLabel_editingFinished()
{ {
saveMetatileLabel(); commitMetatileLabel();
} }
void TilesetEditor::saveMetatileLabel() void TilesetEditor::commitMetatileLabel()
{ {
// TODO: Reimplement edit history for labels
// Only commit if the field has changed. // Only commit if the field has changed.
if (this->metatile && this->metatile->label != this->ui->lineEdit_metatileLabel->text()) { uint16_t metatileId = this->getSelectedMetatileId();
Metatile *prevMetatile = new Metatile(*this->metatile); QString currentLabel = Tileset::getMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
this->metatile->label = this->ui->lineEdit_metatileLabel->text(); QString newLabel = this->ui->lineEdit_metatileLabel->text();
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(), if (currentLabel != newLabel) {
//Metatile *prevMetatile = new Metatile(*this->metatile);
Tileset::setMetatileLabel(metatileId, newLabel, this->primaryTileset, this->secondaryTileset);
/*MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile)); prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit); metatileHistory.push(commit);*/
this->hasUnsavedChanges = true; this->hasUnsavedChanges = true;
} }
} }
@ -597,8 +607,6 @@ void TilesetEditor::on_actionSave_Tileset_triggered()
// when the tilesetsSaved signal is sent, it will be reset to the current map metatile // when the tilesetsSaved signal is sent, it will be reset to the current map metatile
uint16_t reselectMetatileID = this->metatileSelector->getSelectedMetatileId(); uint16_t reselectMetatileID = this->metatileSelector->getSelectedMetatileId();
saveMetatileLabel();
this->project->saveTilesets(this->primaryTileset, this->secondaryTileset); this->project->saveTilesets(this->primaryTileset, this->secondaryTileset);
emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name); emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
this->metatileSelector->select(reselectMetatileID); this->metatileSelector->select(reselectMetatileID);
@ -882,6 +890,7 @@ void TilesetEditor::on_actionCut_triggered()
Metatile * empty = new Metatile(projectConfig.getNumTilesInMetatile()); Metatile * empty = new Metatile(projectConfig.getNumTilesInMetatile());
this->copyMetatile(true); this->copyMetatile(true);
this->pasteMetatile(empty); this->pasteMetatile(empty);
this->setMetatileLabel("");
delete empty; delete empty;
} }
@ -893,17 +902,27 @@ void TilesetEditor::on_actionCopy_triggered()
void TilesetEditor::on_actionPaste_triggered() void TilesetEditor::on_actionPaste_triggered()
{ {
this->pasteMetatile(this->copiedMetatile); this->pasteMetatile(this->copiedMetatile);
this->setMetatileLabel(this->copiedMetatileLabel);
} }
void TilesetEditor::copyMetatile(bool cut) { void TilesetEditor::copyMetatile(bool cut) {
Metatile * toCopy = Tileset::getMetatile(this->getSelectedMetatileId(), this->primaryTileset, this->secondaryTileset); uint16_t metatileId = this->getSelectedMetatileId();
Metatile * toCopy = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
if (!toCopy) return; if (!toCopy) return;
if (!this->copiedMetatile) if (!this->copiedMetatile)
this->copiedMetatile = new Metatile(*toCopy); this->copiedMetatile = new Metatile(*toCopy);
else else
*this->copiedMetatile = *toCopy; *this->copiedMetatile = *toCopy;
if (!cut) this->copiedMetatile->label = ""; // Don't copy the label unless it's a cut, these should be unique to each metatile
// Don't try to copy the label unless it's a cut, these should be unique to each metatile
this->copiedMetatileLabel = "";
if (cut) {
bool isAlternateLabel = false;
QString label = Tileset::getMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset, &isAlternateLabel);
if (!isAlternateLabel)
this->copiedMetatileLabel = label;
}
} }
void TilesetEditor::pasteMetatile(const Metatile * toPaste) void TilesetEditor::pasteMetatile(const Metatile * toPaste)
@ -1146,6 +1165,7 @@ void TilesetEditor::countTileUsage() {
} }
void TilesetEditor::on_copyButton_metatileLabel_clicked() { void TilesetEditor::on_copyButton_metatileLabel_clicked() {
// TODO: Handle alternate labels
QString label = this->ui->lineEdit_metatileLabel->text(); QString label = this->ui->lineEdit_metatileLabel->text();
if (label.isEmpty()) return; if (label.isEmpty()) return;
Tileset * tileset = Tileset::getMetatileTileset(this->getSelectedMetatileId(), this->primaryTileset, this->secondaryTileset); Tileset * tileset = Tileset::getMetatileTileset(this->getSelectedMetatileId(), this->primaryTileset, this->secondaryTileset);