diff --git a/forms/tileseteditor.ui b/forms/tileseteditor.ui
index 4f492cea..572cd478 100644
--- a/forms/tileseteditor.ui
+++ b/forms/tileseteditor.ui
@@ -527,6 +527,8 @@
+
+
@@ -572,6 +574,22 @@
Palette Editor
+
+
+ true
+
+
+ Display Unused Metatiles
+
+
+
+
+ true
+
+
+ Display Metatile Usage Counts
+
+
Undo
diff --git a/include/core/maplayout.h b/include/core/maplayout.h
index 38bb3f23..630bdeae 100644
--- a/include/core/maplayout.h
+++ b/include/core/maplayout.h
@@ -35,6 +35,11 @@ public:
Blockdata blocks;
QSize dimensions;
} lastCommitMapBlocks; // to track map changes
+
+ int getWidth();
+ int getHeight();
+ int getBorderWidth();
+ int getBorderHeight();
};
#endif // MAPLAYOUT_H
diff --git a/include/project.h b/include/project.h
index b33dcee9..355ba6ba 100644
--- a/include/project.h
+++ b/include/project.h
@@ -95,7 +95,8 @@ public:
QMap tilesetLabels;
Blockdata readBlockdata(QString);
- bool loadBlockdata(Map*);
+ bool loadBlockdata(MapLayout*);
+ bool loadLayoutBorder(MapLayout*);
void saveTextFile(QString path, QString text);
void appendTextFile(QString path, QString text);
@@ -122,8 +123,9 @@ public:
QMap getTopLevelMapFields();
bool loadMapData(Map*);
bool readMapLayouts();
+ bool loadLayout(MapLayout *);
bool loadMapLayout(Map*);
- bool loadMapTilesets(Map*);
+ bool loadLayoutTilesets(MapLayout*);
void loadTilesetAssets(Tileset*);
void loadTilesetTiles(Tileset*, QImage);
void loadTilesetMetatiles(Tileset*);
@@ -182,8 +184,6 @@ public:
QStringList getEventScriptsFilePaths() const;
QCompleter *getEventScriptLabelCompleter(QStringList additionalScriptLabels);
- bool loadMapBorder(Map *map);
-
void saveMapHealEvents(Map *map);
static int getNumTilesPrimary();
diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h
index a65cb75d..c95bd521 100644
--- a/include/ui/tileseteditor.h
+++ b/include/ui/tileseteditor.h
@@ -75,6 +75,9 @@ private slots:
void on_actionChange_Palettes_triggered();
+ void on_actionShow_Unused_toggled(bool checked);
+ void on_actionShow_Counts_toggled(bool checked);
+
void on_actionUndo_triggered();
void on_actionRedo_triggered();
@@ -119,6 +122,7 @@ private:
void refresh();
void saveMetatileLabel();
void closeEvent(QCloseEvent*);
+ void countMetatileUsage();
Ui::TilesetEditor *ui;
History metatileHistory;
diff --git a/include/ui/tileseteditormetatileselector.h b/include/ui/tileseteditormetatileselector.h
index 87133a0b..05223fd4 100644
--- a/include/ui/tileseteditormetatileselector.h
+++ b/include/ui/tileseteditormetatileselector.h
@@ -8,21 +8,20 @@
class TilesetEditorMetatileSelector: public SelectablePixmapItem {
Q_OBJECT
public:
- TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Map *map): SelectablePixmapItem(32, 32, 1, 1) {
- this->primaryTileset = primaryTileset;
- this->secondaryTileset = secondaryTileset;
- this->numMetatilesWide = 8;
- this->map = map;
- setAcceptHoverEvents(true);
- }
+ TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Map *map);
Map *map = nullptr;
void draw();
bool select(uint16_t metatileId);
- void setTilesets(Tileset*, Tileset*);
+ void setTilesets(Tileset*, Tileset*, bool draw = true);
uint16_t getSelectedMetatile();
void updateSelectedMetatile();
QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId);
+ //
+ QVector usedMetatiles;
+ bool selectorShowUnused = false;
+ bool selectorShowCounts = false;
+
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
void mouseMoveEvent(QGraphicsSceneMouseEvent*);
@@ -39,6 +38,10 @@ private:
QPoint getMetatileIdCoords(uint16_t);
bool shouldAcceptEvent(QGraphicsSceneMouseEvent*);
+ void drawFilters();
+ void drawUnused();
+ void drawCounts();
+
signals:
void hoveredMetatileChanged(uint16_t);
void hoveredMetatileCleared();
diff --git a/src/core/map.cpp b/src/core/map.cpp
index 202d797b..ee4ee91b 100644
--- a/src/core/map.cpp
+++ b/src/core/map.cpp
@@ -63,19 +63,19 @@ QString Map::bgEventsLabelFromName(QString mapName)
}
int Map::getWidth() {
- return layout->width.toInt(nullptr, 0);
+ return layout->getWidth();
}
int Map::getHeight() {
- return layout->height.toInt(nullptr, 0);
+ return layout->getHeight();
}
int Map::getBorderWidth() {
- return layout->border_width.toInt(nullptr, 0);
+ return layout->getBorderWidth();
}
int Map::getBorderHeight() {
- return layout->border_height.toInt(nullptr, 0);
+ return layout->getBorderHeight();
}
bool Map::mapBlockChanged(int i, const Blockdata &cache) {
diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp
index 98b99d1e..8b1a1d3f 100644
--- a/src/core/maplayout.cpp
+++ b/src/core/maplayout.cpp
@@ -14,3 +14,19 @@ QString MapLayout::layoutConstantFromName(QString mapName) {
return constantName;
}
+
+int MapLayout::getWidth() {
+ return width.toInt(nullptr, 0);
+}
+
+int MapLayout::getHeight() {
+ return height.toInt(nullptr, 0);
+}
+
+int MapLayout::getBorderWidth() {
+ return border_width.toInt(nullptr, 0);
+}
+
+int MapLayout::getBorderHeight() {
+ return border_height.toInt(nullptr, 0);
+}
diff --git a/src/project.cpp b/src/project.cpp
index 860c1087..64a2f741 100644
--- a/src/project.cpp
+++ b/src/project.cpp
@@ -447,6 +447,17 @@ void Project::setNewMapHeader(Map* map, int mapIndex) {
map->battle_scene = mapBattleScenes.value(0, "MAP_BATTLE_SCENE_NORMAL");
}
+bool Project::loadLayout(MapLayout *layout) {
+ // Force these to run even if one fails
+ bool loadedTilesets = loadLayoutTilesets(layout);
+ bool loadedBlockdata = loadBlockdata(layout);
+ bool loadedBorder = loadLayoutBorder(layout);
+
+ return loadedTilesets
+ && loadedBlockdata
+ && loadedBorder;
+}
+
bool Project::loadMapLayout(Map* map) {
if (!map->isPersistedToFile) {
return true;
@@ -459,14 +470,11 @@ bool Project::loadMapLayout(Map* map) {
return false;
}
- // Force these to run even if one fails
- bool loadedTilesets = loadMapTilesets(map);
- bool loadedBlockdata = loadBlockdata(map);
- bool loadedBorder = loadMapBorder(map);
-
- return loadedTilesets
- && loadedBlockdata
- && loadedBorder;
+ if (map->hasUnsavedChanges()) {
+ return true;
+ } else {
+ return loadLayout(map->layout);
+ }
}
bool Project::readMapLayouts() {
@@ -1080,30 +1088,31 @@ void Project::saveTilesetPalettes(Tileset *tileset) {
}
}
-bool Project::loadMapTilesets(Map* map) {
- if (map->hasUnsavedChanges()) {
- return true;
- }
+bool Project::loadLayoutTilesets(MapLayout *layout) {
+ // TODO: investigate where to put this now
+ // if (map->hasUnsavedChanges()) {
+ // return true;
+ // }
- map->layout->tileset_primary = getTileset(map->layout->tileset_primary_label);
- if (!map->layout->tileset_primary) {
+ layout->tileset_primary = getTileset(layout->tileset_primary_label);
+ if (!layout->tileset_primary) {
QString defaultTileset = tilesetLabels["primary"].value(0, "gTileset_General");
- logWarn(QString("Map layout %1 has invalid primary tileset '%2'. Using default '%3'").arg(map->layout->id).arg(map->layout->tileset_primary_label).arg(defaultTileset));
- map->layout->tileset_primary_label = defaultTileset;
- map->layout->tileset_primary = getTileset(map->layout->tileset_primary_label);
- if (!map->layout->tileset_primary) {
+ logWarn(QString("Map layout %1 has invalid primary tileset '%2'. Using default '%3'").arg(layout->id).arg(layout->tileset_primary_label).arg(defaultTileset));
+ layout->tileset_primary_label = defaultTileset;
+ layout->tileset_primary = getTileset(layout->tileset_primary_label);
+ if (!layout->tileset_primary) {
logError(QString("Failed to set default primary tileset."));
return false;
}
}
- map->layout->tileset_secondary = getTileset(map->layout->tileset_secondary_label);
- if (!map->layout->tileset_secondary) {
+ layout->tileset_secondary = getTileset(layout->tileset_secondary_label);
+ if (!layout->tileset_secondary) {
QString defaultTileset = tilesetLabels["secondary"].value(0, projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered ? "gTileset_PalletTown" : "gTileset_Petalburg");
- logWarn(QString("Map layout %1 has invalid secondary tileset '%2'. Using default '%3'").arg(map->layout->id).arg(map->layout->tileset_secondary_label).arg(defaultTileset));
- map->layout->tileset_secondary_label = defaultTileset;
- map->layout->tileset_secondary = getTileset(map->layout->tileset_secondary_label);
- if (!map->layout->tileset_secondary) {
+ logWarn(QString("Map layout %1 has invalid secondary tileset '%2'. Using default '%3'").arg(layout->id).arg(layout->tileset_secondary_label).arg(defaultTileset));
+ layout->tileset_secondary_label = defaultTileset;
+ layout->tileset_secondary = getTileset(layout->tileset_secondary_label);
+ if (!layout->tileset_secondary) {
logError(QString("Failed to set default secondary tileset."));
return false;
}
@@ -1140,23 +1149,23 @@ Tileset* Project::loadTileset(QString label, Tileset *tileset) {
return tileset;
}
-bool Project::loadBlockdata(Map *map) {
- if (map->hasUnsavedChanges()) {
- return true;
- }
+bool Project::loadBlockdata(MapLayout *layout) {
+ // if (map->hasUnsavedChanges()) {
+ // return true;
+ // }
- QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path);
- map->layout->blockdata = readBlockdata(path);
- map->layout->lastCommitMapBlocks.blocks = map->layout->blockdata;
- map->layout->lastCommitMapBlocks.dimensions = QSize(map->getWidth(), map->getHeight());
+ QString path = QString("%1/%2").arg(root).arg(layout->blockdata_path);
+ layout->blockdata = readBlockdata(path);
+ layout->lastCommitMapBlocks.blocks = layout->blockdata;
+ layout->lastCommitMapBlocks.dimensions = QSize(layout->getWidth(), layout->getHeight());
- if (map->layout->blockdata.count() != map->getWidth() * map->getHeight()) {
+ if (layout->blockdata.count() != layout->getWidth() * layout->getHeight()) {
logWarn(QString("Layout blockdata length %1 does not match dimensions %2x%3 (should be %4). Resizing blockdata.")
- .arg(map->layout->blockdata.count())
- .arg(map->getWidth())
- .arg(map->getHeight())
- .arg(map->getWidth() * map->getHeight()));
- map->layout->blockdata.resize(map->getWidth() * map->getHeight());
+ .arg(layout->blockdata.count())
+ .arg(layout->getWidth())
+ .arg(layout->getHeight())
+ .arg(layout->getWidth() * layout->getHeight()));
+ layout->blockdata.resize(layout->getWidth() * layout->getHeight());
}
return true;
}
@@ -1170,19 +1179,19 @@ void Project::setNewMapBlockdata(Map *map) {
map->layout->lastCommitMapBlocks.dimensions = QSize(map->getWidth(), map->getHeight());
}
-bool Project::loadMapBorder(Map *map) {
- if (map->hasUnsavedChanges()) {
- return true;
- }
+bool Project::loadLayoutBorder(MapLayout *layout) {
+ // if (map->hasUnsavedChanges()) {
+ // return true;
+ // }
- QString path = QString("%1/%2").arg(root).arg(map->layout->border_path);
- map->layout->border = readBlockdata(path);
- int borderLength = map->getBorderWidth() * map->getBorderHeight();
- if (map->layout->border.count() != borderLength) {
+ QString path = QString("%1/%2").arg(root).arg(layout->border_path);
+ layout->border = readBlockdata(path);
+ int borderLength = layout->getBorderWidth() * layout->getBorderHeight();
+ if (layout->border.count() != borderLength) {
logWarn(QString("Layout border blockdata length %1 must be %2. Resizing border blockdata.")
- .arg(map->layout->border.count())
+ .arg(layout->border.count())
.arg(borderLength));
- map->layout->border.resize(borderLength);
+ layout->border.resize(borderLength);
}
return true;
}
@@ -1856,7 +1865,8 @@ Map* Project::addNewMapToGroup(QString mapName, int groupNum, Map *newMap, bool
setNewMapBorder(newMap);
}
- loadMapTilesets(newMap);
+ //loadMapTilesets(newMap);
+ loadLayoutTilesets(newMap->layout);
setNewMapEvents(newMap);
setNewMapConnections(newMap);
diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp
index 1054feab..5a4de7a9 100644
--- a/src/ui/tileseteditor.cpp
+++ b/src/ui/tileseteditor.cpp
@@ -281,6 +281,13 @@ void TilesetEditor::refresh() {
this->metatileSelector->select(this->metatileSelector->getSelectedMetatile());
this->drawSelectedTiles();
+ if (metatileSelector) {
+ if (metatileSelector->selectorShowUnused || metatileSelector->selectorShowCounts) {
+ countMetatileUsage();
+ this->metatileSelector->draw();
+ }
+ }
+
this->ui->graphicsView_Tiles->setSceneRect(0, 0, this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2);
this->ui->graphicsView_Tiles->setFixedSize(this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2);
this->ui->graphicsView_Metatiles->setSceneRect(0, 0, this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2);
@@ -909,3 +916,60 @@ void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary)
this->refresh();
this->hasUnsavedChanges = true;
}
+
+void TilesetEditor::on_actionShow_Unused_toggled(bool checked) {
+ this->metatileSelector->selectorShowUnused = checked;
+
+ if (checked) countMetatileUsage();
+
+ this->metatileSelector->draw();
+}
+
+void TilesetEditor::on_actionShow_Counts_toggled(bool checked) {
+ this->metatileSelector->selectorShowCounts = checked;
+
+ if (checked) countMetatileUsage();
+
+ this->metatileSelector->draw();
+}
+
+void TilesetEditor::countMetatileUsage() {
+ // do not double count
+ metatileSelector->usedMetatiles.fill(0);
+
+ for (auto layout : this->project->mapLayouts.values()) {
+ bool usesPrimary = false;
+ bool usesSecondary = false;
+
+ if (layout->tileset_primary_label == this->primaryTileset->name) {
+ usesPrimary = true;
+ }
+
+ if (layout->tileset_secondary_label == this->secondaryTileset->name) {
+ usesSecondary = true;
+ }
+
+ if (usesPrimary || usesSecondary) {
+ this->project->loadLayout(layout);
+
+ // for each block in the layout, mark in the vector that it is used
+ for (int i = 0; i < layout->blockdata.length(); i++) {
+ uint16_t tile = layout->blockdata.at(i).tile;
+ if (tile < this->project->getNumMetatilesPrimary()) {
+ if (usesPrimary) metatileSelector->usedMetatiles[tile]++;
+ } else {
+ if (usesSecondary) metatileSelector->usedMetatiles[tile]++;
+ }
+ }
+
+ for (int i = 0; i < layout->border.length(); i++) {
+ uint16_t tile = layout->border.at(i).tile;
+ if (tile < this->project->getNumMetatilesPrimary()) {
+ if (usesPrimary) metatileSelector->usedMetatiles[tile]++;
+ } else {
+ if (usesSecondary) metatileSelector->usedMetatiles[tile]++;
+ }
+ }
+ }
+ }
+}
diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp
index c7df404b..687dbe5f 100644
--- a/src/ui/tileseteditormetatileselector.cpp
+++ b/src/ui/tileseteditormetatileselector.cpp
@@ -3,6 +3,15 @@
#include "project.h"
#include
+TilesetEditorMetatileSelector::TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Map *map)
+ : SelectablePixmapItem(32, 32, 1, 1) {
+ this->setTilesets(primaryTileset, secondaryTileset, false);
+ this->numMetatilesWide = 8;
+ this->map = map;
+ setAcceptHoverEvents(true);
+ this->usedMetatiles.resize(Project::getNumMetatilesTotal());
+}
+
void TilesetEditorMetatileSelector::draw() {
if (!this->primaryTileset || !this->secondaryTileset) {
this->setPixmap(QPixmap());
@@ -39,6 +48,8 @@ void TilesetEditorMetatileSelector::draw() {
painter.end();
this->setPixmap(QPixmap::fromImage(image));
this->drawSelection();
+
+ drawFilters();
}
bool TilesetEditorMetatileSelector::select(uint16_t metatileId) {
@@ -50,10 +61,11 @@ bool TilesetEditorMetatileSelector::select(uint16_t metatileId) {
return true;
}
-void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
+void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset, bool draw) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
- this->draw();
+
+ if (draw) this->draw();
}
void TilesetEditorMetatileSelector::updateSelectedMetatile() {
@@ -131,3 +143,92 @@ QPoint TilesetEditorMetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metat
pos.ry() = (pos.y() * this->cellHeight) + (this->cellHeight / 2);
return pos;
}
+
+void TilesetEditorMetatileSelector::drawFilters() {
+ if (selectorShowUnused) {
+ drawUnused();
+ }
+ if (selectorShowCounts) {
+ drawCounts();
+ }
+}
+
+void TilesetEditorMetatileSelector::drawUnused() {
+ // setup the circle with a line through it image to layer above unused metatiles
+ QPixmap redX(32, 32);
+ redX.fill(Qt::transparent);
+
+ QPen whitePen(Qt::white);
+ whitePen.setWidth(1);
+ QPen redPen(Qt::magenta);
+ redPen.setWidth(1);
+
+ QPainter oPainter(&redX);
+
+ oPainter.setPen(whitePen);
+ oPainter.drawEllipse(QRect(1, 1, 30, 30));
+ oPainter.setPen(redPen);
+ oPainter.drawEllipse(QRect(2, 2, 28, 28));
+ oPainter.drawEllipse(QRect(3, 3, 26, 26));
+
+ oPainter.setPen(whitePen);
+ oPainter.drawEllipse(QRect(4, 4, 24, 24));
+
+ whitePen.setWidth(5);
+ oPainter.setPen(whitePen);
+ oPainter.drawLine(0, 0, 31, 31);
+
+ redPen.setWidth(3);
+ oPainter.setPen(redPen);
+ oPainter.drawLine(2, 2, 29, 29);
+
+ oPainter.end();
+
+ // draw symbol on unused metatiles
+ QPixmap metatilesPixmap = this->pixmap();
+
+ QPainter unusedPainter(&metatilesPixmap);
+
+ for (int tile = 0; tile < this->usedMetatiles.size(); tile++) {
+ if (!usedMetatiles[tile]) {
+ unusedPainter.drawPixmap((tile % 8) * 32, (tile / 8) * 32, redX);
+ }
+ }
+
+ unusedPainter.end();
+
+ this->setPixmap(metatilesPixmap);
+}
+
+void TilesetEditorMetatileSelector::drawCounts() {
+ QPen blackPen(Qt::black);
+ blackPen.setWidth(1);
+
+ QPixmap metatilesPixmap = this->pixmap();
+
+ QPainter countPainter(&metatilesPixmap);
+ countPainter.setPen(blackPen);
+
+ for (int tile = 0; tile < this->usedMetatiles.size(); tile++) {
+ int count = usedMetatiles[tile];
+ QString countText = QString::number(count);
+ if (count > 1000) countText = ">1k";
+ countPainter.drawText((tile % 8) * 32, (tile / 8) * 32 + 32, countText);
+ }
+
+ // write in white and black for contrast
+ QPen whitePen(Qt::white);
+ whitePen.setWidth(1);
+ countPainter.setPen(whitePen);
+
+ for (int tile = 0; tile < this->usedMetatiles.size(); tile++) {
+ int count = usedMetatiles[tile];
+ QString countText = QString::number(count);
+ if (count > 1000) countText = ">1k";
+ countPainter.drawText((tile % 8) * 32 + 1, (tile / 8) * 32 + 32 - 1, countText);
+ }
+
+ countPainter.end();
+
+ this->setPixmap(metatilesPixmap);
+}