diff --git a/.gitignore b/.gitignore index 3d265d53..bde28f17 100644 --- a/.gitignore +++ b/.gitignore @@ -2,12 +2,14 @@ porymap.pro.user *.autosave *.stash *.o -moc_* -qrc_* porymap.app* porymap +porymap.cfg +porymap.log Makefile -# Generated UI header +# Qt generated files ui_*.h - +moc_*.h +moc_*.cpp +qrc_*.cpp diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index 28fe1aa7..8aa623ee 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -2750,6 +2750,7 @@ + @@ -2977,6 +2978,11 @@ Cursor Tile Outline + + + Region Map Editor + + New Tileset... diff --git a/forms/regionmapeditor.ui b/forms/regionmapeditor.ui new file mode 100644 index 00000000..96cd6e10 --- /dev/null +++ b/forms/regionmapeditor.ui @@ -0,0 +1,1148 @@ + + + RegionMapEditor + + + + 0 + 0 + 829 + 644 + + + + Region Map Editor + + + + + + + Qt::Vertical + + + false + + + + Qt::Horizontal + + + false + + + + + 393 + 251 + + + + 0 + + + + Background Image + + + + + + + 1 + 1 + + + + true + + + + + 0 + 0 + 350 + 225 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + + 0 + 0 + + + + false + + + false + + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + + + + + + Map Layout + + + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 350 + 225 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + false + + + false + + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + + + + + + + 320 + 200 + + + + 0 + + + + + + + + 0 + 0 + + + + + 162 + 162 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + true + + + Qt::AlignHCenter|Qt::AlignTop + + + + true + + + + 8 + 0 + 278 + 262 + + + + + 0 + 0 + + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustIgnored + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + false + + + + 0 + 0 + + + + + 300 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 1 + + + + + 13 + 11 + 281 + 91 + + + + + + + Map Section + + + + + + + + 0 + 0 + + + + <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is display when the player enters it.</p></body></html> + + + true + + + QComboBox::NoInsert + + + + + + + Map Name + + + + + + + + + + City Map + + + + + + + <html><head/><body><p>The map type is a general attribute, which is used for many different things. For example. it determines whether biking or running is allowed.</p></body></html> + + + true + + + + + + + + + 10 + 100 + 113 + 32 + + + + Delete Square + + + + + + + + + + + Qt::Horizontal + + + false + + + + + QLayout::SetDefaultConstraint + + + + + + + City Map: + + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Add a new city map.</p></body></html> + + + + + + + :/icons/add.ico + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 1 + 0 + + + + + 102 + 102 + + + + true + + + + + 0 + 0 + 441 + 230 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + 0 + 0 + + + + false + + + false + + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + + + + + + + 0 + 0 + + + + + 82 + 82 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + true + + + Qt::AlignHCenter|Qt::AlignTop + + + + true + + + + 8 + 0 + 255 + 274 + + + + + 0 + 0 + + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustIgnored + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + 30 + 50 + + + + + 30 + 160 + + + + Qt::StrongFocus + + + 10 + + + 100 + + + 30 + + + Qt::Vertical + + + QSlider::NoTicks + + + 1 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 30 + 0 + + + + + 30 + 160 + + + + Qt::StrongFocus + + + 10 + + + 100 + + + 30 + + + Qt::Vertical + + + QSlider::NoTicks + + + 1 + + + + + + + + + + + + 30 + 50 + + + + + 30 + 160 + + + + Qt::StrongFocus + + + 10 + + + 100 + + + 30 + + + Qt::Vertical + + + QSlider::NoTicks + + + 1 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 30 + 0 + + + + + 30 + 160 + + + + Qt::StrongFocus + + + 10 + + + 100 + + + 30 + + + Qt::Vertical + + + QSlider::NoTicks + + + 1 + + + + + + + + + + + 0 + 0 + 829 + 22 + + + + + File + + + + + + Edit + + + + + + + + + + + + Tools + + + + + + + + + + + + Save + + + Ctrl+S + + + + + Undo + + + Ctrl+Z + + + + + Redo + + + Ctrl+Y + + + + + Resize + + + Ctrl+R + + + + + Generate... + + + + + Swap... + + + + + Clear Background Image + + + + + Clear Map Layout + + + + + Import Region Map Image Tiles... + + + + + Import City Map Image Tiles... + + + + + + diff --git a/include/config.h b/include/config.h index 7f63871f..624f87cd 100644 --- a/include/config.h +++ b/include/config.h @@ -4,6 +4,7 @@ #include #include #include +#include enum MapSortOrder { Group = 0, @@ -36,6 +37,7 @@ public: this->metatilesZoom = 30; this->showPlayerView = false; this->showCursorTile = true; + this->regionMapDimensions = QSize(32, 20); } void setRecentProject(QString project); void setRecentMap(QString map); @@ -46,6 +48,7 @@ public: void setMetatilesZoom(int zoom); void setShowPlayerView(bool enabled); void setShowCursorTile(bool enabled); + void setRegionMapDimensions(int width, int height); QString getRecentProject(); QString getRecentMap(); MapSortOrder getMapSortOrder(); @@ -55,6 +58,7 @@ public: int getMetatilesZoom(); bool getShowPlayerView(); bool getShowCursorTile(); + QSize getRegionMapDimensions(); protected: QString getConfigFilepath(); void parseConfigKeyValue(QString key, QString value); @@ -76,6 +80,7 @@ private: int metatilesZoom; bool showPlayerView; bool showCursorTile; + QSize regionMapDimensions; }; extern PorymapConfig porymapConfig; diff --git a/include/core/historyitem.h b/include/core/historyitem.h index d56392d2..92675055 100644 --- a/include/core/historyitem.h +++ b/include/core/historyitem.h @@ -12,4 +12,21 @@ public: ~HistoryItem(); }; +enum RegionMapEditorBox { + BackgroundImage = 1, + CityMapImage = 2, +}; + +class RegionMapHistoryItem { +public: + int which; + int mapWidth = 0; + int mapHeight = 0; + QVector tiles; + QString cityMap; + RegionMapHistoryItem(int type, QVector tiles, QString cityMap); + RegionMapHistoryItem(int type, QVector tiles, int width, int height); + ~RegionMapHistoryItem(); +}; + #endif // HISTORYITEM_H diff --git a/include/core/mapconnection.h b/include/core/mapconnection.h index a0fe4404..8823e7bc 100644 --- a/include/core/mapconnection.h +++ b/include/core/mapconnection.h @@ -2,6 +2,7 @@ #define MAPCONNECTION_H #include +#include class MapConnection { public: @@ -10,4 +11,12 @@ public: QString map_name; }; +inline bool operator==(const MapConnection &c1, const MapConnection &c2) { + return c1.map_name == c2.map_name; +} + +inline uint qHash(const MapConnection &key) { + return qHash(key.map_name); +} + #endif // MAPCONNECTION_H diff --git a/include/core/paletteparser.h b/include/core/paletteutil.h similarity index 74% rename from include/core/paletteparser.h rename to include/core/paletteutil.h index 336393cf..a067dea3 100644 --- a/include/core/paletteparser.h +++ b/include/core/paletteutil.h @@ -1,15 +1,16 @@ -#ifndef PALETTEPARSER_H -#define PALETTEPARSER_H +#ifndef PALETTEUTIL_H +#define PALETTEUTIL_H #include #include #include -class PaletteParser +class PaletteUtil { public: - PaletteParser(); + PaletteUtil(); QList parse(QString filepath, bool *error); + void writeJASC(QString filepath, QVector colors, int offset, int nColors); private: QList parsePal(QString filepath, bool *error); QList parseJASC(QString filepath, bool *error); @@ -20,4 +21,4 @@ private: int clampColorValue(int value); }; -#endif // PALETTEPARSER_H +#endif // PALETTEUTIL_H diff --git a/include/core/regionmap.h b/include/core/regionmap.h new file mode 100644 index 00000000..f2556c78 --- /dev/null +++ b/include/core/regionmap.h @@ -0,0 +1,113 @@ +#ifndef REGIONMAP_H +#define REGIONMAP_H + +#include "map.h" +#include "project.h" +#include "tilemaptileselector.h" +#include "history.h" +#include "historyitem.h" + +#include +#include +#include +#include +#include +#include +#include + +struct RegionMapEntry +{ + int x; + int y; + int width; + int height; + QString name; +}; + +class RegionMapSquare +{ +public: + int x = -1; + int y = -1; + uint8_t tile_img_id = 0x00; + uint8_t secid = 0x00; + bool has_map = false; + bool has_city_map = false; + bool duplicated = false; + QString map_name; + QString mapsec; + QString city_map_name; +}; + +class RegionMap : public QObject +{ + Q_OBJECT + +public: + RegionMap() = default; + + ~RegionMap() {}; + + Project *project = nullptr; + + QVector map_squares; + History history; + + const int padLeft = 1; + const int padRight = 3; + const int padTop = 2; + const int padBottom = 3; + + void init(Project*); + + void readBkgImgBin(); + void readLayout(); + + void save(); + void saveTileImages(); + void saveBkgImgBin(); + void saveLayout(); + void saveOptions(int id, QString sec, QString name, int x, int y); + + void resize(int width, int height); + void resetSquare(int index); + void clearLayout(); + void clearImage(); + void replaceSectionId(unsigned oldId, unsigned newId); + + int width(); + int height(); + QSize imgSize(); + unsigned getTileId(int x, int y); + int getMapSquareIndex(int x, int y); + QString pngPath(); + void setTemporaryPngPath(QString); + QString cityTilesPath(); + void setTemporaryCityTilesPath(QString); + + QVector getTiles(); + void setTiles(QVector tileIds); + +private: + int layout_width_; + int layout_height_; + int img_width_; + int img_height_; + + QString region_map_png_path; + QString region_map_bin_path; + QString region_map_entries_path; + QString region_map_layout_bin_path; + QString city_map_tiles_path; + + QMap sMapNamesMap; + QMap mapSecToMapEntry; + QVector sMapNames; + + int img_index_(int x, int y); + int layout_index_(int x, int y); + + QString fix_case(QString); +}; + +#endif // REGIONMAP_H diff --git a/include/editor.h b/include/editor.h index 4b7f71a0..3f022388 100644 --- a/include/editor.h +++ b/include/editor.h @@ -116,7 +116,7 @@ public: QString map_edit_mode; QString prev_edit_mode; - + int scale_exp = 0; double scale_base = sqrt(2); // adjust scale factor with this qreal collisionOpacity = 0.5; diff --git a/include/mainwindow.h b/include/mainwindow.h index 84ba958d..2915e169 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -15,6 +15,7 @@ #include "map.h" #include "editor.h" #include "tileseteditor.h" +#include "regionmapeditor.h" #include "filterchildrenproxymodel.h" #include "newmappopup.h" #include "newtilesetdialog.h" @@ -162,9 +163,12 @@ private slots: void on_tableWidget_CustomHeaderFields_cellChanged(int row, int column); void on_horizontalSlider_MetatileZoom_valueChanged(int value); + void on_actionRegion_Map_Editor_triggered(); + private: Ui::MainWindow *ui; TilesetEditor *tilesetEditor = nullptr; + RegionMapEditor *regionMapEditor = nullptr; FilterChildrenProxyModel *mapListProxyModel; NewMapPopup *newmapprompt = nullptr; QStandardItemModel *mapListModel; diff --git a/include/project.h b/include/project.h index 4762e9cd..bac7b372 100644 --- a/include/project.h +++ b/include/project.h @@ -30,7 +30,9 @@ public: QString layoutsLabel; QMap mapLayouts; QMap mapLayoutsMaster; - QStringList *regionMapSections = nullptr; + QMap *mapSecToMapHoverName; + QMap mapSectionNameToValue; + QMap mapSectionValueToName; QStringList *itemNames = nullptr; QStringList *flagNames = nullptr; QStringList *varNames = nullptr; diff --git a/include/ui/citymappixmapitem.h b/include/ui/citymappixmapitem.h new file mode 100644 index 00000000..a3c48faa --- /dev/null +++ b/include/ui/citymappixmapitem.h @@ -0,0 +1,52 @@ +#ifndef CITYMAPPIXMAPITEM_H +#define CITYMAPPIXMAPITEM_H + +#include "tilemaptileselector.h" + +#include +#include +#include + +class CityMapPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + CityMapPixmapItem(QString fname, TilemapTileSelector *tile_selector) { + this->file = fname; + this->tile_selector = tile_selector; + setAcceptHoverEvents(true); + init(); + } + TilemapTileSelector *tile_selector; + + QString file; + + QByteArray data; + + void init(); + void save(); + void create(QString); + virtual void paint(QGraphicsSceneMouseEvent*); + virtual void draw(); + int getIndexAt(int, int); + int width(); + int height(); + + QVector getTiles(); + void setTiles(QVector); + +private: + int width_; + int height_; + +signals: + void mouseEvent(QGraphicsSceneMouseEvent *, CityMapPixmapItem *); + void hoveredRegionMapTileChanged(int x, int y); + void hoveredRegionMapTileCleared(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +#endif // CITYMAPPIXMAPITEM_H diff --git a/include/ui/regionmapeditor.h b/include/ui/regionmapeditor.h new file mode 100644 index 00000000..a684095c --- /dev/null +++ b/include/ui/regionmapeditor.h @@ -0,0 +1,118 @@ +#ifndef REGIONMAPEDITOR_H +#define REGIONMAPEDITOR_H + +#include "regionmappixmapitem.h" +#include "citymappixmapitem.h" +#include "regionmaplayoutpixmapitem.h" +#include "regionmap.h" +#include "history.h" +#include "historyitem.h" + +#include +#include +#include +#include + +namespace Ui { +class RegionMapEditor; +} + +class RegionMapEditor : public QMainWindow +{ + Q_OBJECT + +public: + explicit RegionMapEditor(QWidget *parent = 0, Project *pro = nullptr); + ~RegionMapEditor(); + + RegionMap *region_map; + + void loadRegionMapData(); + void loadCityMaps(); + void setCurrentSquareOptions(); + + void onRegionMapTileSelectorSelectedTileChanged(unsigned id); + void onCityMapTileSelectorSelectedTileChanged(unsigned id); + void onRegionMapTileSelectorHoveredTileChanged(unsigned id); + void onRegionMapTileSelectorHoveredTileCleared(); + + void onRegionMapLayoutSelectedTileChanged(int index); + void onRegionMapLayoutHoveredTileChanged(int index); + void onRegionMapLayoutHoveredTileCleared(); + + void undo(); + void redo(); + + void resize(int width, int height); + +private: + Ui::RegionMapEditor *ui; + Project *project; + + History history; + + int currIndex; + unsigned selectedCityTile; + unsigned selectedImageTile; + + bool hasUnsavedChanges = false; + bool cityMapFirstDraw = true; + bool regionMapFirstDraw = true; + + double scaleUpFactor = 2.0; + double initialScale = 30.0; + + QGraphicsScene *scene_region_map_image = nullptr; + QGraphicsScene *scene_city_map_image = nullptr; + QGraphicsScene *scene_region_map_layout = nullptr; + QGraphicsScene *scene_region_map_tiles = nullptr; + QGraphicsScene *scene_city_map_tiles = nullptr; + + TilemapTileSelector *mapsquare_selector_item = nullptr; + TilemapTileSelector *city_map_selector_item = nullptr; + + RegionMapLayoutPixmapItem *region_map_layout_item = nullptr; + RegionMapPixmapItem *region_map_item = nullptr; + CityMapPixmapItem *city_map_item = nullptr; + + void displayRegionMap(); + void displayRegionMapImage(); + void displayRegionMapLayout(); + void displayRegionMapLayoutOptions(); + void updateRegionMapLayoutOptions(int index); + void displayRegionMapTileSelector(); + void displayCityMapTileSelector(); + void displayCityMap(QString name); + void importTileImage(bool city = false);//QString path);// what is this path tho? + + bool createCityMap(QString name); + + void closeEvent(QCloseEvent* event); + +private slots: + void on_action_RegionMap_Save_triggered(); + void on_action_RegionMap_Undo_triggered(); + void on_action_RegionMap_Redo_triggered(); + void on_action_RegionMap_Resize_triggered(); + void on_action_RegionMap_ClearImage_triggered(); + void on_action_RegionMap_ClearLayout_triggered(); + void on_action_Swap_triggered(); + void on_action_Import_RegionMap_ImageTiles_triggered(); + void on_action_Import_CityMap_ImageTiles_triggered(); + void on_tabWidget_Region_Map_currentChanged(int); + void on_pushButton_RM_Options_delete_clicked(); + void on_comboBox_RM_ConnectedMap_activated(const QString &text); + void on_pushButton_CityMap_add_clicked(); + void on_verticalSlider_Zoom_Map_Image_valueChanged(int); + void on_verticalSlider_Zoom_Image_Tiles_valueChanged(int); + void on_verticalSlider_Zoom_City_Map_valueChanged(int); + void on_verticalSlider_Zoom_City_Tiles_valueChanged(int); + void on_comboBox_CityMap_picker_currentTextChanged(const QString &text); + void on_lineEdit_RM_MapName_textEdited(const QString &text); + void onHoveredRegionMapTileChanged(int x, int y); + void onHoveredRegionMapTileCleared(); + void mouseEvent_region_map(QGraphicsSceneMouseEvent *event, RegionMapPixmapItem *item); + void mouseEvent_city_map(QGraphicsSceneMouseEvent *event, CityMapPixmapItem *item); +}; + +#endif // REGIONMAPEDITOR_H diff --git a/include/ui/regionmaplayoutpixmapitem.h b/include/ui/regionmaplayoutpixmapitem.h new file mode 100644 index 00000000..f3d2a258 --- /dev/null +++ b/include/ui/regionmaplayoutpixmapitem.h @@ -0,0 +1,41 @@ +#ifndef REGIONMAPLAYOUTPIXMAPITEM_H +#define REGIONMAPLAYOUTPIXMAPITEM_H + +#include "tilemaptileselector.h" +#include "regionmap.h" + +class RegionMapLayoutPixmapItem : public SelectablePixmapItem { + Q_OBJECT +public: + RegionMapLayoutPixmapItem(RegionMap *rmap, TilemapTileSelector *ts) : SelectablePixmapItem(8, 8, 1, 1) { + this->region_map = rmap; + this->tile_selector = ts; + setAcceptHoverEvents(true); + } + RegionMap *region_map; + TilemapTileSelector *tile_selector; + int selectedTile; + int highlightedTile; + void draw(); + void select(int, int); + void select(int); + void highlight(int, int, int); + +private: + void updateSelectedTile(); + +signals: + void mouseEvent(QGraphicsSceneMouseEvent *, RegionMapLayoutPixmapItem *); + void hoveredTileChanged(int); + void hoveredTileCleared(); + void selectedTileChanged(int); + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +#endif // REGIONMAPLAYOUTPIXMAPITEM_H diff --git a/include/ui/regionmappixmapitem.h b/include/ui/regionmappixmapitem.h new file mode 100644 index 00000000..0fc11b8c --- /dev/null +++ b/include/ui/regionmappixmapitem.h @@ -0,0 +1,36 @@ +#ifndef REGIONMAPPIXMAPITEM_H +#define REGIONMAPPIXMAPITEM_H + +#include "regionmap.h" +#include "tilemaptileselector.h" +#include + +class RegionMapPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + RegionMapPixmapItem(RegionMap *rmap, TilemapTileSelector *tile_selector) { + this->region_map = rmap; + this->tile_selector = tile_selector; + setAcceptHoverEvents(true); + } + RegionMap *region_map; + TilemapTileSelector *tile_selector; + + virtual void paint(QGraphicsSceneMouseEvent*); + virtual void select(QGraphicsSceneMouseEvent*); + virtual void draw(); + +signals: + void mouseEvent(QGraphicsSceneMouseEvent *, RegionMapPixmapItem *); + void hoveredRegionMapTileChanged(int x, int y); + void hoveredRegionMapTileCleared(); + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +#endif // REGIONMAPPIXMAPITEM_H diff --git a/include/ui/tilemaptileselector.h b/include/ui/tilemaptileselector.h new file mode 100644 index 00000000..293ad3af --- /dev/null +++ b/include/ui/tilemaptileselector.h @@ -0,0 +1,48 @@ +#ifndef TILEMAPTILESELECTOR_H +#define TILEMAPTILESELECTOR_H + +#include "selectablepixmapitem.h" + +class TilemapTileSelector: public SelectablePixmapItem { + Q_OBJECT +public: + TilemapTileSelector(QPixmap pixmap_): SelectablePixmapItem(8, 8, 1, 1) { + this->tilemap = pixmap_; + this->setPixmap(this->tilemap); + this->numTilesWide = tilemap.width() / 8; + this->selectedTile = 0x00; + setAcceptHoverEvents(true); + } + void draw(); + void select(unsigned tileId); + unsigned getSelectedTile(); + + int pixelWidth; + int pixelHeight; + + unsigned selectedTile = 0; + + QPixmap tilemap; + QImage tileImg(unsigned tileId); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + +private: + int numTilesWide; + int numTiles; + void updateSelectedTile(); + unsigned getTileId(int x, int y); + QPoint getTileIdCoords(unsigned); + +signals: + void hoveredTileChanged(unsigned); + void hoveredTileCleared(); + void selectedTileChanged(unsigned); +}; + +#endif // TILEMAPTILESELECTOR_H diff --git a/porymap.pro b/porymap.pro index eaf6da10..c759912c 100644 --- a/porymap.pro +++ b/porymap.pro @@ -24,15 +24,17 @@ SOURCES += src/core/block.cpp \ src/core/maplayout.cpp \ src/core/metatile.cpp \ src/core/metatileparser.cpp \ - src/core/paletteparser.cpp \ + src/core/paletteutil.cpp \ src/core/parseutil.cpp \ src/core/tile.cpp \ src/core/tileset.cpp \ + src/core/regionmap.cpp \ src/ui/aboutporymap.cpp \ src/ui/bordermetatilespixmapitem.cpp \ src/ui/collisionpixmapitem.cpp \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ + src/ui/regionmaplayoutpixmapitem.cpp \ src/ui/cursortilerect.cpp \ src/ui/customattributestable.cpp \ src/ui/eventpropertiesframe.cpp \ @@ -40,6 +42,8 @@ SOURCES += src/core/block.cpp \ src/ui/graphicsview.cpp \ src/ui/imageproviders.cpp \ src/ui/mappixmapitem.cpp \ + src/ui/regionmappixmapitem.cpp \ + src/ui/citymappixmapitem.cpp \ src/ui/mapsceneeventfilter.cpp \ src/ui/metatilelayersitem.cpp \ src/ui/metatileselector.cpp \ @@ -53,6 +57,8 @@ SOURCES += src/core/block.cpp \ src/ui/tileseteditor.cpp \ src/ui/tileseteditormetatileselector.cpp \ src/ui/tileseteditortileselector.cpp \ + src/ui/tilemaptileselector.cpp \ + src/ui/regionmapeditor.cpp \ src/ui/newmappopup.cpp \ src/config.cpp \ src/editor.cpp \ @@ -75,15 +81,17 @@ HEADERS += include/core/block.h \ include/core/maplayout.h \ include/core/metatile.h \ include/core/metatileparser.h \ - include/core/paletteparser.h \ + include/core/paletteutil.h \ include/core/parseutil.h \ include/core/tile.h \ include/core/tileset.h \ - include/ui/aboutporymap.h \ + include/core/regionmap.h \ + include/ui/aboutporymap.h \ include/ui/bordermetatilespixmapitem.h \ include/ui/collisionpixmapitem.h \ include/ui/connectionpixmapitem.h \ include/ui/currentselectedmetatilespixmapitem.h \ + include/ui/regionmaplayoutpixmapitem.h \ include/ui/cursortilerect.h \ include/ui/customattributestable.h \ include/ui/eventpropertiesframe.h \ @@ -91,6 +99,8 @@ HEADERS += include/core/block.h \ include/ui/graphicsview.h \ include/ui/imageproviders.h \ include/ui/mappixmapitem.h \ + include/ui/regionmappixmapitem.h \ + include/ui/citymappixmapitem.h \ include/ui/mapsceneeventfilter.h \ include/ui/metatilelayersitem.h \ include/ui/metatileselector.h \ @@ -104,6 +114,8 @@ HEADERS += include/core/block.h \ include/ui/tileseteditor.h \ include/ui/tileseteditormetatileselector.h \ include/ui/tileseteditortileselector.h \ + include/ui/tilemaptileselector.h \ + include/ui/regionmapeditor.h \ include/ui/newmappopup.h \ include/config.h \ include/editor.h \ @@ -117,6 +129,7 @@ FORMS += forms/mainwindow.ui \ forms/eventpropertiesframe.ui \ forms/tileseteditor.ui \ forms/paletteeditor.ui \ + forms/regionmapeditor.ui \ forms/newmappopup.ui \ forms/aboutporymap.ui \ forms/newtilesetdialog.ui diff --git a/src/config.cpp b/src/config.cpp index aa21a1af..e689b91e 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -156,6 +156,17 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { if (!ok) { logWarn(QString("Invalid config value for show_cursor_tile: '%1'. Must be 0 or 1.").arg(value)); } + } else if (key == "region_map_dimensions") { + bool ok1, ok2; + QStringList dims = value.split("x"); + int w = dims[0].toInt(&ok1); + int h = dims[1].toInt(&ok2); + if (!ok1 || !ok2) { + logWarn("Cannot parse region map dimensions. Using default values instead."); + this->regionMapDimensions = QSize(32, 20); + } else { + this->regionMapDimensions = QSize(w, h); + } } else { logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); } @@ -176,6 +187,8 @@ QMap PorymapConfig::getKeyValueMap() { map.insert("metatiles_zoom", QString("%1").arg(this->metatilesZoom)); map.insert("show_player_view", this->showPlayerView ? "1" : "0"); map.insert("show_cursor_tile", this->showCursorTile ? "1" : "0"); + map.insert("region_map_dimensions", QString("%1x%2").arg(this->regionMapDimensions.width()) + .arg(this->regionMapDimensions.height())); return map; } @@ -246,6 +259,10 @@ void PorymapConfig::setShowCursorTile(bool enabled) { this->save(); } +void PorymapConfig::setRegionMapDimensions(int width, int height) { + this->regionMapDimensions = QSize(width, height); +} + QString PorymapConfig::getRecentProject() { return this->recentProject; } @@ -290,6 +307,10 @@ bool PorymapConfig::getShowCursorTile() { return this->showCursorTile; } +QSize PorymapConfig::getRegionMapDimensions() { + return this->regionMapDimensions; +} + const QMap baseGameVersionMap = { {BaseGameVersion::pokeruby, "pokeruby"}, {BaseGameVersion::pokeemerald, "pokeemerald"}, diff --git a/src/core/historyitem.cpp b/src/core/historyitem.cpp index ded89353..1b0f3888 100644 --- a/src/core/historyitem.cpp +++ b/src/core/historyitem.cpp @@ -9,3 +9,18 @@ HistoryItem::HistoryItem(Blockdata *metatiles, int layoutWidth, int layoutHeight HistoryItem::~HistoryItem() { if (this->metatiles) delete this->metatiles; } + +RegionMapHistoryItem::RegionMapHistoryItem(int which, QVector tiles, QString cityMap) { + this->which = which; + this->tiles = tiles; + this->cityMap = cityMap; +} + +RegionMapHistoryItem::RegionMapHistoryItem(int which, QVector tiles, int width, int height) { + this->which = which; + this->tiles = tiles; + this->mapWidth = width; + this->mapHeight = height; +} + +RegionMapHistoryItem::~RegionMapHistoryItem() {} diff --git a/src/core/map.cpp b/src/core/map.cpp index 78a7f536..b56be61f 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -341,7 +341,6 @@ void Map::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_ } } - void Map::undo() { HistoryItem *commit = metatileHistory.back(); if (!commit) diff --git a/src/core/paletteparser.cpp b/src/core/paletteutil.cpp similarity index 85% rename from src/core/paletteparser.cpp rename to src/core/paletteutil.cpp index 5a024628..3a6f7ff7 100644 --- a/src/core/paletteparser.cpp +++ b/src/core/paletteutil.cpp @@ -1,14 +1,14 @@ -#include "paletteparser.h" +#include "paletteutil.h" #include "log.h" #include #include -PaletteParser::PaletteParser() +PaletteUtil::PaletteUtil() { } -QList PaletteParser::parse(QString filepath, bool *error) { +QList PaletteUtil::parse(QString filepath, bool *error) { QFileInfo info(filepath); QString extension = info.completeSuffix(); if (extension.isNull()) { @@ -34,7 +34,7 @@ QList PaletteParser::parse(QString filepath, bool *error) { return QList(); } -QList PaletteParser::parsePal(QString filepath, bool *error) { +QList PaletteUtil::parsePal(QString filepath, bool *error) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { *error = true; @@ -53,7 +53,7 @@ QList PaletteParser::parsePal(QString filepath, bool *error) { } } -QList PaletteParser::parseJASC(QString filepath, bool *error) { +QList PaletteUtil::parseJASC(QString filepath, bool *error) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { *error = true; @@ -120,7 +120,7 @@ QList PaletteParser::parseJASC(QString filepath, bool *error) { return palette; } -QList PaletteParser::parseAdvanceMapPal(QString filepath, bool *error) { +QList PaletteUtil::parseAdvanceMapPal(QString filepath, bool *error) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { *error = true; @@ -152,7 +152,7 @@ QList PaletteParser::parseAdvanceMapPal(QString filepath, bool *error) { return palette; } -QList PaletteParser::parseAdobeColorTable(QString filepath, bool *error) { +QList PaletteUtil::parseAdobeColorTable(QString filepath, bool *error) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { *error = true; @@ -184,7 +184,7 @@ QList PaletteParser::parseAdobeColorTable(QString filepath, bool *error) { return palette; } -QList PaletteParser::parseTileLayerPro(QString filepath, bool *error) { +QList PaletteUtil::parseTileLayerPro(QString filepath, bool *error) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { *error = true; @@ -222,7 +222,7 @@ QList PaletteParser::parseTileLayerPro(QString filepath, bool *error) { return palette; } -QList PaletteParser::parseAdvancePaletteEditor(QString filepath, bool *error) { +QList PaletteUtil::parseAdvancePaletteEditor(QString filepath, bool *error) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { *error = true; @@ -267,7 +267,35 @@ QList PaletteParser::parseAdvancePaletteEditor(QString filepath, bool *err return palette; } -int PaletteParser::clampColorValue(int value) { +void PaletteUtil::writeJASC(QString filepath, QVector palette, int offset, int nColors) { + if (!nColors) { + logWarn(QString("Cannot save a palette with no colors.")); + return; + } + if (offset > palette.size() || offset + nColors > palette.size()) { + logWarn("Palette offset out of range for color table."); + return; + } + + QString text = "JASC-PAL\r\n0100\r\n"; + text += QString::number(nColors) + "\r\n"; + + for (int i = offset; i < offset + nColors; i++) { + QRgb color = palette.at(i); + text += QString::number(qRed(color)) + " " + + QString::number(qGreen(color)) + " " + + QString::number(qBlue(color)) + "\r\n"; + } + + QFile file(filepath); + if (file.open(QIODevice::WriteOnly)) { + file.write(text.toUtf8()); + } else { + logWarn(QString("Could not write to file '%1': ").arg(filepath) + file.errorString()); + } +} + +int PaletteUtil::clampColorValue(int value) { if (value < 0) { value = 0; } diff --git a/src/core/regionmap.cpp b/src/core/regionmap.cpp new file mode 100644 index 00000000..ec052e3b --- /dev/null +++ b/src/core/regionmap.cpp @@ -0,0 +1,361 @@ +#include "regionmap.h" +#include "paletteutil.h" +#include "log.h" +#include "config.h" + +#include +#include +#include +#include +#include +#include + +void RegionMap::init(Project *pro) { + QString path = pro->root; + this->project = pro; + + QSize dimensions = porymapConfig.getRegionMapDimensions(); + img_width_ = dimensions.width(); + img_height_ = dimensions.height(); + + layout_width_ = img_width_ - this->padLeft - this->padRight; + layout_height_ = img_height_ - this->padTop - this->padBottom; + + region_map_bin_path = path + "/graphics/pokenav/region_map_map.bin"; + region_map_png_path = path + "/graphics/pokenav/region_map.png"; + region_map_entries_path = path + "/src/data/region_map/region_map_entries.h"; + region_map_layout_bin_path = path + "/graphics/pokenav/region_map_section_layout.bin"; + city_map_tiles_path = path + "/graphics/pokenav/zoom_tiles.png"; + + readBkgImgBin(); + readLayout(); +} + +void RegionMap::save() { + logInfo("Saving region map data."); + saveTileImages(); + saveBkgImgBin(); + saveLayout(); + porymapConfig.setRegionMapDimensions(this->img_width_, this->img_height_); +} + +void RegionMap::saveTileImages() { + QFile backgroundTileFile(pngPath()); + if (backgroundTileFile.open(QIODevice::ReadOnly)) { + QByteArray imageData = backgroundTileFile.readAll(); + QImage pngImage = QImage::fromData(imageData); + this->region_map_png_path = project->root + "/graphics/pokenav/region_map.png"; + pngImage.save(pngPath()); + + PaletteUtil parser; + parser.writeJASC(project->root + "/graphics/pokenav/region_map.pal", pngImage.colorTable(), 0x70, 0x20); + } + QFile cityTileFile(cityTilesPath()); + if (cityTileFile.open(QIODevice::ReadOnly)) { + QByteArray imageData = cityTileFile.readAll(); + QImage cityTilesImage = QImage::fromData(imageData); + this->city_map_tiles_path = project->root + "/graphics/pokenav/zoom_tiles.png"; + cityTilesImage.save(cityTilesPath()); + } +} + +void RegionMap::readBkgImgBin() { + QFile binFile(region_map_bin_path); + if (!binFile.open(QIODevice::ReadOnly)) return; + + QByteArray mapBinData = binFile.readAll(); + binFile.close(); + + if (mapBinData.size() < img_height_ * img_width_) { + logError(QString("The region map tilemap at %1 is too small.").arg(region_map_bin_path)); + return; + } + for (int m = 0; m < img_height_; m++) { + for (int n = 0; n < img_width_; n++) { + RegionMapSquare square; + square.tile_img_id = mapBinData.at(n + m * img_width_ * 2); + map_squares.append(square); + } + } +} + +void RegionMap::saveBkgImgBin() { + QByteArray data(pow(img_width_ * 2, 2),0); + + for (int m = 0; m < img_height_; m++) { + for (int n = 0; n < img_width_; n++) { + data[n + m * img_width_ * 2] = map_squares[n + m * img_width_].tile_img_id; + } + } + + QFile file(region_map_bin_path); + if (!file.open(QIODevice::WriteOnly)) return; + file.write(data); + file.close(); +} + +void RegionMap::readLayout() { + QFile file(region_map_entries_path); + if (!file.open(QIODevice::ReadOnly)) return; + + QString line; + + QMap *qmap = new QMap; + + QTextStream in(&file); + in.setCodec("UTF-8"); + while (!in.atEnd()) { + line = in.readLine(); + if (line.startsWith("static const u8")) { + QRegularExpression reBefore("sMapName_(.*)\\["); + QRegularExpression reAfter("_\\(\"(.*)\""); + QString const_name = reBefore.match(line).captured(1); + QString full_name = reAfter.match(line).captured(1); + sMapNames.append(const_name); + sMapNamesMap.insert(const_name, full_name); + } else if (line.contains("MAPSEC")) { + QRegularExpression reBefore("\\[(.*)\\]"); + QRegularExpression reAfter("{(.*)}"); + QStringList entry = reAfter.match(line).captured(1).remove(" ").split(","); + QString mapsec = reBefore.match(line).captured(1); + QString insertion = entry[4].remove("sMapName_"); + qmap->insert(mapsec, sMapNamesMap.value(insertion)); + mapSecToMapEntry[mapsec] = { + // x y width height name + entry[0].toInt(), entry[1].toInt(), entry[2].toInt(), entry[3].toInt(), insertion + }; + } + } + file.close(); + + project->mapSecToMapHoverName = qmap; + + QFile binFile(region_map_layout_bin_path); + if (!binFile.open(QIODevice::ReadOnly)) return; + QByteArray mapBinData = binFile.readAll(); + binFile.close(); + + for (int y = 0; y < layout_height_; y++) { + for (int x = 0; x < layout_width_; x++) { + int i = img_index_(x,y); + uint8_t id = static_cast(mapBinData.at(layout_index_(x,y))); + map_squares[i].secid = id; + QString secname = project->mapSectionValueToName.value(id); + if (secname != "MAPSEC_NONE") map_squares[i].has_map = true; + map_squares[i].mapsec = secname; + map_squares[i].map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name); + map_squares[i].x = x; + map_squares[i].y = y; + } + } +} + +void RegionMap::saveLayout() { + QString entries_text; + QString layout_text; + + entries_text += "#ifndef GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n"; + entries_text += "#define GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n\n"; + + for (auto sName : sMapNames) { + entries_text += "static const u8 sMapName_" + sName + "[] = _(\"" + sMapNamesMap.value(sName) + "\");\n"; + } + + entries_text += "\nconst struct RegionMapLocation gRegionMapEntries[] = {\n"; + + for (auto sec : project->mapSectionNameToValue.keys()) { + if (!mapSecToMapEntry.contains(sec)) continue; + struct RegionMapEntry entry = mapSecToMapEntry.value(sec); + entries_text += " [" + sec + "] = {" + QString::number(entry.x) + ", " + QString::number(entry.y) + ", " + + QString::number(entry.width) + ", " + QString::number(entry.height) + ", sMapName_" + entry.name + "},\n"; + } + entries_text += "};\n\n#endif // GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n"; + + project->saveTextFile(region_map_entries_path, entries_text); + + QByteArray data; + for (int m = 0; m < layout_height_; m++) { + for (int n = 0; n < layout_width_; n++) { + int i = img_index_(n,m); + data.append(map_squares[i].secid); + } + } + QFile bfile(region_map_layout_bin_path); + if (!bfile.open(QIODevice::WriteOnly)) return; + bfile.write(data); + bfile.close(); +} + +void RegionMap::saveOptions(int id, QString sec, QString name, int x, int y) { + resetSquare(id); + int index = getMapSquareIndex(x + this->padLeft, y + this->padTop); + if (!sec.isEmpty()) { + // Validate the input section name. + if (!project->mapSectionNameToValue.contains(sec)) { + sec = "MAPSEC_NONE"; + name = QString(); + } + this->map_squares[index].has_map = sec == "MAPSEC_NONE" ? false : true; + this->map_squares[index].secid = static_cast(project->mapSectionNameToValue.value(sec)); + this->map_squares[index].mapsec = sec; + if (!name.isEmpty()) { + this->map_squares[index].map_name = name; + this->project->mapSecToMapHoverName->insert(sec, name); + QString sName = fix_case(sec); + sMapNamesMap.insert(sName, name); + if (!mapSecToMapEntry.keys().contains(sec)) { + sMapNames.append(sName); + struct RegionMapEntry entry = {x, y, 1, 1, sName}; + mapSecToMapEntry.insert(sec, entry); + } + } + this->map_squares[index].x = x; + this->map_squares[index].y = y; + this->map_squares[index].duplicated = false; + } +} + +void RegionMap::resetSquare(int index) { + this->map_squares[index].mapsec = "MAPSEC_NONE"; + this->map_squares[index].map_name = QString(); + this->map_squares[index].has_map = false; + this->map_squares[index].secid = static_cast(project->mapSectionNameToValue.value("MAPSEC_NONE")); + this->map_squares[index].has_city_map = false; + this->map_squares[index].city_map_name = QString(); + this->map_squares[index].duplicated = false; +} + +void RegionMap::clearLayout() { + for (int i = 0; i < map_squares.size(); i++) + resetSquare(i); +} + +void RegionMap::clearImage() { + for (int i = 0; i < map_squares.size(); i++) + this->map_squares[i].tile_img_id = 0x00; +} + +void RegionMap::replaceSectionId(unsigned oldId, unsigned newId) { + for (auto &square : map_squares) { + if (square.secid == oldId) { + square.has_map = false; + square.secid = newId; + QString secname = project->mapSectionValueToName.value(newId); + if (secname != "MAPSEC_NONE") square.has_map = true; + square.mapsec = secname; + square.map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name); + } + } +} + +void RegionMap::resize(int newWidth, int newHeight) { + QVector new_squares; + + for (int y = 0; y < newHeight; y++) { + for (int x = 0; x < newWidth; x++) { + RegionMapSquare square; + if (x < img_width_ && y < img_height_) { + square = map_squares[getMapSquareIndex(x, y)]; + } else if (x < newWidth - this->padRight && y < newHeight - this->padBottom) { + square.tile_img_id = 0; + square.x = x; + square.y = y; + square.mapsec = "MAPSEC_NONE"; + } else { + square.tile_img_id = 0; + } + new_squares.append(square); + } + } + this->map_squares = new_squares; + this->img_width_ = newWidth; + this->img_height_ = newHeight; + this->layout_width_ = newWidth - this->padLeft - this->padRight; + this->layout_height_ = newHeight - this->padTop - this->padBottom; +} + +QVector RegionMap::getTiles() { + QVector tileIds; + for (auto square : map_squares) { + tileIds.append(square.tile_img_id); + } + return tileIds; +} + +void RegionMap::setTiles(QVector tileIds) { + if (tileIds.size() != map_squares.size()) return; + + int i = 0; + for (uint8_t tileId : tileIds) { + map_squares[i].tile_img_id = tileId; + i++; + } +} + +// Layout coords to image index. +int RegionMap::img_index_(int x, int y) { + return ((x + this->padLeft) + (y + this->padTop) * img_width_); +} + +// Layout coords to layout index. +int RegionMap::layout_index_(int x, int y) { + return (x + y * layout_width_); +} + +int RegionMap::width() { + return this->img_width_; +} + +int RegionMap::height() { + return this->img_height_; +} + +QSize RegionMap::imgSize() { + return QSize(img_width_ * 8, img_height_ * 8); +} + +unsigned RegionMap::getTileId(int x, int y) { + return map_squares.at(x + y * img_width_).tile_img_id; +} + +QString RegionMap::pngPath() { + return this->region_map_png_path; +} + +void RegionMap::setTemporaryPngPath(QString path) { + this->region_map_png_path = path; +} + +QString RegionMap::cityTilesPath() { + return this->city_map_tiles_path; +} + +void RegionMap::setTemporaryCityTilesPath(QString path) { + this->city_map_tiles_path = path; +} + +// From x, y of image. +int RegionMap::getMapSquareIndex(int x, int y) { + int index = (x + y * img_width_); + return index < map_squares.length() ? index : 0; +} + +// For turning a MAPSEC_NAME into a unique identifier sMapName-style variable. +// CAPS_WITH_UNDERSCORE to CamelCase +QString RegionMap::fix_case(QString caps) { + bool big = true; + QString camel; + + for (auto ch : caps.remove(QRegularExpression("({.*})")).remove("MAPSEC")) { + if (ch == '_' || ch == ' ') { + big = true; + continue; + } + if (big) { + camel += ch.toUpper(); + big = false; + } + else camel += ch.toLower(); + } + return camel; +} diff --git a/src/editor.cpp b/src/editor.cpp index abcec186..14dc0dcf 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include static bool selectingEvent = false; @@ -1283,6 +1284,5 @@ void Editor::objectsView_onMousePress(QMouseEvent *event) { selected_events->append(first); updateSelectedEvents(); } - selectingEvent = false; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e1b54e6f..87fa3f52 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -465,7 +465,7 @@ void MainWindow::displayMapProperties() { ui->comboBox_Song->addItems(songs); ui->comboBox_Song->setCurrentText(map->song); - ui->comboBox_Location->addItems(*project->regionMapSections); + ui->comboBox_Location->addItems(project->mapSectionValueToName.values()); ui->comboBox_Location->setCurrentText(map->location); QMap tilesets = project->getTilesetLabels(); @@ -658,8 +658,8 @@ void MainWindow::sortMapList() { case MapSortOrder::Area: { QMap mapsecToGroupNum; - for (int i = 0; i < project->regionMapSections->length(); i++) { - QString mapsec_name = project->regionMapSections->value(i); + for (int i = 0; i < project->mapSectionNameToValue.size(); i++) { + QString mapsec_name = project->mapSectionValueToName.value(i); QStandardItem *mapsec = new QStandardItem; mapsec->setText(mapsec_name); mapsec->setIcon(folderIcon); @@ -2123,6 +2123,24 @@ void MainWindow::on_horizontalSlider_MetatileZoom_valueChanged(int value) { currentMetatilesSelectionChanged(); } +void MainWindow::on_actionRegion_Map_Editor_triggered() { + if (!this->regionMapEditor) { + this->regionMapEditor = new RegionMapEditor(this, this->editor->project); + this->regionMapEditor->loadRegionMapData(); + this->regionMapEditor->loadCityMaps(); + connect(this->regionMapEditor, &QObject::destroyed, [=](QObject *) { this->regionMapEditor = nullptr; }); + this->regionMapEditor->setAttribute(Qt::WA_DeleteOnClose); + } + + if (!this->regionMapEditor->isVisible()) { + this->regionMapEditor->show(); + } else if (this->regionMapEditor->isMinimized()) { + this->regionMapEditor->showNormal(); + } else { + this->regionMapEditor->activateWindow(); + } +} + void MainWindow::closeEvent(QCloseEvent *event) { porymapConfig.setGeometry( this->saveGeometry(), diff --git a/src/project.cpp b/src/project.cpp index 5629d22b..c87508db 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -4,6 +4,7 @@ #include "historyitem.h" #include "log.h" #include "parseutil.h" +#include "paletteutil.h" #include "tile.h" #include "tileset.h" @@ -30,7 +31,6 @@ Project::Project() groupNames = new QStringList; map_groups = new QMap; mapNames = new QStringList; - regionMapSections = new QStringList; itemNames = new QStringList; flagNames = new QStringList; varNames = new QStringList; @@ -720,20 +720,10 @@ void Project::saveTilesetTilesImage(Tileset *tileset) { } void Project::saveTilesetPalettes(Tileset *tileset, bool primary) { + PaletteUtil parser; for (int i = 0; i < Project::getNumPalettesTotal(); 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); + parser.writeJASC(filepath, tileset->palettes->at(i).toVector(), 0, 16); } } @@ -1507,8 +1497,18 @@ void Project::readTilesetProperties() { void Project::readRegionMapSections() { QString filepath = root + "/include/constants/region_map_sections.h"; - QStringList prefixes = (QStringList() << "MAPSEC_"); - readCDefinesSorted(filepath, prefixes, regionMapSections); + this->mapSectionNameToValue.clear(); + this->mapSectionValueToName.clear(); + QString text = readTextFile(filepath); + if (!text.isNull()) { + QStringList prefixes = (QStringList() << "MAPSEC_"); + this->mapSectionNameToValue = readCDefines(text, prefixes); + for (QString defineName : this->mapSectionNameToValue.keys()) { + this->mapSectionValueToName.insert(this->mapSectionNameToValue[defineName], defineName); + } + } else { + logError(QString("Failed to read C defines file: '%1'").arg(filepath)); + } } void Project::readItemNames() { diff --git a/src/ui/citymappixmapitem.cpp b/src/ui/citymappixmapitem.cpp new file mode 100644 index 00000000..5f270eb4 --- /dev/null +++ b/src/ui/citymappixmapitem.cpp @@ -0,0 +1,111 @@ +#include "citymappixmapitem.h" +#include "imageproviders.h" +#include "config.h" +#include "log.h" + +#include +#include +#include + +void CityMapPixmapItem::init() { + width_ = 10; + height_ = 10; + + QFile binFile(file); + if (!binFile.open(QIODevice::ReadOnly)) return; + + data = binFile.readAll(); + if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokeruby) { + for (int i = 0; i < data.size(); i++) + data[i] = data[i] ^ 0x80; + } + + binFile.close(); +} + +void CityMapPixmapItem::draw() { + QImage image(width_ * 8, height_ * 8, QImage::Format_RGBA8888); + + QPainter painter(&image); + for (int i = 0; i < data.size() / 2; i++) { + QImage img = this->tile_selector->tileImg(data[i * 2]);// need to skip every other tile + int x = i % width_; + int y = i / width_; + QPoint pos = QPoint(x * 8, y * 8); + painter.drawImage(pos, img); + } + painter.end(); + + this->setPixmap(QPixmap::fromImage(image)); +} + +void CityMapPixmapItem::save() { + QFile binFile(file); + if (!binFile.open(QIODevice::WriteOnly)) { + logError(QString("Cannot save city map tilemap to %1.").arg(file)); + return; + } + if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokeruby) { + for (int i = 0; i < data.size(); i++) + data[i] = data[i] ^ 0x80; + } + binFile.write(data); + binFile.close(); +} + +void CityMapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 8; + int y = static_cast(pos.y()) / 8; + int index = getIndexAt(x, y); + data[index] = static_cast(this->tile_selector->selectedTile); + + draw(); +} + +void CityMapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} + +void CityMapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 8; + int y = static_cast(pos.y()) / 8; + if (x < width_ && x >= 0 + && y < height_ && y >= 0) { + emit this->hoveredRegionMapTileChanged(x, y); + emit mouseEvent(event, this); + } +} + +void CityMapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} + +QVector CityMapPixmapItem::getTiles() { + QVector tiles; + for (auto tile : data) { + tiles.append(tile); + } + return tiles; +} + +void CityMapPixmapItem::setTiles(QVector tiles) { + QByteArray newData; + for (auto tile : tiles) { + newData.append(tile); + } + this->data = newData; +} + +int CityMapPixmapItem::getIndexAt(int x, int y) { + return 2 * (x + y * this->width_); +} + +int CityMapPixmapItem::width() { + return this->width_; +} + +int CityMapPixmapItem::height() { + return this->height_; +} diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index db2d9134..c07691e9 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -70,7 +70,7 @@ void NewMapPopup::setDefaultValues(int groupNum, QString mapSec) { } ui->comboBox_NewMap_Type->addItems(*project->mapTypes); - ui->comboBox_NewMap_Location->addItems(*project->regionMapSections); + ui->comboBox_NewMap_Location->addItems(project->mapSectionValueToName.values()); if (!mapSec.isEmpty()) ui->comboBox_NewMap_Location->setCurrentText(mapSec); ui->frame_NewMap_Options->setEnabled(true); diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp index fa195d7c..c573fa79 100644 --- a/src/ui/paletteeditor.cpp +++ b/src/ui/paletteeditor.cpp @@ -1,6 +1,6 @@ #include "paletteeditor.h" #include "ui_paletteeditor.h" -#include "paletteparser.h" +#include "paletteutil.h" #include #include #include "log.h" @@ -268,7 +268,7 @@ void PaletteEditor::on_actionImport_Palette_triggered() return; } - PaletteParser parser; + PaletteUtil parser; bool error = false; QList palette = parser.parse(filepath, &error); if (error) { diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp new file mode 100644 index 00000000..858abc82 --- /dev/null +++ b/src/ui/regionmapeditor.cpp @@ -0,0 +1,807 @@ +#include "regionmapeditor.h" +#include "ui_regionmapeditor.h" +#include "imageexport.h" +#include "config.h" +#include "log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RegionMapEditor::RegionMapEditor(QWidget *parent, Project *project_) : + QMainWindow(parent), + ui(new Ui::RegionMapEditor) +{ + this->ui->setupUi(this); + this->project = project_; + this->region_map = new RegionMap; + this->ui->action_RegionMap_Resize->setVisible(false); +} + +RegionMapEditor::~RegionMapEditor() +{ + delete ui; + delete region_map; + delete region_map_item; + delete mapsquare_selector_item; + delete region_map_layout_item; + delete scene_region_map_image; + delete city_map_selector_item; + delete city_map_item; + delete scene_city_map_tiles; + delete scene_city_map_image; + delete scene_region_map_layout; + delete scene_region_map_tiles; +} + +void RegionMapEditor::on_action_RegionMap_Save_triggered() { + setCurrentSquareOptions(); + if (project && region_map) { + this->region_map->save(); + this->city_map_item->save(); + this->currIndex = this->region_map_layout_item->highlightedTile; + this->region_map_layout_item->highlightedTile = -1; + displayRegionMap(); + } + this->hasUnsavedChanges = false; +} + +void RegionMapEditor::setCurrentSquareOptions() { + if (project && region_map) { + this->region_map->saveOptions( + this->currIndex, + this->ui->comboBox_RM_ConnectedMap->currentText(), + this->ui->lineEdit_RM_MapName->text(), + this->region_map->map_squares[this->currIndex].x, + this->region_map->map_squares[this->currIndex].y + ); + } +} + +void RegionMapEditor::loadRegionMapData() { + this->region_map->init(project); + this->currIndex = this->region_map->width() * this->region_map->padTop + this->region_map->padLeft; + displayRegionMap(); +} + +void RegionMapEditor::loadCityMaps() { + QDir directory(project->root + "/graphics/pokenav/city_maps"); + QStringList files = directory.entryList(QStringList() << "*.bin", QDir::Files); + QStringList without_bin; + for (QString file : files) { + without_bin.append(file.remove(".bin")); + } + this->ui->comboBox_CityMap_picker->addItems(without_bin); +} + +void RegionMapEditor::displayRegionMap() { + displayRegionMapTileSelector(); + displayCityMapTileSelector(); + displayRegionMapImage(); + displayRegionMapLayout(); + displayRegionMapLayoutOptions(); +} + +void RegionMapEditor::displayRegionMapImage() { + if (!scene_region_map_image) { + this->scene_region_map_image = new QGraphicsScene; + } + if (region_map_item && scene_region_map_image) { + scene_region_map_image->removeItem(region_map_item); + delete region_map_item; + } + + this->region_map_item = new RegionMapPixmapItem(this->region_map, this->mapsquare_selector_item); + this->region_map_item->draw(); + + connect(this->region_map_item, &RegionMapPixmapItem::mouseEvent, + this, &RegionMapEditor::mouseEvent_region_map); + connect(this->region_map_item, &RegionMapPixmapItem::hoveredRegionMapTileChanged, + this, &RegionMapEditor::onHoveredRegionMapTileChanged); + connect(this->region_map_item, &RegionMapPixmapItem::hoveredRegionMapTileCleared, + this, &RegionMapEditor::onHoveredRegionMapTileCleared); + + this->scene_region_map_image->addItem(this->region_map_item); + this->scene_region_map_image->setSceneRect(this->scene_region_map_image->itemsBoundingRect()); + + this->ui->graphicsView_Region_Map_BkgImg->setScene(this->scene_region_map_image); + + if (regionMapFirstDraw) { + on_verticalSlider_Zoom_Map_Image_valueChanged(initialScale); + RegionMapHistoryItem *commit = new RegionMapHistoryItem( + RegionMapEditorBox::BackgroundImage, this->region_map->getTiles(), this->region_map->width(), this->region_map->height() + ); + history.push(commit); + regionMapFirstDraw = false; + } +} + +void RegionMapEditor::displayRegionMapLayout() { + if (!scene_region_map_layout) { + this->scene_region_map_layout = new QGraphicsScene; + } + if (region_map_layout_item && scene_region_map_layout) { + this->scene_region_map_layout->removeItem(region_map_layout_item); + delete region_map_layout_item; + } + + this->region_map_layout_item = new RegionMapLayoutPixmapItem(this->region_map, this->mapsquare_selector_item); + + connect(this->region_map_layout_item, &RegionMapLayoutPixmapItem::selectedTileChanged, + this, &RegionMapEditor::onRegionMapLayoutSelectedTileChanged); + connect(this->region_map_layout_item, &RegionMapLayoutPixmapItem::hoveredTileChanged, + this, &RegionMapEditor::onRegionMapLayoutHoveredTileChanged); + connect(this->region_map_layout_item, &RegionMapLayoutPixmapItem::hoveredTileCleared, + this, &RegionMapEditor::onRegionMapLayoutHoveredTileCleared); + + this->region_map_layout_item->draw(); + this->region_map_layout_item->select(this->currIndex); + + this->scene_region_map_layout->addItem(region_map_layout_item); + this->scene_region_map_layout->setSceneRect(this->scene_region_map_layout->itemsBoundingRect()); + + this->ui->graphicsView_Region_Map_Layout->setScene(this->scene_region_map_layout); +} + +void RegionMapEditor::displayRegionMapLayoutOptions() { + this->ui->comboBox_RM_ConnectedMap->clear(); + this->ui->comboBox_RM_ConnectedMap->addItems(this->project->mapSectionValueToName.values()); + + this->ui->frame_RM_Options->setEnabled(true); + + updateRegionMapLayoutOptions(this->currIndex); + + // TODO: implement when the code is decompiled + this->ui->label_RM_CityMap->setVisible(false); + this->ui->comboBox_RM_CityMap->setVisible(false); +} + +void RegionMapEditor::updateRegionMapLayoutOptions(int index) { + this->ui->comboBox_RM_ConnectedMap->blockSignals(true); + this->ui->lineEdit_RM_MapName->setText(this->project->mapSecToMapHoverName->value(this->region_map->map_squares[index].mapsec)); + this->ui->comboBox_RM_ConnectedMap->setCurrentText(this->region_map->map_squares[index].mapsec); + this->ui->comboBox_RM_ConnectedMap->blockSignals(false); +} + +void RegionMapEditor::displayRegionMapTileSelector() { + if (!scene_region_map_tiles) { + this->scene_region_map_tiles = new QGraphicsScene; + } + if (mapsquare_selector_item && scene_region_map_tiles) { + this->scene_region_map_tiles->removeItem(mapsquare_selector_item); + delete mapsquare_selector_item; + } + + this->mapsquare_selector_item = new TilemapTileSelector(QPixmap(this->region_map->pngPath())); + this->mapsquare_selector_item->draw(); + + this->scene_region_map_tiles->addItem(this->mapsquare_selector_item); + + connect(this->mapsquare_selector_item, &TilemapTileSelector::selectedTileChanged, + this, &RegionMapEditor::onRegionMapTileSelectorSelectedTileChanged); + connect(this->mapsquare_selector_item, &TilemapTileSelector::hoveredTileChanged, + this, &RegionMapEditor::onRegionMapTileSelectorHoveredTileChanged); + connect(this->mapsquare_selector_item, &TilemapTileSelector::hoveredTileCleared, + this, &RegionMapEditor::onRegionMapTileSelectorHoveredTileCleared); + + this->ui->graphicsView_RegionMap_Tiles->setScene(this->scene_region_map_tiles); + on_verticalSlider_Zoom_Image_Tiles_valueChanged(initialScale); + + this->mapsquare_selector_item->select(this->selectedImageTile); +} + +void RegionMapEditor::displayCityMapTileSelector() { + if (!scene_city_map_tiles) { + this->scene_city_map_tiles = new QGraphicsScene; + } + if (city_map_selector_item && scene_city_map_tiles) { + scene_city_map_tiles->removeItem(city_map_selector_item); + delete city_map_selector_item; + } + + this->city_map_selector_item = new TilemapTileSelector(QPixmap(this->region_map->cityTilesPath())); + this->city_map_selector_item->draw(); + + this->scene_city_map_tiles->addItem(this->city_map_selector_item); + + connect(this->city_map_selector_item, &TilemapTileSelector::selectedTileChanged, + this, &RegionMapEditor::onCityMapTileSelectorSelectedTileChanged); + + this->ui->graphicsView_City_Map_Tiles->setScene(this->scene_city_map_tiles); + on_verticalSlider_Zoom_City_Tiles_valueChanged(initialScale); + + this->city_map_selector_item->select(this->selectedCityTile); +} + +void RegionMapEditor::displayCityMap(QString f) { + QString file = this->project->root + "/graphics/pokenav/city_maps/" + f + ".bin"; + + if (!scene_city_map_image) { + scene_city_map_image = new QGraphicsScene; + } + if (city_map_item && scene_city_map_image) { + scene_city_map_image->removeItem(city_map_item); + delete city_map_item; + } + + city_map_item = new CityMapPixmapItem(file, this->city_map_selector_item); + city_map_item->draw(); + + connect(this->city_map_item, &CityMapPixmapItem::mouseEvent, + this, &RegionMapEditor::mouseEvent_city_map); + + scene_city_map_image->addItem(city_map_item); + scene_city_map_image->setSceneRect(this->scene_city_map_image->sceneRect()); + + this->ui->graphicsView_City_Map->setScene(scene_city_map_image); + on_verticalSlider_Zoom_City_Map_valueChanged(initialScale); +} + +bool RegionMapEditor::createCityMap(QString name) { + bool errored = false; + + QString file = this->project->root + "/graphics/pokenav/city_maps/" + name + ".bin"; + + uint8_t filler = 0x30; + uint8_t border = 0x7; + uint8_t blank = 0x1; + QByteArray new_data(400, filler); + for (int i = 0; i < new_data.size(); i++) { + if (i % 2) continue; + int x = i % 20; + int y = i / 20; + if (y <= 1 || y >= 8 || x <= 3 || x >= 16) + new_data[i] = border; + else + new_data[i] = blank; + } + + QFile binFile(file); + if (!binFile.open(QIODevice::WriteOnly)) errored = true; + binFile.write(new_data); + binFile.close(); + + loadCityMaps(); + this->ui->comboBox_CityMap_picker->setCurrentText(name); + + return !errored; +} + +void RegionMapEditor::onRegionMapTileSelectorSelectedTileChanged(unsigned id) { + this->selectedImageTile = id; +} + +void RegionMapEditor::onCityMapTileSelectorSelectedTileChanged(unsigned id) { + this->selectedCityTile = id; +} + +void RegionMapEditor::onRegionMapTileSelectorHoveredTileChanged(unsigned tileId) { + QString message = QString("Tile: 0x") + QString("%1").arg(tileId, 4, 16, QChar('0')).toUpper(); + this->ui->statusbar->showMessage(message); +} + +void RegionMapEditor::onRegionMapTileSelectorHoveredTileCleared() { + this->ui->statusbar->clearMessage(); +} + +void RegionMapEditor::onRegionMapLayoutSelectedTileChanged(int index) { + setCurrentSquareOptions(); + QString message = QString(); + this->currIndex = index; + this->region_map_layout_item->highlightedTile = index; + if (this->region_map->map_squares[index].has_map) { + message = QString("\t %1").arg(this->project->mapSecToMapHoverName->value( + this->region_map->map_squares[index].mapsec)).remove("{NAME_END}"); + } + this->ui->statusbar->showMessage(message); + + updateRegionMapLayoutOptions(index); + this->region_map_layout_item->draw(); +} + +void RegionMapEditor::onRegionMapLayoutHoveredTileChanged(int index) { + QString message = QString(); + int x = this->region_map->map_squares[index].x; + int y = this->region_map->map_squares[index].y; + if (x >= 0 && y >= 0) { + message = QString("(%1, %2)").arg(x).arg(y); + if (this->region_map->map_squares[index].has_map) { + message += QString("\t %1").arg(this->project->mapSecToMapHoverName->value( + this->region_map->map_squares[index].mapsec)).remove("{NAME_END}"); + } + } + this->ui->statusbar->showMessage(message); +} + +void RegionMapEditor::onRegionMapLayoutHoveredTileCleared() { + this->ui->statusbar->clearMessage(); +} + +void RegionMapEditor::onHoveredRegionMapTileChanged(int x, int y) { + QString message = QString("x: %1, y: %2 Tile: 0x").arg(x).arg(y) + + QString("%1").arg(this->region_map->getTileId(x, y), 4, 16, QChar('0')).toUpper(); + this->ui->statusbar->showMessage(message); +} + +void RegionMapEditor::onHoveredRegionMapTileCleared() { + this->ui->statusbar->clearMessage(); +} + +void RegionMapEditor::mouseEvent_region_map(QGraphicsSceneMouseEvent *event, RegionMapPixmapItem *item) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 8; + int y = static_cast(pos.y()) / 8; + int index = this->region_map->getMapSquareIndex(x, y); + if (index > this->region_map->map_squares.size() - 1) return; + + if (event->buttons() & Qt::RightButton) { + item->select(event); + //} else if (event->buttons() & Qt::MiddleButton) {// TODO + } else { + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + RegionMapHistoryItem *current = history.current(); + bool addToHistory = !(current && current->tiles == this->region_map->getTiles()); + if (addToHistory) { + RegionMapHistoryItem *commit = new RegionMapHistoryItem( + RegionMapEditorBox::BackgroundImage, this->region_map->getTiles(), this->region_map->width(), this->region_map->height() + ); + history.push(commit); + } + } else { + item->paint(event); + this->region_map_layout_item->draw(); + this->hasUnsavedChanges = true; + } + } +} + +void RegionMapEditor::mouseEvent_city_map(QGraphicsSceneMouseEvent *event, CityMapPixmapItem *item) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 8; + int y = static_cast(pos.y()) / 8; + int index = this->city_map_item->getIndexAt(x, y); + + if (cityMapFirstDraw) { + RegionMapHistoryItem *commit = new RegionMapHistoryItem( + RegionMapEditorBox::CityMapImage, this->city_map_item->getTiles(), this->city_map_item->file + ); + history.push(commit); + cityMapFirstDraw = false; + } + + if (event->buttons() & Qt::RightButton) {// TODO + //} else if (event->buttons() & Qt::MiddleButton) {// TODO + } else { + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + RegionMapHistoryItem *current = history.current(); + bool addToHistory = !(current && current->tiles == this->city_map_item->getTiles()); + if (addToHistory) { + RegionMapHistoryItem *commit = new RegionMapHistoryItem( + RegionMapEditorBox::CityMapImage, this->city_map_item->getTiles(), this->city_map_item->file + ); + history.push(commit); + } + } else { + item->paint(event); + this->hasUnsavedChanges = true; + } + } +} + +void RegionMapEditor::on_tabWidget_Region_Map_currentChanged(int index) { + this->ui->stackedWidget_RM_Options->setCurrentIndex(index); + switch (index) + { + case 0: + this->ui->verticalSlider_Zoom_Image_Tiles->setVisible(true); + break; + case 1: + this->ui->verticalSlider_Zoom_Image_Tiles->setVisible(false); + break; + } +} + +void RegionMapEditor::on_comboBox_RM_ConnectedMap_activated(const QString &mapsec) { + this->ui->lineEdit_RM_MapName->setText(this->project->mapSecToMapHoverName->value(mapsec)); + this->hasUnsavedChanges = true;// sometimes this is called for unknown reasons +} + +void RegionMapEditor::on_lineEdit_RM_MapName_textEdited(const QString &text) { + this->hasUnsavedChanges = true; +} + +void RegionMapEditor::on_pushButton_RM_Options_delete_clicked() { + this->region_map->resetSquare(this->region_map_layout_item->selectedTile); + updateRegionMapLayoutOptions(this->region_map_layout_item->selectedTile); + this->region_map_layout_item->draw(); + this->region_map_layout_item->select(this->region_map_layout_item->selectedTile); + this->hasUnsavedChanges = true; +} + +void RegionMapEditor::on_pushButton_CityMap_add_clicked() { + QDialog popup(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + popup.setWindowTitle("New City Map"); + popup.setWindowModality(Qt::NonModal); + + QFormLayout form(&popup); + + QLineEdit *input = new QLineEdit(); + form.addRow(new QLabel("Name:"), input); + + QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup); + + QString name; + + form.addRow(&buttonBox); + connect(&buttonBox, SIGNAL(rejected()), &popup, SLOT(reject())); + connect(&buttonBox, &QDialogButtonBox::accepted, [&popup, &input, &name](){ + name = input->text().remove(QRegularExpression("[^a-zA-Z0-9_]+")); + if (!name.isEmpty()) + popup.accept(); + }); + + if (popup.exec() == QDialog::Accepted) { + createCityMap(name); + } + + this->hasUnsavedChanges = true; +} + +void RegionMapEditor::on_action_RegionMap_Resize_triggered() { + QDialog popup(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + popup.setWindowTitle("New Region Map Dimensions"); + popup.setWindowModality(Qt::NonModal); + + QFormLayout form(&popup); + + QSpinBox *widthSpinBox = new QSpinBox(); + QSpinBox *heightSpinBox = new QSpinBox(); + widthSpinBox->setMinimum(32); + heightSpinBox->setMinimum(20); + widthSpinBox->setMaximum(60);// TODO: find real limits + heightSpinBox->setMaximum(40); + widthSpinBox->setValue(this->region_map->width()); + heightSpinBox->setValue(this->region_map->height()); + form.addRow(new QLabel("Width"), widthSpinBox); + form.addRow(new QLabel("Height"), heightSpinBox); + + QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup); + + form.addRow(&buttonBox); + connect(&buttonBox, SIGNAL(rejected()), &popup, SLOT(reject())); + connect(&buttonBox, SIGNAL(accepted()), &popup, SLOT(accept())); + + if (popup.exec() == QDialog::Accepted) { + resize(widthSpinBox->value(), heightSpinBox->value()); + RegionMapHistoryItem *commit = new RegionMapHistoryItem( + RegionMapEditorBox::BackgroundImage, this->region_map->getTiles(), widthSpinBox->value(), heightSpinBox->value() + ); + history.push(commit); + } + + this->hasUnsavedChanges = true; +} + +void RegionMapEditor::on_action_RegionMap_Undo_triggered() { + undo(); + this->hasUnsavedChanges = true; +} + +void RegionMapEditor::undo() { + RegionMapHistoryItem *commit = history.back(); + if (!commit) return; + + switch (commit->which) + { + case RegionMapEditorBox::BackgroundImage: + if (commit->mapWidth != this->region_map->width() || commit->mapHeight != this->region_map->height()) + this->resize(commit->mapWidth, commit->mapHeight); + this->region_map->setTiles(commit->tiles); + this->region_map_item->draw(); + this->region_map_layout_item->draw(); + break; + case RegionMapEditorBox::CityMapImage: + if (commit->cityMap == this->city_map_item->file) + this->city_map_item->setTiles(commit->tiles); + this->city_map_item->draw(); + break; + } +} + +void RegionMapEditor::on_action_RegionMap_Redo_triggered() { + redo(); + this->hasUnsavedChanges = true; +} + +void RegionMapEditor::redo() { + RegionMapHistoryItem *commit = history.next(); + if (!commit) return; + + switch (commit->which) + { + case RegionMapEditorBox::BackgroundImage: + if (commit->mapWidth != this->region_map->width() || commit->mapHeight != this->region_map->height()) + this->resize(commit->mapWidth, commit->mapHeight); + this->region_map->setTiles(commit->tiles); + this->region_map_item->draw(); + this->region_map_layout_item->draw(); + break; + case RegionMapEditorBox::CityMapImage: + this->city_map_item->setTiles(commit->tiles); + this->city_map_item->draw(); + break; + } +} + +void RegionMapEditor::resize(int w, int h) { + this->region_map->resize(w, h); + this->currIndex = this->region_map->padLeft * w + this->region_map->padTop; + displayRegionMapImage(); + displayRegionMapLayout(); + displayRegionMapLayoutOptions(); +} + +void RegionMapEditor::on_action_Swap_triggered() { + QDialog popup(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + popup.setWindowTitle("New City Map"); + popup.setWindowModality(Qt::NonModal); + + QFormLayout form(&popup); + + QComboBox *oldSecBox = new QComboBox(); + oldSecBox->addItems(this->project->mapSectionValueToName.values()); + form.addRow(new QLabel("Old Map Section:"), oldSecBox); + QComboBox *newSecBox = new QComboBox(); + newSecBox->addItems(this->project->mapSectionValueToName.values()); + form.addRow(new QLabel("New Map Section:"), newSecBox); + + QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup); + form.addRow(&buttonBox); + + QString beforeSection, afterSection; + uint8_t oldId, newId; + connect(&buttonBox, SIGNAL(rejected()), &popup, SLOT(reject())); + connect(&buttonBox, &QDialogButtonBox::accepted, [this, &popup, &oldSecBox, &newSecBox, + &beforeSection, &afterSection, &oldId, &newId](){ + beforeSection = oldSecBox->currentText(); + afterSection = newSecBox->currentText(); + if (!beforeSection.isEmpty() && !afterSection.isEmpty()) { + oldId = static_cast(this->project->mapSectionNameToValue.value(beforeSection)); + newId = static_cast(this->project->mapSectionNameToValue.value(afterSection)); + popup.accept(); + } + }); + + if (popup.exec() == QDialog::Accepted) { + this->region_map->replaceSectionId(oldId, newId); + this->region_map_layout_item->draw(); + this->region_map_layout_item->select(this->region_map_layout_item->selectedTile); + this->hasUnsavedChanges = true; + } +} + +void RegionMapEditor::on_action_RegionMap_ClearImage_triggered() { + this->region_map->clearImage(); + RegionMapHistoryItem *commit = new RegionMapHistoryItem( + RegionMapEditorBox::BackgroundImage, this->region_map->getTiles(), this->region_map->width(), this->region_map->height() + ); + history.push(commit); + + displayRegionMapImage(); + displayRegionMapLayout(); +} + +void RegionMapEditor::on_action_RegionMap_ClearLayout_triggered() { + QMessageBox::StandardButton result = QMessageBox::question( + this, + "WARNING", + "This action will reset the entire map layout to MAPSEC_NONE, continue?", + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Yes + ); + + if (result == QMessageBox::Yes) { + this->region_map->clearLayout(); + displayRegionMapLayout(); + } else { + return; + } +} + +void RegionMapEditor::on_action_Import_RegionMap_ImageTiles_triggered() { + importTileImage(false); +} + +void RegionMapEditor::on_action_Import_CityMap_ImageTiles_triggered() { + importTileImage(true); +} + +void RegionMapEditor::importTileImage(bool city) { + QString descriptor = city ? "City Map" : "Region Map"; + + QString infilepath = QFileDialog::getOpenFileName(this, QString("Import %1 Tiles Image").arg(descriptor), + this->project->root, "Image Files (*.png *.bmp *.jpg *.dib)"); + if (infilepath.isEmpty()) { + return; + } + + logInfo(QString("Importing %1 Tiles from '%2'").arg(descriptor).arg(infilepath)); + + // Read image data from buffer so that the built-in QImage doesn't try to detect file format + // purely from the extension name. + QFile file(infilepath); + QImage image; + if (file.open(QIODevice::ReadOnly)) { + QByteArray imageData = file.readAll(); + image = QImage::fromData(imageData); + } else { + QString errorMessage = QString("Failed to open image file: '%1'").arg(infilepath); + logError(errorMessage); + QMessageBox msgBox(this); + msgBox.setText("Failed to import tiles."); + msgBox.setInformativeText(errorMessage); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + if (image.width() == 0 || image.height() == 0 || image.width() % 8 != 0 || image.height() % 8 != 0) { + QString errorMessage = QString("The image dimensions (%1 x %2) are invalid. Width and height must be multiples of 8 pixels.") + .arg(image.width()) + .arg(image.height()); + logError(errorMessage); + QMessageBox msgBox(this); + msgBox.setText("Failed to import tiles."); + msgBox.setInformativeText(errorMessage); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + + // Validate total number of tiles in image. + int numTilesWide = image.width() / 8; + int numTilesHigh = image.height() / 8; + int totalTiles = numTilesHigh * numTilesWide; + int maxAllowedTiles = 0x100; + if (totalTiles > maxAllowedTiles) { + QString errorMessage = QString("The total number of tiles in the provided image (%1) is greater than the allowed number (%2).") + .arg(maxAllowedTiles) + .arg(totalTiles); + logError(errorMessage); + QMessageBox msgBox(this); + msgBox.setText("Failed to import tiles."); + msgBox.setInformativeText(errorMessage); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + + // Validate the image's palette. + QString palMessage = QString(); + bool palError = false; + if (image.colorCount() == 0) { + palMessage = QString("The provided image is not indexed."); + palError = true; + } else if (!city && image.colorCount() != 256) { + palMessage = QString("The provided image has a palette with %1 colors. You must provide an indexed imaged with a 256 color palette.").arg(image.colorCount()); + palError = true; + } else if (city && image.colorCount() != 16) { + palMessage = QString("The provided image has a palette with %1 colors. You must provide an indexed imaged with a 16 color palette.").arg(image.colorCount()); + palError = true; + } + + if (palError) { + logError(palMessage); + QMessageBox msgBox(this); + msgBox.setText("Failed to import tiles."); + msgBox.setInformativeText(palMessage); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + + // Use the image from the correct path. + if (city) { + this->region_map->setTemporaryCityTilesPath(infilepath); + } else { + this->region_map->setTemporaryPngPath(infilepath); + } + this->hasUnsavedChanges = true; + + // Redload and redraw images. + displayRegionMap(); + displayCityMap(this->ui->comboBox_CityMap_picker->currentText()); +} + +void RegionMapEditor::on_comboBox_CityMap_picker_currentTextChanged(const QString &file) { + this->displayCityMap(file); + this->cityMapFirstDraw = true; +} + +void RegionMapEditor::closeEvent(QCloseEvent *event) +{ + if (this->hasUnsavedChanges) { + QMessageBox::StandardButton result = QMessageBox::question( + this, + "porymap", + "The region map has been modified, save changes?", + QMessageBox::No | QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Yes); + + if (result == QMessageBox::Yes) { + this->on_action_RegionMap_Save_triggered(); + event->accept(); + } else if (result == QMessageBox::No) { + event->accept(); + } else if (result == QMessageBox::Cancel) { + event->ignore(); + } + } else { + event->accept(); + } +} + +void RegionMapEditor::on_verticalSlider_Zoom_Map_Image_valueChanged(int val) { + double scale = pow(scaleUpFactor, static_cast(val - initialScale) / initialScale); + + QMatrix matrix; + matrix.scale(scale, scale); + int width = ceil(static_cast(this->region_map->imgSize().width()) * scale); + int height = ceil(static_cast(this->region_map->imgSize().height()) * scale); + + ui->graphicsView_Region_Map_BkgImg->setResizeAnchor(QGraphicsView::NoAnchor); + ui->graphicsView_Region_Map_BkgImg->setMatrix(matrix); + ui->graphicsView_Region_Map_BkgImg->setFixedSize(width + 2, height + 2); + ui->graphicsView_Region_Map_Layout->setResizeAnchor(QGraphicsView::NoAnchor); + ui->graphicsView_Region_Map_Layout->setMatrix(matrix); + ui->graphicsView_Region_Map_Layout->setFixedSize(width + 2, height + 2); +} + +void RegionMapEditor::on_verticalSlider_Zoom_Image_Tiles_valueChanged(int val) { + double scale = pow(scaleUpFactor, static_cast(val - initialScale) / initialScale); + + QMatrix matrix; + matrix.scale(scale, scale); + int width = ceil(static_cast(this->mapsquare_selector_item->pixelWidth) * scale); + int height = ceil(static_cast(this->mapsquare_selector_item->pixelHeight) * scale); + + ui->graphicsView_RegionMap_Tiles->setResizeAnchor(QGraphicsView::NoAnchor); + ui->graphicsView_RegionMap_Tiles->setMatrix(matrix); + ui->graphicsView_RegionMap_Tiles->setFixedSize(width + 2, height + 2); +} + +void RegionMapEditor::on_verticalSlider_Zoom_City_Map_valueChanged(int val) { + double scale = pow(scaleUpFactor, static_cast(val - initialScale) / initialScale); + + QMatrix matrix; + matrix.scale(scale, scale); + int width = ceil(static_cast(8 * city_map_item->width()) * scale); + int height = ceil(static_cast(8 * city_map_item->height()) * scale); + + ui->graphicsView_City_Map->setResizeAnchor(QGraphicsView::NoAnchor); + ui->graphicsView_City_Map->setMatrix(matrix); + ui->graphicsView_City_Map->setFixedSize(width + 2, height + 2); +} + +void RegionMapEditor::on_verticalSlider_Zoom_City_Tiles_valueChanged(int val) { + double scale = pow(scaleUpFactor, static_cast(val - initialScale) / initialScale); + + QMatrix matrix; + matrix.scale(scale, scale); + int width = ceil(static_cast(this->city_map_selector_item->pixelWidth) * scale); + int height = ceil(static_cast(this->city_map_selector_item->pixelHeight) * scale); + + ui->graphicsView_City_Map_Tiles->setResizeAnchor(QGraphicsView::NoAnchor); + ui->graphicsView_City_Map_Tiles->setMatrix(matrix); + ui->graphicsView_City_Map_Tiles->setFixedSize(width + 2, height + 2); +} diff --git a/src/ui/regionmaplayoutpixmapitem.cpp b/src/ui/regionmaplayoutpixmapitem.cpp new file mode 100644 index 00000000..f9f6c2fc --- /dev/null +++ b/src/ui/regionmaplayoutpixmapitem.cpp @@ -0,0 +1,90 @@ +#include "regionmaplayoutpixmapitem.h" + +void RegionMapLayoutPixmapItem::draw() { + if (!region_map) return; + + QImage image(region_map->width() * 8, region_map->height() * 8, QImage::Format_RGBA8888); + + QPainter painter(&image); + for (int i = 0; i < region_map->map_squares.size(); i++) { + QImage bottom_img = this->tile_selector->tileImg(region_map->map_squares[i].tile_img_id); + QImage top_img(8, 8, QImage::Format_RGBA8888); + if (region_map->map_squares[i].has_map) { + top_img.fill(Qt::gray); + } else { + top_img.fill(Qt::black); + } + int x = i % region_map->width(); + int y = i / region_map->width(); + QPoint pos = QPoint(x * 8, y * 8); + painter.setOpacity(1); + painter.drawImage(pos, bottom_img); + painter.save(); + painter.setOpacity(0.55); + painter.drawImage(pos, top_img); + painter.restore(); + } + painter.end(); + + this->setPixmap(QPixmap::fromImage(image)); + this->drawSelection(); +} + +void RegionMapLayoutPixmapItem::select(int x, int y) { + int index = this->region_map->getMapSquareIndex(x, y); + SelectablePixmapItem::select(x, y, 0, 0); + this->selectedTile = index; + this->updateSelectedTile(); + + emit selectedTileChanged(index); +} + +void RegionMapLayoutPixmapItem::select(int index) { + int x = index % this->region_map->width(); + int y = index / this->region_map->width(); + SelectablePixmapItem::select(x, y, 0, 0); + this->selectedTile = index; + this->updateSelectedTile(); + + emit selectedTileChanged(index); +} + +void RegionMapLayoutPixmapItem::highlight(int x, int y, int red) { + this->highlightedTile = red; + SelectablePixmapItem::select(x + this->region_map->padLeft, y + this->region_map->padTop, 0, 0); + draw(); +} + +void RegionMapLayoutPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + QPoint pos = this->getCellPos(event->pos()); + int index = this->region_map->getMapSquareIndex(pos.x(), pos.y()); + if (this->region_map->map_squares[index].x >= 0 + && this->region_map->map_squares[index].y >= 0) { + SelectablePixmapItem::mousePressEvent(event); + this->updateSelectedTile(); + emit selectedTileChanged(this->selectedTile); + } +} + +void RegionMapLayoutPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + mousePressEvent(event); +} + +void RegionMapLayoutPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {} + +void RegionMapLayoutPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + QPoint pos = this->getCellPos(event->pos()); + int tileId = this->region_map->getMapSquareIndex(pos.x(), pos.y()); + emit this->hoveredTileChanged(tileId); +} + +void RegionMapLayoutPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) { + emit this->hoveredTileCleared(); +} + +void RegionMapLayoutPixmapItem::updateSelectedTile() { + QPoint origin = this->getSelectionStart(); + this->selectedTile = this->region_map->getMapSquareIndex(origin.x(), origin.y()); + this->highlightedTile = -1; + draw(); +} diff --git a/src/ui/regionmappixmapitem.cpp b/src/ui/regionmappixmapitem.cpp new file mode 100644 index 00000000..3ca04b06 --- /dev/null +++ b/src/ui/regionmappixmapitem.cpp @@ -0,0 +1,66 @@ +#include "regionmappixmapitem.h" + +void RegionMapPixmapItem::draw() { + if (!region_map) return; + + QImage image(region_map->width() * 8, region_map->height() * 8, QImage::Format_RGBA8888); + + QPainter painter(&image); + for (int i = 0; i < region_map->map_squares.size(); i++) { + QImage img = this->tile_selector->tileImg(region_map->map_squares[i].tile_img_id); + int x = i % region_map->width(); + int y = i / region_map->width(); + QPoint pos = QPoint(x * 8, y * 8); + painter.drawImage(pos, img); + } + painter.end(); + + this->setPixmap(QPixmap::fromImage(image)); +} + +void RegionMapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { + if (region_map) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 8; + int y = static_cast(pos.y()) / 8; + int index = x + y * region_map->width(); + this->region_map->map_squares[index].tile_img_id = this->tile_selector->selectedTile; + draw(); + } +} + +void RegionMapPixmapItem::select(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 8; + int y = static_cast(pos.y()) / 8; + this->tile_selector->select(this->region_map->getTileId(x, y)); +} + +void RegionMapPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + int x = static_cast(event->pos().x()) / 8; + int y = static_cast(event->pos().y()) / 8; + emit this->hoveredRegionMapTileChanged(x, y); +} + +void RegionMapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { + emit this->hoveredRegionMapTileCleared(); +} + +void RegionMapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} + +void RegionMapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 8; + int y = static_cast(pos.y()) / 8; + if (x < this->region_map->width() && x >= 0 + && y < this->region_map->height() && y >= 0) { + emit this->hoveredRegionMapTileChanged(x, y); + emit mouseEvent(event, this); + } +} + +void RegionMapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} diff --git a/src/ui/tilemaptileselector.cpp b/src/ui/tilemaptileselector.cpp new file mode 100644 index 00000000..a18185ab --- /dev/null +++ b/src/ui/tilemaptileselector.cpp @@ -0,0 +1,77 @@ +#include "tilemaptileselector.h" + +#include + +void TilemapTileSelector::draw() { + size_t width_ = this->tilemap.width(); + this->pixelWidth = width_; + size_t height_ = this->tilemap.height(); + this->pixelHeight = height_; + size_t ntiles_ = (width_/8) * (height_/8); + + this->numTilesWide = width_ / 8; + this->numTiles = ntiles_; + + this->setPixmap(tilemap); + this->drawSelection(); +} + +void TilemapTileSelector::select(unsigned tileId) { + QPoint coords = this->getTileIdCoords(tileId); + SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0); + this->selectedTile = tileId; + emit selectedTileChanged(tileId); +} + +void TilemapTileSelector::updateSelectedTile() { + QPoint origin = this->getSelectionStart(); + this->selectedTile = this->getTileId(origin.x(), origin.y()); +} + +unsigned TilemapTileSelector::getSelectedTile() { + return this->selectedTile; +} + +unsigned TilemapTileSelector::getTileId(int x, int y) { + int index = y * this->numTilesWide + x; + return index < this->numTiles ? index : this->numTiles % index; +} + +QPoint TilemapTileSelector::getTileIdCoords(unsigned tileId) { + int index = tileId < this->numTiles ? tileId : this->numTiles % tileId; + return QPoint(index % this->numTilesWide, index / this->numTilesWide); +} + +QImage TilemapTileSelector::tileImg(unsigned tileId) { + QPoint pos = getTileIdCoords(tileId); + return this->tilemap.copy(pos.x() * 8, pos.y() * 8, 8, 8).toImage(); +} + +void TilemapTileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mousePressEvent(event); + this->updateSelectedTile(); + emit selectedTileChanged(this->selectedTile); +} + +void TilemapTileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseMoveEvent(event); + this->updateSelectedTile(); + emit hoveredTileChanged(this->selectedTile); + emit selectedTileChanged(this->selectedTile); +} + +void TilemapTileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseReleaseEvent(event); + this->updateSelectedTile(); + emit selectedTileChanged(this->selectedTile); +} + +void TilemapTileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + QPoint pos = this->getCellPos(event->pos()); + unsigned tileId = this->getTileId(pos.x(), pos.y()); + emit this->hoveredTileChanged(tileId); +} + +void TilemapTileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) { + emit this->hoveredTileCleared(); +} diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 1df9f2b9..01ac188f 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -3,7 +3,7 @@ #include "log.h" #include "imageproviders.h" #include "metatileparser.h" -#include "paletteparser.h" +#include "paletteutil.h" #include "imageexport.h" #include #include @@ -410,7 +410,7 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) { return; } - PaletteParser parser; + PaletteUtil parser; bool error = false; QList palette = parser.parse(filepath, &error); if (error) {