diff --git a/forms/paletteeditor.ui b/forms/paletteeditor.ui
new file mode 100644
index 00000000..a8b18de4
--- /dev/null
+++ b/forms/paletteeditor.ui
@@ -0,0 +1,1536 @@
+
+
+ PaletteEditor
+
+
+
+ 0
+ 0
+ 817
+ 739
+
+
+
+ MainWindow
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Palette
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Color 4
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 5
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 7
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 6
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 10
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 8
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 9
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 11
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 12
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 14
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 13
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 15
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 0
+
+
+
-
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+
+
+
+ -
+
+
+ Color 1
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 2
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+ Color 3
+
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ -
+
+
+ Red
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+
+ -
+
+
+ Green
+
+
+
+ -
+
+
+ Blue
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 31
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/forms/tileseteditor.ui b/forms/tileseteditor.ui
index 81727bc1..f8b6947f 100644
--- a/forms/tileseteditor.ui
+++ b/forms/tileseteditor.ui
@@ -1,232 +1,445 @@
TilesetEditor
-
+
0
0
- 1022
- 386
+ 800
+ 600
- Dialog
+ MainWindow
-
- -
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
-
-
- -
-
-
-
- 1
- 0
-
-
-
- true
-
-
-
-
- 0
- 0
- 329
- 339
-
+
+
+
-
+
+
+ true
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 26
- 20
-
-
-
-
- -
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 26
- 20
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 120
-
-
-
-
-
+
+
+
+ 0
+ 0
+ 386
+ 539
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
-
-
- -
-
-
-
- 2
- 0
-
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
-
-
-
-
- 1
- 0
-
-
-
- true
-
-
-
-
- 0
- 0
- 316
- 319
-
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ QFrame::NoFrame
-
-
-
-
-
- Qt::Horizontal
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Tile Properties
-
-
- 21
- 20
-
+
+ false
-
+
+ -
+
+
+ Palette
+
+
+
+ -
+
+
+ -
+
+
+ X Flip
+
+
+
+ -
+
+
+ Y Flip
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Selection
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::StyledPanel
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+
+
- -
-
-
- -
-
-
- Qt::Horizontal
+
-
+
+
+ Metatile Properties
-
-
- 21
- 20
-
+
+ false
-
-
- -
-
-
- Qt::Vertical
+
+ false
-
-
- 20
- 100
-
-
-
+
+
-
+
+
+ Bottom/Top
+
+
+
+ -
+
+
+
+ 64
+ 32
+
+
+
+
+ 64
+ 32
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+ -
+
+
+ Metatile Behavior
+
+
+
+ -
+
+
+ -
+
+
+ Layer Type
+
+
+
+ -
+
+
+
+
-
-
- -
-
-
-
- 1
- 0
-
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
-
-
-
-
+
+ -
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 386
+ 359
+
+
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Save Tileset
+
+
+ Ctrl+S
+
+
+
+
+ Import Primary Tiles
+
+
+
+
+ Import Secondary Tiles
+
+
+
+
+ Change Number of Metatiles
+
+
+
+
+ Change Palettes
+
+
-
-
- buttonBox
- accepted()
- TilesetEditor
- accept()
-
-
- 248
- 254
-
-
- 157
- 274
-
-
-
-
- buttonBox
- rejected()
- TilesetEditor
- reject()
-
-
- 316
- 260
-
-
- 286
- 274
-
-
-
-
+
diff --git a/include/core/metatile.h b/include/core/metatile.h
index b5cb19d5..4f280608 100644
--- a/include/core/metatile.h
+++ b/include/core/metatile.h
@@ -10,7 +10,10 @@ public:
Metatile();
public:
QList *tiles = nullptr;
+ uint8_t behavior;
+ uint8_t layerType;
+ Metatile *copy();
static int getBlockIndex(int);
};
diff --git a/include/core/tileset.h b/include/core/tileset.h
index d18ab5e0..ce735e35 100644
--- a/include/core/tileset.h
+++ b/include/core/tileset.h
@@ -17,16 +17,24 @@ public:
QString tiles_label;
QString palettes_label;
QString metatiles_label;
+ QString metatiles_path;
QString callback_label;
QString metatile_attrs_label;
+ QString metatile_attrs_path;
+ QString tilesImagePath;
+ QImage tilesImage;
+ QList palettePaths;
QList *tiles = nullptr;
QList *metatiles = nullptr;
QList> *palettes = nullptr;
+ Tileset* copy();
+
static Tileset* getBlockTileset(int, Tileset*, Tileset*);
static Metatile* getMetatile(int, Tileset*, Tileset*);
static QList> getBlockPalettes(Tileset*, Tileset*);
+ static QList getPalette(int, Tileset*, Tileset*);
};
#endif // TILESET_H
diff --git a/include/editor.h b/include/editor.h
index a9433e95..ac95c761 100644
--- a/include/editor.h
+++ b/include/editor.h
@@ -68,9 +68,10 @@ public:
void updateDiveMap(QString mapName);
void updateEmergeMap(QString mapName);
void setSelectedConnectionFromMap(QString mapName);
- void updatePrimaryTileset(QString tilesetLabel);
- void updateSecondaryTileset(QString tilesetLabel);
+ void updatePrimaryTileset(QString tilesetLabel, bool forceLoad = false);
+ void updateSecondaryTileset(QString tilesetLabel, bool forceLoad = false);
void toggleBorderVisibility(bool visible);
+ Tileset *getCurrentMapPrimaryTileset();
DraggablePixmapItem *addMapEvent(Event *event);
void selectMapEvent(DraggablePixmapItem *object);
diff --git a/include/mainwindow.h b/include/mainwindow.h
index 732f96b1..1eb1ed43 100644
--- a/include/mainwindow.h
+++ b/include/mainwindow.h
@@ -12,6 +12,7 @@
#include "project.h"
#include "map.h"
#include "editor.h"
+#include "tileseteditor.h"
namespace Ui {
class MainWindow;
@@ -42,6 +43,7 @@ private slots:
void onLoadMapRequested(QString, QString);
void onMapChanged(Map *map);
void onMapNeedsRedrawing();
+ void onTilesetsSaved(QString, QString);
void on_action_Save_triggered();
void on_tabWidget_2_currentChanged(int index);
@@ -92,6 +94,7 @@ private slots:
void onAddNewMapToGroupClick(QAction* triggeredAction);
void onTilesetChanged(QString);
void currentMetatilesSelectionChanged();
+ void onTilesetEditorClosed();
void on_action_Export_Map_Image_triggered();
@@ -123,8 +126,11 @@ private slots:
void resetMapViewScale();
+ void on_actionTileset_Editor_triggered();
+
private:
Ui::MainWindow *ui;
+ TilesetEditor *tilesetEditor = nullptr;
QStandardItemModel *mapListModel;
QList *mapGroupsModel;
QMap mapListIndexes;
@@ -153,6 +159,7 @@ private:
void initEditor();
void loadUserSettings();
void openRecentProject();
+ void updateTilesetEditor();
};
enum MapListUserRoles {
diff --git a/include/project.h b/include/project.h
index 4efe58a6..fa4ee2ed 100644
--- a/include/project.h
+++ b/include/project.h
@@ -40,14 +40,16 @@ public:
QStringList *secretBaseIds = nullptr;
QStringList *bgEventFacingDirections = nullptr;
QStringList mapsWithConnections;
+ QMap metatileBehaviorMap;
+ QMap metatileBehaviorMapInverse;
QMap *map_cache;
Map* loadMap(QString);
Map* getMap(QString);
QMap *tileset_cache = nullptr;
- Tileset* loadTileset(QString);
- Tileset* getTileset(QString);
+ Tileset* loadTileset(QString, Tileset *tileset = nullptr);
+ Tileset* getTileset(QString, bool forceLoad = false);
Blockdata* readBlockdata(QString);
void loadBlockdata(Map*);
@@ -72,6 +74,8 @@ public:
void readMapsWithConnections();
void loadMapTilesets(Map*);
void loadTilesetAssets(Tileset*);
+ void loadTilesetTiles(Tileset*, QImage);
+ void loadTilesetMetatiles(Tileset*);
void saveBlockdata(Map*);
void saveMapBorder(Map*);
@@ -83,6 +87,7 @@ public:
void saveMapGroupsTable();
void saveMapConstantsHeader();
void saveHealLocationStruct(Map*);
+ void saveTilesets(Tileset*, Tileset*);
QList* parseAsm(QString text);
QStringList getSongNames();
@@ -100,9 +105,11 @@ public:
void readCoordEventWeatherNames();
void readSecretBaseIds();
void readBgEventFacingDirections();
+ void readMetatileBehaviors();
void loadEventPixmaps(QList objects);
QMap getEventObjGfxConstants();
+ QString fixPalettePath(QString path);
QString fixGraphicPath(QString path);
void readMapEvents(Map *map);
@@ -127,6 +134,10 @@ private:
QString getMapLayoutFilepath(QString);
void saveMapHeader(Map*);
void saveMapConnections(Map*);
+ void saveTilesetMetatileAttributes(Tileset*);
+ void saveTilesetMetatiles(Tileset*);
+ void saveTilesetTilesImage(Tileset*);
+ void saveTilesetPalettes(Tileset*, bool);
void updateMapsWithConnections(Map*);
void saveMapsWithConnections();
void saveMapLayoutsTable();
diff --git a/include/ui/imageproviders.h b/include/ui/imageproviders.h
index 4bff143a..96896e78 100644
--- a/include/ui/imageproviders.h
+++ b/include/ui/imageproviders.h
@@ -8,7 +8,8 @@
QImage getCollisionMetatileImage(Block);
QImage getCollisionMetatileImage(int, int);
-QImage getMetatileImage(int, Tileset*, Tileset*);
-QImage getTileImage(int, Tileset*, Tileset*);
+QImage getMetatileImage(uint16_t, Tileset*, Tileset*);
+QImage getTileImage(uint16_t, Tileset*, Tileset*);
+QImage getColoredTileImage(uint16_t, Tileset*, Tileset*, int);
#endif // IMAGEPROVIDERS_H
diff --git a/include/ui/metatilelayersitem.h b/include/ui/metatilelayersitem.h
new file mode 100644
index 00000000..b6599e58
--- /dev/null
+++ b/include/ui/metatilelayersitem.h
@@ -0,0 +1,29 @@
+#ifndef METATILELAYERSITEM_H
+#define METATILELAYERSITEM_H
+
+#include "tileset.h"
+#include
+#include
+
+class MetatileLayersItem : public QObject, public QGraphicsPixmapItem {
+ Q_OBJECT
+public:
+ MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset) {
+ this->metatile = metatile;
+ this->primaryTileset = primaryTileset;
+ this->secondaryTileset = secondaryTileset;
+ }
+ void draw();
+ void setTilesets(Tileset*, Tileset*);
+ void setMetatile(Metatile*);
+private:
+ Metatile* metatile;
+ Tileset *primaryTileset;
+ Tileset *secondaryTileset;
+signals:
+ void tileChanged(int, int);
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent*);
+};
+
+#endif // METATILELAYERSITEM_H
diff --git a/include/ui/paletteeditor.h b/include/ui/paletteeditor.h
new file mode 100644
index 00000000..d8866f24
--- /dev/null
+++ b/include/ui/paletteeditor.h
@@ -0,0 +1,43 @@
+#ifndef PALETTEEDITOR_H
+#define PALETTEEDITOR_H
+
+#include
+#include
+#include
+#include "project.h"
+
+namespace Ui {
+class PaletteEditor;
+}
+
+class PaletteEditor : public QMainWindow {
+ Q_OBJECT
+public:
+ explicit PaletteEditor(Project*, Tileset*, Tileset*, QWidget *parent = nullptr);
+ ~PaletteEditor();
+ void setPaletteId(int);
+
+private:
+ Ui::PaletteEditor *ui;
+ Project *project = nullptr;
+ QList> sliders;
+ QList frames;
+ Tileset *primaryTileset;
+ Tileset *secondaryTileset;
+ void disableSliderSignals();
+ void enableSliderSignals();
+ void initColorSliders();
+ void refreshColorSliders();
+ void refreshColors();
+ void refreshColor(int);
+ void setColor(int);
+
+signals:
+ void closed();
+ void changedPaletteColor();
+ void changedPalette(int);
+private slots:
+ void on_spinBox_PaletteId_valueChanged(int arg1);
+};
+
+#endif // PALETTEEDITOR_H
diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h
index 7befc465..f2d1f54e 100644
--- a/include/ui/tileseteditor.h
+++ b/include/ui/tileseteditor.h
@@ -1,22 +1,90 @@
#ifndef TILESETEDITOR_H
#define TILESETEDITOR_H
-#include
+#include
+#include "project.h"
+#include "paletteeditor.h"
+#include "tileseteditormetatileselector.h"
+#include "tileseteditortileselector.h"
+#include "metatilelayersitem.h"
namespace Ui {
class TilesetEditor;
}
-class TilesetEditor : public QDialog
+class TilesetEditor : public QMainWindow
{
Q_OBJECT
public:
- explicit TilesetEditor(QWidget *parent = nullptr);
+ explicit TilesetEditor(Project*, QString, QString, QWidget *parent = nullptr);
~TilesetEditor();
+ void setTilesets(QString, QString);
+ void init(Project*, QString, QString);
+
+private slots:
+ void onHoveredMetatileChanged(uint16_t);
+ void onHoveredMetatileCleared();
+ void onSelectedMetatileChanged(uint16_t);
+ void onHoveredTileChanged(uint16_t);
+ void onHoveredTileCleared();
+ void onSelectedTilesChanged();
+ void onMetatileLayerTileChanged(int, int);
+ void onPaletteEditorClosed();
+ void onPaletteEditorChangedPaletteColor();
+ void onPaletteEditorChangedPalette(int);
+
+ void on_spinBox_paletteSelector_valueChanged(int arg1);
+
+ void on_checkBox_xFlip_stateChanged(int arg1);
+
+ void on_checkBox_yFlip_stateChanged(int arg1);
+
+ void on_comboBox_metatileBehaviors_currentIndexChanged(const QString &arg1);
+
+ void on_comboBox_layerType_currentIndexChanged(int index);
+
+ void on_actionSave_Tileset_triggered();
+
+ void on_actionImport_Primary_Tiles_triggered();
+
+ void on_actionImport_Secondary_Tiles_triggered();
+
+ void on_actionChange_Metatiles_Count_triggered();
+
+ void on_actionChange_Palettes_triggered();
private:
+ void closeEvent(QCloseEvent*);
+ void initMetatileSelector();
+ void initTileSelector();
+ void initSelectedTileItem();
+ void initMetatileLayersItem();
+ void drawSelectedTiles();
+ void importTilesetTiles(Tileset*, bool);
+ void refresh();
Ui::TilesetEditor *ui;
+ TilesetEditorMetatileSelector *metatileSelector = nullptr;
+ TilesetEditorTileSelector *tileSelector = nullptr;
+ MetatileLayersItem *metatileLayersItem = nullptr;
+ PaletteEditor *paletteEditor = nullptr;
+ Project *project = nullptr;
+ Metatile *metatile = nullptr;
+ int paletteId;
+ bool tileXFlip;
+ bool tileYFlip;
+ bool hasUnsavedChanges;
+ Tileset *primaryTileset = nullptr;
+ Tileset *secondaryTileset = nullptr;
+ QGraphicsScene *metatilesScene = nullptr;
+ QGraphicsScene *tilesScene = nullptr;
+ QGraphicsScene *selectedTileScene = nullptr;
+ QGraphicsPixmapItem *selectedTilePixmapItem = nullptr;
+ QGraphicsScene *metatileLayersScene = nullptr;
+
+signals:
+ void tilesetsSaved(QString, QString);
+ void closed();
};
#endif // TILESETEDITOR_H
diff --git a/include/ui/tileseteditormetatileselector.h b/include/ui/tileseteditormetatileselector.h
new file mode 100644
index 00000000..3da82721
--- /dev/null
+++ b/include/ui/tileseteditormetatileselector.h
@@ -0,0 +1,44 @@
+#ifndef TILESETEDITORMETATILESELECTOR_H
+#define TILESETEDITORMETATILESELECTOR_H
+
+#include "selectablepixmapitem.h"
+#include "tileset.h"
+
+class TilesetEditorMetatileSelector: public SelectablePixmapItem {
+ Q_OBJECT
+public:
+ TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(32, 32, 1, 1) {
+ this->primaryTileset = primaryTileset;
+ this->secondaryTileset = secondaryTileset;
+ this->numMetatilesWide = 8;
+ setAcceptHoverEvents(true);
+ }
+ void draw();
+ void select(uint16_t metatileId);
+ void setTilesets(Tileset*, Tileset*);
+ uint16_t getSelectedMetatile();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent*);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent*);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
+ void hoverMoveEvent(QGraphicsSceneHoverEvent*);
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
+
+private:
+ Tileset *primaryTileset = nullptr;
+ Tileset *secondaryTileset = nullptr;
+ uint16_t selectedMetatile;
+ int numMetatilesWide;
+ void updateSelectedMetatile();
+ uint16_t getMetatileId(int x, int y);
+ QPoint getMetatileIdCoords(uint16_t);
+ uint16_t getValidMetatileId(uint16_t);
+
+signals:
+ void hoveredMetatileChanged(uint16_t);
+ void hoveredMetatileCleared();
+ void selectedMetatileChanged(uint16_t);
+};
+
+#endif // TILESETEDITORMETATILESELECTOR_H
diff --git a/include/ui/tileseteditortileselector.h b/include/ui/tileseteditortileselector.h
new file mode 100644
index 00000000..fe8d6bc2
--- /dev/null
+++ b/include/ui/tileseteditortileselector.h
@@ -0,0 +1,52 @@
+#ifndef TILESETEDITORTILESELECTOR_H
+#define TILESETEDITORTILESELECTOR_H
+
+#include "selectablepixmapitem.h"
+#include "tileset.h"
+
+class TilesetEditorTileSelector: public SelectablePixmapItem {
+ Q_OBJECT
+public:
+ TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(16, 16, 2, 2) {
+ this->primaryTileset = primaryTileset;
+ this->secondaryTileset = secondaryTileset;
+ this->numTilesWide = 16;
+ this->paletteId = 0;
+ this->xFlip = false;
+ this->yFlip = false;
+ setAcceptHoverEvents(true);
+ }
+ void draw();
+ void select(uint16_t metatileId);
+ void setTilesets(Tileset*, Tileset*);
+ void setPaletteId(int);
+ void setTileFlips(bool, bool);
+ QList getSelectedTiles();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent*);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent*);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
+ void hoverMoveEvent(QGraphicsSceneHoverEvent*);
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
+
+private:
+ Tileset *primaryTileset;
+ Tileset *secondaryTileset;
+ QList selectedTiles;
+ int numTilesWide;
+ int paletteId;
+ bool xFlip;
+ bool yFlip;
+ void updateSelectedTiles();
+ uint16_t getTileId(int x, int y);
+ QPoint getTileCoords(uint16_t);
+ QList getCurPaletteTable();
+
+signals:
+ void hoveredTileChanged(uint16_t);
+ void hoveredTileCleared();
+ void selectedTilesChanged();
+};
+
+#endif // TILESETEDITORTILESELECTOR_H
diff --git a/porymap.pro b/porymap.pro
index dc94f6eb..e96e2f40 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -35,13 +35,17 @@ SOURCES += src/core/block.cpp \
src/ui/imageproviders.cpp \
src/ui/mappixmapitem.cpp \
src/ui/mapsceneeventfilter.cpp \
+ src/ui/metatilelayersitem.cpp \
src/ui/metatileselector.cpp \
src/ui/movementpermissionsselector.cpp \
src/ui/neweventtoolbutton.cpp \
src/ui/noscrollcombobox.cpp \
src/ui/noscrollspinbox.cpp \
+ src/ui/paletteeditor.cpp \
src/ui/selectablepixmapitem.cpp \
src/ui/tileseteditor.cpp \
+ src/ui/tileseteditormetatileselector.cpp \
+ src/ui/tileseteditortileselector.cpp \
src/editor.cpp \
src/main.cpp \
src/mainwindow.cpp \
@@ -71,13 +75,17 @@ HEADERS += include/core/block.h \
include/ui/imageproviders.h \
include/ui/mappixmapitem.h \
include/ui/mapsceneeventfilter.h \
+ include/ui/metatilelayersitem.h \
include/ui/metatileselector.h \
include/ui/movementpermissionsselector.h \
include/ui/neweventtoolbutton.h \
include/ui/noscrollcombobox.h \
include/ui/noscrollspinbox.h \
+ include/ui/paletteeditor.h \
include/ui/selectablepixmapitem.h \
include/ui/tileseteditor.h \
+ include/ui/tileseteditormetatileselector.h \
+ include/ui/tileseteditortileselector.h \
include/editor.h \
include/mainwindow.h \
include/project.h \
@@ -85,7 +93,8 @@ HEADERS += include/core/block.h \
FORMS += forms/mainwindow.ui \
forms/eventpropertiesframe.ui \
- forms/tileseteditor.ui
+ forms/tileseteditor.ui \
+ forms/paletteeditor.ui
RESOURCES += \
resources/images.qrc
diff --git a/src/core/map.cpp b/src/core/map.cpp
index 3954df06..185306e5 100644
--- a/src/core/map.cpp
+++ b/src/core/map.cpp
@@ -226,7 +226,7 @@ QPixmap Map::renderBorder() {
}
QPixmap Map::renderConnection(MapConnection connection) {
- render();
+ render(true);
int x, y, w, h;
if (connection.direction == "up") {
x = 0;
diff --git a/src/core/metatile.cpp b/src/core/metatile.cpp
index 6dff9dc6..a44b4fa3 100644
--- a/src/core/metatile.cpp
+++ b/src/core/metatile.cpp
@@ -7,6 +7,17 @@ Metatile::Metatile()
tiles = new QList;
}
+Metatile* Metatile::copy() {
+ Metatile *copy = new Metatile;
+ copy->behavior = this->behavior;
+ copy->layerType = this->layerType;
+ copy->tiles = new QList;
+ for (Tile tile : *this->tiles) {
+ copy->tiles->append(tile);
+ }
+ return copy;
+}
+
int Metatile::getBlockIndex(int index) {
if (index < Project::getNumMetatilesPrimary()) {
return index;
diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp
index 772e4d7b..d3acbf2b 100644
--- a/src/core/tileset.cpp
+++ b/src/core/tileset.cpp
@@ -11,6 +11,43 @@ Tileset::Tileset()
}
+Tileset* Tileset::copy() {
+ Tileset *copy = new Tileset;
+ copy->name = this->name;
+ copy->is_compressed = this->is_compressed;
+ copy->is_secondary = this->is_secondary;
+ copy->padding = this->padding;
+ copy->tiles_label = this->tiles_label;
+ copy->palettes_label = this->palettes_label;
+ copy->metatiles_label = this->metatiles_label;
+ copy->metatiles_path = this->metatiles_path;
+ copy->callback_label = this->callback_label;
+ copy->metatile_attrs_label = this->metatile_attrs_label;
+ copy->metatile_attrs_path = this->metatile_attrs_path;
+ copy->tilesImage = this->tilesImage.copy();
+ copy->tilesImagePath = this->tilesImagePath;
+ for (int i = 0; i < this->palettePaths.length(); i++) {
+ copy->palettePaths.append(this->palettePaths.at(i));
+ }
+ copy->tiles = new QList;
+ for (QImage tile : *this->tiles) {
+ copy->tiles->append(tile.copy());
+ }
+ copy->metatiles = new QList;
+ for (Metatile *metatile : *this->metatiles) {
+ copy->metatiles->append(metatile->copy());
+ }
+ copy->palettes = new QList>;
+ for (QList palette : *this->palettes) {
+ QList copyPalette;
+ for (QRgb color : palette) {
+ copyPalette.append(color);
+ }
+ copy->palettes->append(copyPalette);
+ }
+ return copy;
+}
+
Tileset* Tileset::getBlockTileset(int metatile_index, Tileset *primaryTileset, Tileset *secondaryTileset) {
if (metatile_index < Project::getNumMetatilesPrimary()) {
return primaryTileset;
@@ -39,3 +76,14 @@ QList> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *s
}
return palettes;
}
+
+QList Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset) {
+ QList paletteTable;
+ Tileset *tileset = paletteId < Project::getNumPalettesPrimary()
+ ? primaryTileset
+ : secondaryTileset;
+ for (int i = 0; i < tileset->palettes->at(paletteId).length(); i++) {
+ paletteTable.append(tileset->palettes->at(paletteId).at(i));
+ }
+ return paletteTable;
+}
diff --git a/src/editor.cpp b/src/editor.cpp
index 23fce69d..08f0c766 100644
--- a/src/editor.cpp
+++ b/src/editor.cpp
@@ -961,22 +961,22 @@ void Editor::updateDiveEmergeMap(QString mapName, QString direction) {
ui->label_NumConnections->setText(QString::number(map->connections.length()));
}
-void Editor::updatePrimaryTileset(QString tilesetLabel)
+void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)
{
- if (map->layout->tileset_primary_label != tilesetLabel)
+ if (map->layout->tileset_primary_label != tilesetLabel || forceLoad)
{
map->layout->tileset_primary_label = tilesetLabel;
- map->layout->tileset_primary = project->getTileset(tilesetLabel);
+ map->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad);
emit tilesetChanged(map->name);
}
}
-void Editor::updateSecondaryTileset(QString tilesetLabel)
+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 = project->getTileset(tilesetLabel);
+ map->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad);
emit tilesetChanged(map->name);
}
}
@@ -987,6 +987,12 @@ void Editor::toggleBorderVisibility(bool visible)
this->setConnectionsVisibility(visible);
}
+Tileset* Editor::getCurrentMapPrimaryTileset()
+{
+ QString tilesetLabel = map->layout->tileset_primary_label;
+ return project->getTileset(tilesetLabel);
+}
+
void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) {
active = true;
last_x = static_cast(mouse->pos().x() + this->pos().x()) / 16;
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index e5e1115e..8dbc04e3 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -50,7 +50,7 @@ MainWindow::~MainWindow()
}
void MainWindow::initExtraShortcuts() {
- new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo()));
+ new QShortcut(QKeySequence("Ctrl+Shift+Z"), this, SLOT(redo()));
new QShortcut(QKeySequence("Ctrl+0"), this, SLOT(resetMapViewScale()));
ui->actionZoom_In->setShortcuts({QKeySequence("Ctrl++"), QKeySequence("Ctrl+=")});
}
@@ -200,6 +200,7 @@ void MainWindow::setMap(QString map_name, bool scrollTreeView) {
setRecentMap(map_name);
updateMapList();
+ updateTilesetEditor();
}
void MainWindow::redrawMapScene()
@@ -410,6 +411,7 @@ void MainWindow::loadDataStructures() {
project->readSecretBaseIds();
project->readBgEventFacingDirections();
project->readMapsWithConnections();
+ project->readMetatileBehaviors();
project->readTilesetProperties();
}
@@ -525,6 +527,12 @@ void MainWindow::onTilesetChanged(QString mapName)
setMap(mapName);
}
+void MainWindow::updateTilesetEditor() {
+ if (this->tilesetEditor) {
+ this->tilesetEditor->setTilesets(editor->ui->comboBox_PrimaryTileset->currentText(), editor->ui->comboBox_SecondaryTileset->currentText());
+ }
+}
+
void MainWindow::currentMetatilesSelectionChanged()
{
ui->graphicsView_currentMetatileSelection->setFixedSize(editor->scene_current_metatile_selection_item->pixmap().width() + 2, editor->scene_current_metatile_selection_item->pixmap().height() + 2);
@@ -1099,6 +1107,11 @@ void MainWindow::onMapNeedsRedrawing() {
redrawMapScene();
}
+void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryTilesetLabel) {
+ this->editor->updatePrimaryTileset(primaryTilesetLabel, true);
+ this->editor->updateSecondaryTileset(secondaryTilesetLabel, true);
+}
+
void MainWindow::on_action_Export_Map_Image_triggered()
{
QString defaultFilepath = QString("%1/%2.png").arg(editor->project->root).arg(editor->map->name);
@@ -1223,3 +1236,27 @@ void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected)
bool visible = selected != 0;
editor->toggleBorderVisibility(visible);
}
+
+void MainWindow::on_actionTileset_Editor_triggered()
+{
+ 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);
+ connect(this->tilesetEditor, SIGNAL(tilesetsSaved(QString, QString)), this, SLOT(onTilesetsSaved(QString, QString)));
+ connect(this->tilesetEditor, SIGNAL(closed()), this, SLOT(onTilesetEditorClosed()));
+ }
+
+ if (!this->tilesetEditor->isVisible()) {
+ this->tilesetEditor->show();
+ } else if (this->tilesetEditor->isMinimized()) {
+ this->tilesetEditor->showNormal();
+ } else {
+ this->tilesetEditor->activateWindow();
+ }
+}
+
+void MainWindow::onTilesetEditorClosed() {
+ if (this->tilesetEditor) {
+ delete this->tilesetEditor;
+ this->tilesetEditor = nullptr;
+ }
+}
diff --git a/src/project.cpp b/src/project.cpp
index 63899604..f1168123 100644
--- a/src/project.cpp
+++ b/src/project.cpp
@@ -576,6 +576,77 @@ void Project::saveHealLocationStruct(Map *map) {
saveTextFile(root + "/include/constants/heal_locations.h", constants_text);
}
+void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
+ saveTilesetMetatileAttributes(primaryTileset);
+ saveTilesetMetatileAttributes(secondaryTileset);
+ saveTilesetMetatiles(primaryTileset);
+ saveTilesetMetatiles(secondaryTileset);
+ saveTilesetTilesImage(primaryTileset);
+ saveTilesetTilesImage(secondaryTileset);
+ saveTilesetPalettes(primaryTileset, true);
+ saveTilesetPalettes(secondaryTileset, false);
+}
+
+void Project::saveTilesetMetatileAttributes(Tileset *tileset) {
+ QFile attrs_file(tileset->metatile_attrs_path);
+ if (attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ QByteArray data;
+ for (Metatile *metatile : *tileset->metatiles) {
+ data.append(static_cast(metatile->behavior));
+ data.append(static_cast((metatile->layerType << 4) & 0xF0));
+ }
+ attrs_file.write(data);
+ } else {
+ qDebug() << QString("Could not save tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path);
+ }
+}
+
+void Project::saveTilesetMetatiles(Tileset *tileset) {
+ QFile metatiles_file(tileset->metatiles_path);
+ if (metatiles_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ QByteArray data;
+ for (Metatile *metatile : *tileset->metatiles) {
+ for (int i = 0; i < 8; i++) {
+ Tile tile = metatile->tiles->at(i);
+ uint16_t value = static_cast((tile.tile & 0x3ff)
+ | ((tile.xflip & 1) << 10)
+ | ((tile.yflip & 1) << 11)
+ | ((tile.palette & 0xf) << 12));
+ data.append(static_cast(value & 0xff));
+ data.append(static_cast((value >> 8) & 0xff));
+ }
+ }
+ metatiles_file.write(data);
+ } else {
+ tileset->metatiles = new QList;
+ qDebug() << QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path);
+ }
+}
+
+void Project::saveTilesetTilesImage(Tileset *tileset) {
+ tileset->tilesImage.save(tileset->tilesImagePath);
+}
+
+void Project::saveTilesetPalettes(Tileset *tileset, bool primary) {
+ int startPaletteId = primary ? 0 : Project::getNumPalettesPrimary();
+ int endPaletteId = primary ? Project::getNumPalettesPrimary() : Project::getNumPalettesTotal();
+ for (int i = startPaletteId; i < endPaletteId; i++) {
+ QString filepath = tileset->palettePaths.at(i);
+ QString content = "JASC-PAL\r\n";
+ content += "0100\r\n";
+ content += "16\r\n";
+ for (int j = 0; j < 16; j++) {
+ QRgb color = tileset->palettes->at(i).at(j);
+ content += QString("%1 %2 %3\r\n")
+ .arg(qRed(color))
+ .arg(qGreen(color))
+ .arg(qBlue(color));
+ }
+
+ saveTextFile(filepath, content);
+ }
+}
+
void Project::loadMapTilesets(Map* map) {
if (map->layout->has_unsaved_changes) {
return;
@@ -585,12 +656,14 @@ void Project::loadMapTilesets(Map* map) {
map->layout->tileset_secondary = getTileset(map->layout->tileset_secondary_label);
}
-Tileset* Project::loadTileset(QString label) {
+Tileset* Project::loadTileset(QString label, Tileset *tileset) {
ParseUtil *parser = new ParseUtil;
QString headers_text = readTextFile(root + "/data/tilesets/headers.inc");
QStringList *values = getLabelValues(parser->parseAsm(headers_text), label);
- Tileset *tileset = new Tileset;
+ if (tileset == nullptr) {
+ tileset = new Tileset;
+ }
tileset->name = label;
tileset->is_compressed = values->value(0);
tileset->is_secondary = values->value(1);
@@ -750,7 +823,8 @@ void Project::loadTilesetAssets(Tileset* tileset) {
if (tileset->name.isNull()) {
return;
}
- QString dir_path = root + "/data/tilesets/" + category + "/" + tileset->name.replace("gTileset_", "").toLower();
+ QString tilesetName = tileset->name;
+ QString dir_path = root + "/data/tilesets/" + category + "/" + tilesetName.replace("gTileset_", "").toLower();
QString graphics_text = readTextFile(root + "/data/tilesets/graphics.inc");
QList *graphics = parser->parseAsm(graphics_text);
@@ -767,53 +841,94 @@ void Project::loadTilesetAssets(Tileset* tileset) {
}
}
- QStringList *palette_paths = new QStringList;
if (!palettes_values->isEmpty()) {
for (int i = 0; i < palettes_values->length(); i++) {
QString value = palettes_values->value(i);
- palette_paths->append(root + "/" + value.section('"', 1, 1));
+ tileset->palettePaths.append(this->fixPalettePath(root + "/" + value.section('"', 1, 1)));
}
} else {
QString palettes_dir_path = dir_path + "/palettes";
for (int i = 0; i < 16; i++) {
- palette_paths->append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".gbapal");
+ tileset->palettePaths.append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".pal");
}
}
- QString metatiles_path;
- QString metatile_attrs_path;
QString metatiles_text = readTextFile(root + "/data/tilesets/metatiles.inc");
QList *metatiles_macros = parser->parseAsm(metatiles_text);
QStringList *metatiles_values = getLabelValues(metatiles_macros, tileset->metatiles_label);
if (!metatiles_values->isEmpty()) {
- metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1);
+ tileset->metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1);
} else {
- metatiles_path = dir_path + "/metatiles.bin";
+ tileset->metatiles_path = dir_path + "/metatiles.bin";
}
QStringList *metatile_attrs_values = getLabelValues(metatiles_macros, tileset->metatile_attrs_label);
if (!metatile_attrs_values->isEmpty()) {
- metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1);
+ tileset->metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1);
} else {
- metatile_attrs_path = dir_path + "/metatile_attributes.bin";
+ tileset->metatile_attrs_path = dir_path + "/metatile_attributes.bin";
}
- // tiles
tiles_path = fixGraphicPath(tiles_path);
- QImage *image = new QImage(tiles_path);
- //image->setColor(0, qRgb(0xff, 0, 0)); // debug
+ tileset->tilesImagePath = tiles_path;
+ QImage image = QImage(tileset->tilesImagePath);
+ this->loadTilesetTiles(tileset, image);
+ this->loadTilesetMetatiles(tileset);
+ // palettes
+ QList> *palettes = new QList>;
+ for (int i = 0; i < tileset->palettePaths.length(); i++) {
+ QList palette;
+ QString path = tileset->palettePaths.value(i);
+ QString text = readTextFile(path);
+ if (!text.isNull()) {
+ QStringList lines = text.split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
+ if (lines.length() == 19 && lines[0] == "JASC-PAL" && lines[1] == "0100" && lines[2] == "16") {
+ for (int j = 0; j < 16; j++) {
+ QStringList rgb = lines[j + 3].split(QRegExp(" "), QString::SkipEmptyParts);
+ if (rgb.length() != 3) {
+ qDebug() << QString("Invalid tileset palette RGB value: '%1'").arg(lines[j + 3]);
+ palette.append(qRgb((j - 3) * 16, (j - 3) * 16, (j - 3) * 16));
+ } else {
+ int red = rgb[0].toInt();
+ int green = rgb[1].toInt();
+ int blue = rgb[2].toInt();
+ QRgb color = qRgb(red, green, blue);
+ palette.append(color);
+ }
+ }
+ } else {
+ qDebug() << QString("Invalid JASC-PAL palette file for tileset.");
+ for (int j = 0; j < 16; j++) {
+ palette.append(qRgb(j * 16, j * 16, j * 16));
+ }
+ }
+ } 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 *tiles = new QList;
int w = 8;
int h = 8;
- for (int y = 0; y < image->height(); y += h)
- for (int x = 0; x < image->width(); x += w) {
- QImage tile = image->copy(x, y, w, h);
+ for (int y = 0; y < image.height(); y += h)
+ for (int x = 0; x < image.width(); x += w) {
+ QImage tile = image.copy(x, y, w, h);
tiles->append(tile);
}
+ tileset->tilesImage = image;
tileset->tiles = tiles;
+}
- // metatiles
- QFile metatiles_file(metatiles_path);
+void Project::loadTilesetMetatiles(Tileset* tileset) {
+ QFile metatiles_file(tileset->metatiles_path);
if (metatiles_file.open(QIODevice::ReadOnly)) {
QByteArray data = metatiles_file.readAll();
int num_metatiles = data.length() / 16;
@@ -837,11 +952,10 @@ void Project::loadTilesetAssets(Tileset* tileset) {
tileset->metatiles = metatiles;
} else {
tileset->metatiles = new QList;
- qDebug() << QString("Could not open '%1'").arg(metatiles_path);
+ qDebug() << QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path);
}
- QFile attrs_file(metatile_attrs_path);
- //qDebug() << metatile_attrs_path;
+ QFile attrs_file(tileset->metatile_attrs_path);
if (attrs_file.open(QIODevice::ReadOnly)) {
QByteArray data = attrs_file.readAll();
int num_metatiles = tileset->metatiles->count();
@@ -851,41 +965,14 @@ void Project::loadTilesetAssets(Tileset* tileset) {
if (num_metatiles > num_metatileAttrs)
num_metatiles = num_metatileAttrs;
}
- } else {
- qDebug() << QString("Could not open '%1'").arg(metatile_attrs_path);
- }
-
- // palettes
- QList> *palettes = new QList>;
- 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 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);
+ for (int i = 0; i < num_metatileAttrs; i++) {
+ int value = (static_cast(data.at(i * 2 + 1)) << 8) | static_cast(data.at(i * 2));
+ tileset->metatiles->at(i)->behavior = value & 0xFF;
+ tileset->metatiles->at(i)->layerType = (value & 0xF000) >> 12;
}
-
- palettes->append(palette);
+ } else {
+ qDebug() << QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path);
}
- tileset->palettes = palettes;
}
Blockdata* Project::readBlockdata(QString path) {
@@ -913,11 +1000,16 @@ Map* Project::getMap(QString map_name) {
}
}
-Tileset* Project::getTileset(QString label) {
+Tileset* Project::getTileset(QString label, bool forceLoad) {
+ Tileset *existingTileset = nullptr;
if (tileset_cache->contains(label)) {
+ existingTileset = tileset_cache->value(label);
+ }
+
+ if (existingTileset && !forceLoad) {
return tileset_cache->value(label);
} else {
- Tileset *tileset = loadTileset(label);
+ Tileset *tileset = loadTileset(label, existingTileset);
return tileset;
}
}
@@ -1249,6 +1341,22 @@ void Project::readBgEventFacingDirections() {
readCDefinesSorted(filepath, prefixes, bgEventFacingDirections);
}
+void Project::readMetatileBehaviors() {
+ this->metatileBehaviorMap.clear();
+ this->metatileBehaviorMapInverse.clear();
+ QString filepath = root + "/include/constants/metatile_behaviors.h";
+ QString text = readTextFile(filepath);
+ if (!text.isNull()) {
+ QStringList prefixes = (QStringList() << "MB_");
+ this->metatileBehaviorMap = readCDefines(text, prefixes);
+ for (QString defineName : this->metatileBehaviorMap.keys()) {
+ this->metatileBehaviorMapInverse.insert(this->metatileBehaviorMap[defineName], defineName);
+ }
+ } else {
+ qDebug() << "Failed to read C defines file: " << filepath;
+ }
+}
+
void Project::readCDefinesSorted(QString filepath, QStringList prefixes, QStringList* definesToSet) {
QString text = readTextFile(filepath);
if (!text.isNull()) {
@@ -1324,6 +1432,11 @@ QMap Project::getEventObjGfxConstants() {
return constants;
}
+QString Project::fixPalettePath(QString path) {
+ path = path.replace(QRegExp("\\.gbapal$"), ".pal");
+ return path;
+}
+
QString Project::fixGraphicPath(QString path) {
path = path.replace(QRegExp("\\.lz$"), "");
path = path.replace(QRegExp("\\.[1248]bpp$"), ".png");
diff --git a/src/ui/imageproviders.cpp b/src/ui/imageproviders.cpp
index d5873800..b9a6a6ad 100644
--- a/src/ui/imageproviders.cpp
+++ b/src/ui/imageproviders.cpp
@@ -13,7 +13,7 @@ QImage getCollisionMetatileImage(int collision, int elevation) {
return collisionImage.toImage();
}
-QImage getMetatileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
+QImage getMetatileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
QImage metatile_image(16, 16, QImage::Format_RGBA8888);
Metatile* metatile = Tileset::getMetatile(tile, primaryTileset, secondaryTileset);
@@ -71,7 +71,7 @@ QImage getMetatileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTil
return metatile_image;
}
-QImage getTileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
+QImage getTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
Tileset *tileset = Tileset::getBlockTileset(tile, primaryTileset, secondaryTileset);
int local_index = Metatile::getBlockIndex(tile);
if (!tileset || !tileset->tiles) {
@@ -79,3 +79,19 @@ QImage getTileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTileset
}
return tileset->tiles->value(local_index, QImage());
}
+
+QImage getColoredTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId) {
+ QList palette = Tileset::getPalette(paletteId, primaryTileset, secondaryTileset);
+ QImage tileImage = getTileImage(tile, primaryTileset, secondaryTileset);
+ if (tileImage.isNull()) {
+ tileImage = QImage(16, 16, QImage::Format_RGBA8888);
+ QPainter painter(&tileImage);
+ painter.fillRect(0, 0, 16, 16, palette.at(0));
+ } else {
+ for (int i = 0; i < 16; i++) {
+ tileImage.setColor(i, palette.at(i));
+ }
+ }
+
+ return tileImage;
+}
diff --git a/src/ui/metatilelayersitem.cpp b/src/ui/metatilelayersitem.cpp
new file mode 100644
index 00000000..fe8b164f
--- /dev/null
+++ b/src/ui/metatilelayersitem.cpp
@@ -0,0 +1,45 @@
+#include "metatilelayersitem.h"
+#include "imageproviders.h"
+#include
+
+void MetatileLayersItem::draw() {
+ const QList tileCoords = QList{
+ QPoint(0, 0),
+ QPoint(16, 0),
+ QPoint(0, 16),
+ QPoint(16, 16),
+ QPoint(32, 0),
+ QPoint(48, 0),
+ QPoint(32, 16),
+ QPoint(48, 16),
+ };
+
+ QPixmap pixmap(64, 32);
+ QPainter painter(&pixmap);
+ for (int i = 0; i < 8; i++) {
+ Tile tile = this->metatile->tiles->at(i);
+ QImage tileImage = getColoredTileImage(tile.tile, this->primaryTileset, this->secondaryTileset, tile.palette)
+ .mirrored(tile.xflip, tile.yflip)
+ .scaled(16, 16);
+ painter.drawImage(tileCoords.at(i), tileImage);
+ }
+
+ this->setPixmap(pixmap);
+}
+
+void MetatileLayersItem::setMetatile(Metatile *metatile) {
+ this->metatile = metatile;
+}
+
+void MetatileLayersItem::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
+ this->primaryTileset = primaryTileset;
+ this->secondaryTileset = secondaryTileset;
+ this->draw();
+}
+
+void MetatileLayersItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+ QPointF pos = event->pos();
+ int x = static_cast(pos.x()) / 16;
+ int y = static_cast(pos.y()) / 16;
+ emit this->tileChanged(x, y);
+}
diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp
new file mode 100644
index 00000000..bd67fa5f
--- /dev/null
+++ b/src/ui/paletteeditor.cpp
@@ -0,0 +1,176 @@
+#include "paletteeditor.h"
+#include "ui_paletteeditor.h"
+
+PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::PaletteEditor)
+{
+ this->project = project;
+ this->primaryTileset = primaryTileset;
+ this->secondaryTileset = secondaryTileset;
+ this->ui->setupUi(this);
+ this->ui->spinBox_PaletteId->setMinimum(0);
+ this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1);
+ this->sliders.clear();
+ for (int i = 0; i < 16; i++) {
+ this->sliders.append(QList());
+ }
+ this->sliders[0].append(this->ui->horizontalSlider);
+ this->sliders[0].append(this->ui->horizontalSlider_2);
+ this->sliders[0].append(this->ui->horizontalSlider_3);
+ this->sliders[1].append(this->ui->horizontalSlider_4);
+ this->sliders[1].append(this->ui->horizontalSlider_5);
+ this->sliders[1].append(this->ui->horizontalSlider_6);
+ this->sliders[2].append(this->ui->horizontalSlider_7);
+ this->sliders[2].append(this->ui->horizontalSlider_8);
+ this->sliders[2].append(this->ui->horizontalSlider_9);
+ this->sliders[3].append(this->ui->horizontalSlider_10);
+ this->sliders[3].append(this->ui->horizontalSlider_11);
+ this->sliders[3].append(this->ui->horizontalSlider_12);
+ this->sliders[4].append(this->ui->horizontalSlider_13);
+ this->sliders[4].append(this->ui->horizontalSlider_14);
+ this->sliders[4].append(this->ui->horizontalSlider_15);
+ this->sliders[5].append(this->ui->horizontalSlider_16);
+ this->sliders[5].append(this->ui->horizontalSlider_17);
+ this->sliders[5].append(this->ui->horizontalSlider_18);
+ this->sliders[6].append(this->ui->horizontalSlider_19);
+ this->sliders[6].append(this->ui->horizontalSlider_20);
+ this->sliders[6].append(this->ui->horizontalSlider_21);
+ this->sliders[7].append(this->ui->horizontalSlider_22);
+ this->sliders[7].append(this->ui->horizontalSlider_23);
+ this->sliders[7].append(this->ui->horizontalSlider_24);
+ this->sliders[8].append(this->ui->horizontalSlider_25);
+ this->sliders[8].append(this->ui->horizontalSlider_26);
+ this->sliders[8].append(this->ui->horizontalSlider_27);
+ this->sliders[9].append(this->ui->horizontalSlider_28);
+ this->sliders[9].append(this->ui->horizontalSlider_29);
+ this->sliders[9].append(this->ui->horizontalSlider_30);
+ this->sliders[10].append(this->ui->horizontalSlider_31);
+ this->sliders[10].append(this->ui->horizontalSlider_32);
+ this->sliders[10].append(this->ui->horizontalSlider_33);
+ this->sliders[11].append(this->ui->horizontalSlider_34);
+ this->sliders[11].append(this->ui->horizontalSlider_35);
+ this->sliders[11].append(this->ui->horizontalSlider_36);
+ this->sliders[12].append(this->ui->horizontalSlider_37);
+ this->sliders[12].append(this->ui->horizontalSlider_38);
+ this->sliders[12].append(this->ui->horizontalSlider_39);
+ this->sliders[13].append(this->ui->horizontalSlider_40);
+ this->sliders[13].append(this->ui->horizontalSlider_41);
+ this->sliders[13].append(this->ui->horizontalSlider_42);
+ this->sliders[14].append(this->ui->horizontalSlider_43);
+ this->sliders[14].append(this->ui->horizontalSlider_44);
+ this->sliders[14].append(this->ui->horizontalSlider_45);
+ this->sliders[15].append(this->ui->horizontalSlider_46);
+ this->sliders[15].append(this->ui->horizontalSlider_47);
+ this->sliders[15].append(this->ui->horizontalSlider_48);
+
+ this->frames.clear();
+ this->frames.append(this->ui->frame);
+ this->frames.append(this->ui->frame_2);
+ this->frames.append(this->ui->frame_3);
+ this->frames.append(this->ui->frame_4);
+ this->frames.append(this->ui->frame_5);
+ this->frames.append(this->ui->frame_6);
+ this->frames.append(this->ui->frame_7);
+ this->frames.append(this->ui->frame_8);
+ this->frames.append(this->ui->frame_9);
+ this->frames.append(this->ui->frame_10);
+ this->frames.append(this->ui->frame_11);
+ this->frames.append(this->ui->frame_12);
+ this->frames.append(this->ui->frame_13);
+ this->frames.append(this->ui->frame_14);
+ this->frames.append(this->ui->frame_15);
+ this->frames.append(this->ui->frame_16);
+
+ this->initColorSliders();
+ this->refreshColorSliders();
+ this->refreshColors();
+}
+
+PaletteEditor::~PaletteEditor()
+{
+ delete ui;
+}
+
+void PaletteEditor::disableSliderSignals() {
+ for (int i = 0; i < this->sliders.length(); i++) {
+ this->sliders.at(i).at(0)->blockSignals(true);
+ this->sliders.at(i).at(1)->blockSignals(true);
+ this->sliders.at(i).at(2)->blockSignals(true);
+ }
+}
+
+void PaletteEditor::enableSliderSignals() {
+ for (int i = 0; i < this->sliders.length(); i++) {
+ this->sliders.at(i).at(0)->blockSignals(false);
+ this->sliders.at(i).at(1)->blockSignals(false);
+ this->sliders.at(i).at(2)->blockSignals(false);
+ }
+}
+
+void PaletteEditor::initColorSliders() {
+ for (int i = 0; i < 16; i++) {
+ connect(this->sliders[i][0], &QSlider::valueChanged, [=](int) { this->setColor(i); });
+ connect(this->sliders[i][1], &QSlider::valueChanged, [=](int) { this->setColor(i); });
+ connect(this->sliders[i][2], &QSlider::valueChanged, [=](int) { this->setColor(i); });
+ }
+}
+
+void PaletteEditor::refreshColorSliders() {
+ disableSliderSignals();
+ int paletteNum = this->ui->spinBox_PaletteId->value();
+ for (int i = 0; i < 16; i++) {
+ QRgb color;
+ if (paletteNum < Project::getNumPalettesPrimary()) {
+ color = this->primaryTileset->palettes->at(paletteNum).at(i);
+ } else {
+ color = this->secondaryTileset->palettes->at(paletteNum).at(i);
+ }
+
+ this->sliders[i][0]->setValue(qRed(color) / 8);
+ this->sliders[i][1]->setValue(qGreen(color) / 8);
+ this->sliders[i][2]->setValue(qBlue(color) / 8);
+ }
+ enableSliderSignals();
+}
+
+void PaletteEditor::refreshColors() {
+ for (int i = 0; i < 16; i++) {
+ this->refreshColor(i);
+ }
+}
+
+void PaletteEditor::refreshColor(int colorIndex) {
+ QString stylesheet = QString("background-color: rgb(%1, %2, %3);")
+ .arg(this->sliders[colorIndex][0]->value() * 8)
+ .arg(this->sliders[colorIndex][1]->value() * 8)
+ .arg(this->sliders[colorIndex][2]->value() * 8);
+ this->frames[colorIndex]->setStyleSheet(stylesheet);
+}
+
+void PaletteEditor::setPaletteId(int paletteId) {
+ this->ui->spinBox_PaletteId->blockSignals(true);
+ this->ui->spinBox_PaletteId->setValue(paletteId);
+ this->refreshColorSliders();
+ this->refreshColors();
+ this->ui->spinBox_PaletteId->blockSignals(false);
+}
+
+void PaletteEditor::setColor(int colorIndex) {
+ int paletteNum = this->ui->spinBox_PaletteId->value();
+ int red = this->sliders[colorIndex][0]->value() * 8;
+ int green = this->sliders[colorIndex][1]->value() * 8;
+ int blue = this->sliders[colorIndex][2]->value() * 8;
+ Tileset *tileset = paletteNum < Project::getNumPalettesPrimary()
+ ? this->primaryTileset
+ : this->secondaryTileset;
+ (*tileset->palettes)[paletteNum][colorIndex] = qRgb(red, green, blue);
+ this->refreshColor(colorIndex);
+ emit this->changedPaletteColor();
+}
+
+void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
+ this->refreshColorSliders();
+ this->refreshColors();
+ emit this->changedPalette(paletteId);
+}
diff --git a/src/ui/selectablepixmapitem.cpp b/src/ui/selectablepixmapitem.cpp
index 5dcd252f..dbfbaa34 100644
--- a/src/ui/selectablepixmapitem.cpp
+++ b/src/ui/selectablepixmapitem.cpp
@@ -80,6 +80,10 @@ void SelectablePixmapItem::updateSelection(int x, int y)
QPoint SelectablePixmapItem::getCellPos(QPointF pos)
{
+ if (pos.x() < 0) pos.setX(0);
+ if (pos.y() < 0) pos.setY(0);
+ if (pos.x() >= this->pixmap().width()) pos.setX(this->pixmap().width() - 1);
+ if (pos.y() >= this->pixmap().height()) pos.setY(this->pixmap().height() - 1);
return QPoint(static_cast(pos.x()) / this->cellWidth,
static_cast(pos.y()) / this->cellHeight);
}
diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp
index b4be9766..60fe30d4 100644
--- a/src/ui/tileseteditor.cpp
+++ b/src/ui/tileseteditor.cpp
@@ -1,14 +1,460 @@
#include "tileseteditor.h"
#include "ui_tileseteditor.h"
+#include "imageproviders.h"
+#include
+#include
+#include
+#include
-TilesetEditor::TilesetEditor(QWidget *parent) :
- QDialog(parent),
+TilesetEditor::TilesetEditor(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel, QWidget *parent) :
+ QMainWindow(parent),
ui(new Ui::TilesetEditor)
{
- ui->setupUi(this);
+ this->init(project, primaryTilesetLabel, secondaryTilesetLabel);
}
TilesetEditor::~TilesetEditor()
{
delete ui;
}
+
+void TilesetEditor::init(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel) {
+ ui->setupUi(this);
+ this->project = project;
+
+ this->hasUnsavedChanges = false;
+ this->tileXFlip = ui->checkBox_xFlip->isChecked();
+ this->tileYFlip = ui->checkBox_yFlip->isChecked();
+ this->paletteId = ui->spinBox_paletteSelector->value();
+
+ Tileset *primaryTileset = project->getTileset(primaryTilesetLabel);
+ Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
+ if (this->primaryTileset) delete this->primaryTileset;
+ if (this->secondaryTileset) delete this->secondaryTileset;
+ this->primaryTileset = primaryTileset->copy();
+ this->secondaryTileset = secondaryTileset->copy();
+
+ QList sortedBehaviors;
+ for (int num : project->metatileBehaviorMapInverse.keys()) {
+ this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num);
+ }
+ this->ui->comboBox_layerType->addItem("Normal - Middle/Top", 0);
+ this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", 1);
+ this->ui->comboBox_layerType->addItem("Split - Bottom/Top", 2);
+ this->ui->spinBox_paletteSelector->setMinimum(0);
+ this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1);
+
+ this->initMetatileSelector();
+ this->initMetatileLayersItem();
+ this->initTileSelector();
+ this->initSelectedTileItem();
+ this->metatileSelector->select(0);
+}
+
+void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel) {
+ delete this->primaryTileset;
+ delete this->secondaryTileset;
+ Tileset *primaryTileset = project->getTileset(primaryTilesetLabel);
+ Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
+ this->primaryTileset = primaryTileset->copy();
+ this->secondaryTileset = secondaryTileset->copy();
+ this->refresh();
+}
+
+void TilesetEditor::refresh() {
+ this->metatileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
+ this->tileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
+ this->metatileLayersItem->setTilesets(this->primaryTileset, this->secondaryTileset);
+ this->metatileSelector->select(this->metatileSelector->getSelectedMetatile());
+ this->drawSelectedTiles();
+
+ 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);
+ this->ui->graphicsView_Metatiles->setFixedSize(this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2);
+ this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height());
+}
+
+void TilesetEditor::initMetatileSelector()
+{
+ this->metatileSelector = new TilesetEditorMetatileSelector(this->primaryTileset, this->secondaryTileset);
+ connect(this->metatileSelector, SIGNAL(hoveredMetatileChanged(uint16_t)),
+ this, SLOT(onHoveredMetatileChanged(uint16_t)));
+ connect(this->metatileSelector, SIGNAL(hoveredMetatileCleared()),
+ this, SLOT(onHoveredMetatileCleared()));
+ connect(this->metatileSelector, SIGNAL(selectedMetatileChanged(uint16_t)),
+ this, SLOT(onSelectedMetatileChanged(uint16_t)));
+
+ this->metatilesScene = new QGraphicsScene;
+ this->metatilesScene->addItem(this->metatileSelector);
+ this->metatileSelector->draw();
+
+ this->ui->graphicsView_Metatiles->setScene(this->metatilesScene);
+ this->ui->graphicsView_Metatiles->setFixedSize(this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2);
+}
+
+void TilesetEditor::initTileSelector()
+{
+ this->tileSelector = new TilesetEditorTileSelector(this->primaryTileset, this->secondaryTileset);
+ connect(this->tileSelector, SIGNAL(hoveredTileChanged(uint16_t)),
+ this, SLOT(onHoveredTileChanged(uint16_t)));
+ connect(this->tileSelector, SIGNAL(hoveredTileCleared()),
+ this, SLOT(onHoveredTileCleared()));
+ connect(this->tileSelector, SIGNAL(selectedTilesChanged()),
+ this, SLOT(onSelectedTilesChanged()));
+
+ this->tilesScene = new QGraphicsScene;
+ this->tilesScene->addItem(this->tileSelector);
+ this->tileSelector->select(0);
+ this->tileSelector->draw();
+
+ this->ui->graphicsView_Tiles->setScene(this->tilesScene);
+ this->ui->graphicsView_Tiles->setFixedSize(this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2);
+}
+
+void TilesetEditor::initSelectedTileItem() {
+ this->selectedTileScene = new QGraphicsScene;
+ this->drawSelectedTiles();
+ this->ui->graphicsView_selectedTile->setScene(this->selectedTileScene);
+ this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height());
+}
+
+void TilesetEditor::drawSelectedTiles() {
+ if (!this->selectedTileScene) {
+ return;
+ }
+
+ this->selectedTileScene->clear();
+ QList tiles = this->tileSelector->getSelectedTiles();
+ QPoint dimensions = this->tileSelector->getSelectionDimensions();
+ QImage selectionImage(16 * dimensions.x(), 16 * dimensions.y(), QImage::Format_RGBA8888);
+ QPainter painter(&selectionImage);
+ int tileIndex = 0;
+ for (int j = 0; j < dimensions.y(); j++) {
+ for (int i = 0; i < dimensions.x(); i++) {
+ QImage tileImage = getColoredTileImage(tiles.at(tileIndex), this->primaryTileset, this->secondaryTileset, this->paletteId)
+ .mirrored(this->tileXFlip, this->tileYFlip)
+ .scaled(16, 16);
+ tileIndex++;
+ painter.drawImage(i * 16, j * 16, tileImage);
+ }
+ }
+
+ this->selectedTilePixmapItem = new QGraphicsPixmapItem(QPixmap::fromImage(selectionImage));
+ this->selectedTileScene->addItem(this->selectedTilePixmapItem);
+ this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height());
+}
+
+void TilesetEditor::initMetatileLayersItem() {
+ Metatile *metatile = Tileset::getMetatile(this->metatileSelector->getSelectedMetatile(), this->primaryTileset, this->secondaryTileset);
+ this->metatileLayersItem = new MetatileLayersItem(metatile, this->primaryTileset, this->secondaryTileset);
+ connect(this->metatileLayersItem, SIGNAL(tileChanged(int, int)),
+ this, SLOT(onMetatileLayerTileChanged(int, int)));
+
+ this->metatileLayersScene = new QGraphicsScene;
+ this->metatileLayersScene->addItem(this->metatileLayersItem);
+ this->ui->graphicsView_metatileLayers->setScene(this->metatileLayersScene);
+}
+
+void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) {
+ QString message = QString("Metatile: 0x%1")
+ .arg(QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper());
+ this->ui->statusbar->showMessage(message);
+}
+
+void TilesetEditor::onHoveredMetatileCleared() {
+ this->ui->statusbar->clearMessage();
+}
+
+void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) {
+ this->metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
+ this->metatileLayersItem->setMetatile(metatile);
+ this->metatileLayersItem->draw();
+ this->ui->comboBox_metatileBehaviors->setCurrentIndex(this->ui->comboBox_metatileBehaviors->findData(this->metatile->behavior));
+ this->ui->comboBox_layerType->setCurrentIndex(this->ui->comboBox_layerType->findData(this->metatile->layerType));
+}
+
+void TilesetEditor::onHoveredTileChanged(uint16_t tile) {
+ QString message = QString("Tile: 0x%1")
+ .arg(QString("%1").arg(tile, 3, 16, QChar('0')).toUpper());
+ this->ui->statusbar->showMessage(message);
+}
+
+void TilesetEditor::onHoveredTileCleared() {
+ this->ui->statusbar->clearMessage();
+}
+
+void TilesetEditor::onSelectedTilesChanged() {
+ this->drawSelectedTiles();
+}
+
+void TilesetEditor::onMetatileLayerTileChanged(int x, int y) {
+ int maxTileIndex = x < 2 ? 3 : 7;
+ QPoint dimensions = this->tileSelector->getSelectionDimensions();
+ QList tiles = this->tileSelector->getSelectedTiles();
+ int selectedTileIndex = 0;
+ for (int j = 0; j < dimensions.y(); j++) {
+ for (int i = 0; i < dimensions.x(); i++) {
+ int tileIndex = ((x + i) / 2 * 4) + ((y + j) * 2) + ((x + i) % 2);
+ if (tileIndex <= maxTileIndex) {
+ Tile tile = this->metatile->tiles->at(tileIndex);
+ tile.tile = tiles.at(selectedTileIndex);
+ tile.xflip = this->tileXFlip;
+ tile.yflip = this->tileYFlip;
+ tile.palette = this->paletteId;
+ (*this->metatile->tiles)[tileIndex] = tile;
+ }
+ selectedTileIndex++;
+ }
+ }
+
+ this->metatileSelector->draw();
+ this->metatileLayersItem->draw();
+ this->hasUnsavedChanges = true;
+}
+
+void TilesetEditor::on_spinBox_paletteSelector_valueChanged(int paletteId)
+{
+ this->ui->spinBox_paletteSelector->blockSignals(true);
+ this->ui->spinBox_paletteSelector->setValue(paletteId);
+ this->ui->spinBox_paletteSelector->blockSignals(false);
+ this->paletteId = paletteId;
+ this->tileSelector->setPaletteId(paletteId);
+ this->drawSelectedTiles();
+ if (this->paletteEditor) {
+ this->paletteEditor->setPaletteId(paletteId);
+ }
+}
+
+void TilesetEditor::on_checkBox_xFlip_stateChanged(int checked)
+{
+ this->tileXFlip = checked;
+ this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip);
+ this->drawSelectedTiles();
+}
+
+void TilesetEditor::on_checkBox_yFlip_stateChanged(int checked)
+{
+ this->tileYFlip = checked;
+ this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip);
+ this->drawSelectedTiles();
+}
+
+void TilesetEditor::on_comboBox_metatileBehaviors_currentIndexChanged(const QString &metatileBehavior)
+{
+ if (this->metatile) {
+ this->metatile->behavior = static_cast(project->metatileBehaviorMap[metatileBehavior]);
+ }
+}
+
+void TilesetEditor::on_comboBox_layerType_currentIndexChanged(int layerType)
+{
+ if (this->metatile) {
+ this->metatile->layerType = static_cast(layerType);
+ }
+}
+
+void TilesetEditor::on_actionSave_Tileset_triggered()
+{
+ this->project->saveTilesets(this->primaryTileset, this->secondaryTileset);
+ emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
+ this->ui->statusbar->showMessage(QString("Saved primary and secondary Tilesets!"), 5000);
+ this->hasUnsavedChanges = false;
+}
+
+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();
+ this->hasUnsavedChanges = true;
+}
+
+void TilesetEditor::closeEvent(QCloseEvent *event)
+{
+ bool close = true;
+ if (this->hasUnsavedChanges) {
+ QMessageBox::StandardButton result = QMessageBox::question(this, "porymap",
+ "Discard unsaved Tileset changes?",
+ QMessageBox::No | QMessageBox::Yes,
+ QMessageBox::Yes);
+ close = result == QMessageBox::Yes;
+ }
+
+ if (close) {
+ event->accept();
+ emit closed();
+ } else {
+ event->ignore();
+ }
+}
+
+void TilesetEditor::on_actionChange_Metatiles_Count_triggered()
+{
+ QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
+ dialog.setWindowTitle("Change Number of Metatiles");
+ dialog.setWindowModality(Qt::NonModal);
+
+ QFormLayout form(&dialog);
+
+ QSpinBox *primarySpinBox = new QSpinBox();
+ QSpinBox *secondarySpinBox = new QSpinBox();
+ primarySpinBox->setMinimum(1);
+ secondarySpinBox->setMinimum(1);
+ primarySpinBox->setMaximum(Project::getNumMetatilesPrimary());
+ secondarySpinBox->setMaximum(Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary());
+ primarySpinBox->setValue(this->primaryTileset->metatiles->length());
+ secondarySpinBox->setValue(this->secondaryTileset->metatiles->length());
+ form.addRow(new QLabel("Primary Tileset"), primarySpinBox);
+ form.addRow(new QLabel("Secondary Tileset"), secondarySpinBox);
+
+ QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
+ connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
+ connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
+ form.addRow(&buttonBox);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ int numPrimaryMetatiles = primarySpinBox->value();
+ int numSecondaryMetatiles = secondarySpinBox->value();
+ while (this->primaryTileset->metatiles->length() > numPrimaryMetatiles) {
+ Metatile *metatile = this->primaryTileset->metatiles->takeLast();
+ delete metatile;
+ }
+ while (this->primaryTileset->metatiles->length() < numPrimaryMetatiles) {
+ Tile tile;
+ tile.palette = 0;
+ tile.tile = 0;
+ tile.xflip = 0;
+ tile.yflip = 0;
+ Metatile *metatile = new Metatile;
+ metatile->behavior = 0;
+ metatile->layerType = 0;
+ for (int i = 0; i < 8; i++) {
+ metatile->tiles->append(tile);
+ }
+ this->primaryTileset->metatiles->append(metatile);
+ }
+ while (this->secondaryTileset->metatiles->length() > numSecondaryMetatiles) {
+ Metatile *metatile = this->secondaryTileset->metatiles->takeLast();
+ delete metatile;
+ }
+ while (this->secondaryTileset->metatiles->length() < numSecondaryMetatiles) {
+ Tile tile;
+ tile.palette = 0;
+ tile.tile = 0;
+ tile.xflip = 0;
+ tile.yflip = 0;
+ Metatile *metatile = new Metatile;
+ metatile->behavior = 0;
+ metatile->layerType = 0;
+ for (int i = 0; i < 8; i++) {
+ metatile->tiles->append(tile);
+ }
+ this->secondaryTileset->metatiles->append(metatile);
+ }
+
+ this->refresh();
+ this->hasUnsavedChanges = true;
+ }
+}
+
+void TilesetEditor::onPaletteEditorClosed() {
+ if (this->paletteEditor) {
+ delete this->paletteEditor;
+ this->paletteEditor = nullptr;
+ }
+}
+
+void TilesetEditor::on_actionChange_Palettes_triggered()
+{
+ if (!this->paletteEditor) {
+ this->paletteEditor = new PaletteEditor(this->project, this->primaryTileset, this->secondaryTileset, this);
+ connect(this->paletteEditor, SIGNAL(closed()), this, SLOT(onPaletteEditorClosed()));
+ connect(this->paletteEditor, SIGNAL(changedPaletteColor()), this, SLOT(onPaletteEditorChangedPaletteColor()));
+ connect(this->paletteEditor, SIGNAL(changedPalette(int)), this, SLOT(onPaletteEditorChangedPalette(int)));
+ }
+
+ if (!this->paletteEditor->isVisible()) {
+ this->paletteEditor->show();
+ } else if (this->paletteEditor->isMinimized()) {
+ this->paletteEditor->showNormal();
+ } else {
+ this->paletteEditor->activateWindow();
+ }
+}
+
+void TilesetEditor::onPaletteEditorChangedPaletteColor() {
+ this->refresh();
+ this->hasUnsavedChanges = true;
+}
+
+void TilesetEditor::onPaletteEditorChangedPalette(int paletteId) {
+ this->on_spinBox_paletteSelector_valueChanged(paletteId);
+}
diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp
new file mode 100644
index 00000000..852ab0e9
--- /dev/null
+++ b/src/ui/tileseteditormetatileselector.cpp
@@ -0,0 +1,114 @@
+#include "tileseteditormetatileselector.h"
+#include "imageproviders.h"
+#include "project.h"
+#include
+
+void TilesetEditorMetatileSelector::draw() {
+ if (!this->primaryTileset || !this->primaryTileset->metatiles
+ || !this->secondaryTileset || !this->secondaryTileset->metatiles) {
+ this->setPixmap(QPixmap());
+ }
+
+ int primaryLength = this->primaryTileset->metatiles->length();
+ int length_ = primaryLength + this->secondaryTileset->metatiles->length();
+ int height_ = length_ / this->numMetatilesWide;
+ QImage image(this->numMetatilesWide * 32, height_ * 32, QImage::Format_RGBA8888);
+ QPainter painter(&image);
+ for (int i = 0; i < length_; i++) {
+ int tile = i;
+ if (i >= primaryLength) {
+ tile += Project::getNumMetatilesPrimary() - primaryLength;
+ }
+ QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset).scaled(32, 32);
+ int map_y = i / this->numMetatilesWide;
+ int map_x = i % this->numMetatilesWide;
+ QPoint metatile_origin = QPoint(map_x * 32, map_y * 32);
+ painter.drawImage(metatile_origin, metatile_image);
+ }
+
+ painter.end();
+ this->setPixmap(QPixmap::fromImage(image));
+ this->drawSelection();
+}
+
+void TilesetEditorMetatileSelector::select(uint16_t metatileId) {
+ metatileId = this->getValidMetatileId(metatileId);
+ QPoint coords = this->getMetatileIdCoords(metatileId);
+ SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
+ this->selectedMetatile = metatileId;
+ emit selectedMetatileChanged(metatileId);
+}
+
+void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
+ this->primaryTileset = primaryTileset;
+ this->secondaryTileset = secondaryTileset;
+ this->draw();
+}
+
+void TilesetEditorMetatileSelector::updateSelectedMetatile() {
+ QPoint origin = this->getSelectionStart();
+ this->selectedMetatile = this->getMetatileId(origin.x(), origin.y());
+}
+
+uint16_t TilesetEditorMetatileSelector::getSelectedMetatile() {
+ return this->selectedMetatile;
+}
+
+uint16_t TilesetEditorMetatileSelector::getMetatileId(int x, int y) {
+ int index = y * this->numMetatilesWide + x;
+ if (index < this->primaryTileset->metatiles->length()) {
+ return static_cast(index);
+ } else {
+ return static_cast(Project::getNumMetatilesPrimary() + index - this->primaryTileset->metatiles->length());
+ }
+}
+
+void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+ SelectablePixmapItem::mousePressEvent(event);
+ this->updateSelectedMetatile();
+ emit selectedMetatileChanged(this->selectedMetatile);
+}
+
+void TilesetEditorMetatileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+ SelectablePixmapItem::mouseMoveEvent(event);
+ this->updateSelectedMetatile();
+
+ QPoint pos = this->getCellPos(event->pos());
+ uint16_t metatileId = this->getMetatileId(pos.x(), pos.y());
+ emit hoveredMetatileChanged(metatileId);
+ emit selectedMetatileChanged(metatileId);
+}
+
+void TilesetEditorMetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+ SelectablePixmapItem::mouseReleaseEvent(event);
+ this->updateSelectedMetatile();
+ emit selectedMetatileChanged(this->selectedMetatile);
+}
+
+void TilesetEditorMetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+ QPoint pos = this->getCellPos(event->pos());
+ uint16_t metatileId = this->getMetatileId(pos.x(), pos.y());
+ emit this->hoveredMetatileChanged(metatileId);
+}
+
+void TilesetEditorMetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
+ emit this->hoveredMetatileCleared();
+}
+
+QPoint TilesetEditorMetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
+ int index = metatileId < Project::getNumMetatilesPrimary()
+ ? metatileId
+ : metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->metatiles->length();
+ return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
+}
+
+uint16_t TilesetEditorMetatileSelector::getValidMetatileId(uint16_t metatileId) {
+ if (metatileId >= Project::getNumMetatilesTotal()
+ || (metatileId < Project::getNumMetatilesPrimary() && metatileId >= this->primaryTileset->metatiles->length())
+ || (metatileId < Project::getNumMetatilesTotal() && metatileId >= Project::getNumMetatilesPrimary() + this->secondaryTileset->metatiles->length()))
+ {
+ return 0;
+ }
+ return metatileId;
+}
+
diff --git a/src/ui/tileseteditortileselector.cpp b/src/ui/tileseteditortileselector.cpp
new file mode 100644
index 00000000..6d323f46
--- /dev/null
+++ b/src/ui/tileseteditortileselector.cpp
@@ -0,0 +1,130 @@
+#include "tileseteditortileselector.h"
+#include "imageproviders.h"
+#include "project.h"
+#include
+
+void TilesetEditorTileSelector::draw() {
+ if (!this->primaryTileset || !this->primaryTileset->tiles
+ || !this->secondaryTileset || !this->secondaryTileset->tiles) {
+ this->setPixmap(QPixmap());
+ }
+
+ int totalTiles = Project::getNumTilesTotal();
+ int primaryLength = this->primaryTileset->tiles->length();
+ int secondaryLength = this->secondaryTileset->tiles->length();
+ int height = totalTiles / this->numTilesWide;
+ QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset);
+ QImage image(this->numTilesWide * 16, height * 16, QImage::Format_RGBA8888);
+
+ QPainter painter(&image);
+ for (uint16_t tile = 0; tile < totalTiles; tile++) {
+ QImage tileImage;
+ if (tile < primaryLength) {
+ tileImage = getColoredTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16);
+ } else if (tile < Project::getNumTilesPrimary()) {
+ tileImage = QImage(16, 16, QImage::Format_RGBA8888);
+ tileImage.fill(palette.at(0));
+ } else if (tile < Project::getNumTilesPrimary() + secondaryLength) {
+ tileImage = getColoredTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16);
+ } else {
+ tileImage = QImage(16, 16, QImage::Format_RGBA8888);
+ QPainter painter(&tileImage);
+ painter.fillRect(0, 0, 16, 16, palette.at(0));
+ }
+
+ int y = tile / this->numTilesWide;
+ int x = tile % this->numTilesWide;
+ QPoint origin = QPoint(x * 16, y * 16);
+ painter.drawImage(origin, tileImage.mirrored(this->xFlip, this->yFlip));
+ }
+
+ painter.end();
+ this->setPixmap(QPixmap::fromImage(image));
+ this->drawSelection();
+}
+
+void TilesetEditorTileSelector::select(uint16_t tile) {
+ QPoint coords = this->getTileCoords(tile);
+ SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
+ this->updateSelectedTiles();
+ emit selectedTilesChanged();
+}
+
+void TilesetEditorTileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
+ this->primaryTileset = primaryTileset;
+ this->secondaryTileset = secondaryTileset;
+ this->draw();
+}
+
+void TilesetEditorTileSelector::setPaletteId(int paletteId) {
+ this->paletteId = paletteId;
+ this->draw();
+}
+
+void TilesetEditorTileSelector::setTileFlips(bool xFlip, bool yFlip) {
+ this->xFlip = xFlip;
+ this->yFlip = yFlip;
+ this->draw();
+}
+
+void TilesetEditorTileSelector::updateSelectedTiles() {
+ this->selectedTiles.clear();
+ QPoint origin = this->getSelectionStart();
+ QPoint dimensions = this->getSelectionDimensions();
+ for (int j = 0; j < dimensions.y(); j++) {
+ for (int i = 0; i < dimensions.x(); i++) {
+ uint16_t metatileId = this->getTileId(origin.x() + i, origin.y() + j);
+ this->selectedTiles.append(metatileId);
+ }
+ }
+}
+
+QList TilesetEditorTileSelector::getSelectedTiles() {
+ return this->selectedTiles;
+}
+
+uint16_t TilesetEditorTileSelector::getTileId(int x, int y) {
+ return static_cast(y * this->numTilesWide + x);
+}
+
+void TilesetEditorTileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+ SelectablePixmapItem::mousePressEvent(event);
+ this->updateSelectedTiles();
+ emit selectedTilesChanged();
+}
+
+void TilesetEditorTileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+ SelectablePixmapItem::mouseMoveEvent(event);
+ this->updateSelectedTiles();
+
+ QPoint pos = this->getCellPos(event->pos());
+ uint16_t tile = this->getTileId(pos.x(), pos.y());
+ emit hoveredTileChanged(tile);
+ emit selectedTilesChanged();
+}
+
+void TilesetEditorTileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+ SelectablePixmapItem::mouseReleaseEvent(event);
+ this->updateSelectedTiles();
+ emit selectedTilesChanged();
+}
+
+void TilesetEditorTileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+ QPoint pos = this->getCellPos(event->pos());
+ uint16_t tile = this->getTileId(pos.x(), pos.y());
+ emit this->hoveredTileChanged(tile);
+}
+
+void TilesetEditorTileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
+ emit this->hoveredTileCleared();
+}
+
+QPoint TilesetEditorTileSelector::getTileCoords(uint16_t tile) {
+ if (tile >= Project::getNumTilesTotal())
+ {
+ // Invalid tile.
+ return QPoint(0, 0);
+ }
+
+ return QPoint(tile % this->numTilesWide, tile / this->numTilesWide);
+}