Merge pull request #77 from garakmon/rme

Region Map Editor
This commit is contained in:
huderlem 2019-04-06 19:59:08 -05:00 committed by GitHub
commit e91df38714
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 3257 additions and 49 deletions

10
.gitignore vendored
View file

@ -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

View file

@ -2750,6 +2750,7 @@
<addaction name="action_NewMap"/>
<addaction name="actionNew_Tileset"/>
<addaction name="actionTileset_Editor"/>
<addaction name="actionRegion_Map_Editor"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
@ -2977,6 +2978,11 @@
<string>Cursor Tile Outline</string>
</property>
</action>
<action name="actionRegion_Map_Editor">
<property name="text">
<string>Region Map Editor</string>
</property>
</action>
<action name="actionNew_Tileset">
<property name="text">
<string>New Tileset...</string>

1148
forms/regionmapeditor.ui Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@
#include <QString>
#include <QObject>
#include <QByteArrayList>
#include <QSize>
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;

View file

@ -12,4 +12,21 @@ public:
~HistoryItem();
};
enum RegionMapEditorBox {
BackgroundImage = 1,
CityMapImage = 2,
};
class RegionMapHistoryItem {
public:
int which;
int mapWidth = 0;
int mapHeight = 0;
QVector<uint8_t> tiles;
QString cityMap;
RegionMapHistoryItem(int type, QVector<uint8_t> tiles, QString cityMap);
RegionMapHistoryItem(int type, QVector<uint8_t> tiles, int width, int height);
~RegionMapHistoryItem();
};
#endif // HISTORYITEM_H

View file

@ -2,6 +2,7 @@
#define MAPCONNECTION_H
#include <QString>
#include <QHash>
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

View file

@ -1,15 +1,16 @@
#ifndef PALETTEPARSER_H
#define PALETTEPARSER_H
#ifndef PALETTEUTIL_H
#define PALETTEUTIL_H
#include <QList>
#include <QRgb>
#include <QString>
class PaletteParser
class PaletteUtil
{
public:
PaletteParser();
PaletteUtil();
QList<QRgb> parse(QString filepath, bool *error);
void writeJASC(QString filepath, QVector<QRgb> colors, int offset, int nColors);
private:
QList<QRgb> parsePal(QString filepath, bool *error);
QList<QRgb> parseJASC(QString filepath, bool *error);
@ -20,4 +21,4 @@ private:
int clampColorValue(int value);
};
#endif // PALETTEPARSER_H
#endif // PALETTEUTIL_H

113
include/core/regionmap.h Normal file
View file

@ -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 <QStringList>
#include <QString>
#include <QVector>
#include <QList>
#include <QMap>
#include <QGraphicsScene>
#include <QGraphicsView>
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<RegionMapSquare> map_squares;
History<RegionMapHistoryItem*> 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<uint8_t> getTiles();
void setTiles(QVector<uint8_t> 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<QString, QString> sMapNamesMap;
QMap<QString, struct RegionMapEntry> mapSecToMapEntry;
QVector<QString> sMapNames;
int img_index_(int x, int y);
int layout_index_(int x, int y);
QString fix_case(QString);
};
#endif // REGIONMAP_H

View file

@ -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;

View file

@ -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;

View file

@ -30,7 +30,9 @@ public:
QString layoutsLabel;
QMap<QString, MapLayout*> mapLayouts;
QMap<QString, MapLayout*> mapLayoutsMaster;
QStringList *regionMapSections = nullptr;
QMap<QString, QString> *mapSecToMapHoverName;
QMap<QString, int> mapSectionNameToValue;
QMap<int, QString> mapSectionValueToName;
QStringList *itemNames = nullptr;
QStringList *flagNames = nullptr;
QStringList *varNames = nullptr;

View file

@ -0,0 +1,52 @@
#ifndef CITYMAPPIXMAPITEM_H
#define CITYMAPPIXMAPITEM_H
#include "tilemaptileselector.h"
#include <QGraphicsPixmapItem>
#include <QByteArray>
#include <QVector>
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<uint8_t> getTiles();
void setTiles(QVector<uint8_t>);
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

View file

@ -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 <QMainWindow>
#include <QGraphicsSceneMouseEvent>
#include <QCloseEvent>
#include <QResizeEvent>
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<RegionMapHistoryItem*> 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

View file

@ -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

View file

@ -0,0 +1,36 @@
#ifndef REGIONMAPPIXMAPITEM_H
#define REGIONMAPPIXMAPITEM_H
#include "regionmap.h"
#include "tilemaptileselector.h"
#include <QGraphicsPixmapItem>
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

View file

@ -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

View file

@ -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

View file

@ -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<QString, QString> 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<BaseGameVersion, QString> baseGameVersionMap = {
{BaseGameVersion::pokeruby, "pokeruby"},
{BaseGameVersion::pokeemerald, "pokeemerald"},

View file

@ -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<uint8_t> tiles, QString cityMap) {
this->which = which;
this->tiles = tiles;
this->cityMap = cityMap;
}
RegionMapHistoryItem::RegionMapHistoryItem(int which, QVector<uint8_t> tiles, int width, int height) {
this->which = which;
this->tiles = tiles;
this->mapWidth = width;
this->mapHeight = height;
}
RegionMapHistoryItem::~RegionMapHistoryItem() {}

View file

@ -341,7 +341,6 @@ void Map::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_
}
}
void Map::undo() {
HistoryItem *commit = metatileHistory.back();
if (!commit)

View file

@ -1,14 +1,14 @@
#include "paletteparser.h"
#include "paletteutil.h"
#include "log.h"
#include <QFileInfo>
#include <QRegularExpression>
PaletteParser::PaletteParser()
PaletteUtil::PaletteUtil()
{
}
QList<QRgb> PaletteParser::parse(QString filepath, bool *error) {
QList<QRgb> PaletteUtil::parse(QString filepath, bool *error) {
QFileInfo info(filepath);
QString extension = info.completeSuffix();
if (extension.isNull()) {
@ -34,7 +34,7 @@ QList<QRgb> PaletteParser::parse(QString filepath, bool *error) {
return QList<QRgb>();
}
QList<QRgb> PaletteParser::parsePal(QString filepath, bool *error) {
QList<QRgb> PaletteUtil::parsePal(QString filepath, bool *error) {
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly)) {
*error = true;
@ -53,7 +53,7 @@ QList<QRgb> PaletteParser::parsePal(QString filepath, bool *error) {
}
}
QList<QRgb> PaletteParser::parseJASC(QString filepath, bool *error) {
QList<QRgb> PaletteUtil::parseJASC(QString filepath, bool *error) {
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly)) {
*error = true;
@ -120,7 +120,7 @@ QList<QRgb> PaletteParser::parseJASC(QString filepath, bool *error) {
return palette;
}
QList<QRgb> PaletteParser::parseAdvanceMapPal(QString filepath, bool *error) {
QList<QRgb> PaletteUtil::parseAdvanceMapPal(QString filepath, bool *error) {
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly)) {
*error = true;
@ -152,7 +152,7 @@ QList<QRgb> PaletteParser::parseAdvanceMapPal(QString filepath, bool *error) {
return palette;
}
QList<QRgb> PaletteParser::parseAdobeColorTable(QString filepath, bool *error) {
QList<QRgb> PaletteUtil::parseAdobeColorTable(QString filepath, bool *error) {
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly)) {
*error = true;
@ -184,7 +184,7 @@ QList<QRgb> PaletteParser::parseAdobeColorTable(QString filepath, bool *error) {
return palette;
}
QList<QRgb> PaletteParser::parseTileLayerPro(QString filepath, bool *error) {
QList<QRgb> PaletteUtil::parseTileLayerPro(QString filepath, bool *error) {
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly)) {
*error = true;
@ -222,7 +222,7 @@ QList<QRgb> PaletteParser::parseTileLayerPro(QString filepath, bool *error) {
return palette;
}
QList<QRgb> PaletteParser::parseAdvancePaletteEditor(QString filepath, bool *error) {
QList<QRgb> PaletteUtil::parseAdvancePaletteEditor(QString filepath, bool *error) {
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly)) {
*error = true;
@ -267,7 +267,35 @@ QList<QRgb> PaletteParser::parseAdvancePaletteEditor(QString filepath, bool *err
return palette;
}
int PaletteParser::clampColorValue(int value) {
void PaletteUtil::writeJASC(QString filepath, QVector<QRgb> 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;
}

361
src/core/regionmap.cpp Normal file
View file

@ -0,0 +1,361 @@
#include "regionmap.h"
#include "paletteutil.h"
#include "log.h"
#include "config.h"
#include <QByteArray>
#include <QFile>
#include <QDebug>
#include <QRegularExpression>
#include <QImage>
#include <math.h>
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<QString, QString> *qmap = new QMap<QString, QString>;
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<uint8_t>(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<uint8_t>(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<uint8_t>(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<RegionMapSquare> 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<uint8_t> RegionMap::getTiles() {
QVector<uint8_t> tileIds;
for (auto square : map_squares) {
tileIds.append(square.tile_img_id);
}
return tileIds;
}
void RegionMap::setTiles(QVector<uint8_t> 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;
}

View file

@ -8,6 +8,7 @@
#include <QCheckBox>
#include <QPainter>
#include <QMouseEvent>
#include <QDir>
#include <math.h>
static bool selectingEvent = false;
@ -1283,6 +1284,5 @@ void Editor::objectsView_onMousePress(QMouseEvent *event) {
selected_events->append(first);
updateSelectedEvents();
}
selectingEvent = false;
}

View file

@ -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<QString, QStringList> tilesets = project->getTilesetLabels();
@ -658,8 +658,8 @@ void MainWindow::sortMapList() {
case MapSortOrder::Area:
{
QMap<QString, int> 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(),

View file

@ -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<QString, int>;
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() {

View file

@ -0,0 +1,111 @@
#include "citymappixmapitem.h"
#include "imageproviders.h"
#include "config.h"
#include "log.h"
#include <QFile>
#include <QPainter>
#include <QDebug>
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<int>(pos.x()) / 8;
int y = static_cast<int>(pos.y()) / 8;
int index = getIndexAt(x, y);
data[index] = static_cast<uint8_t>(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<int>(pos.x()) / 8;
int y = static_cast<int>(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<uint8_t> CityMapPixmapItem::getTiles() {
QVector<uint8_t> tiles;
for (auto tile : data) {
tiles.append(tile);
}
return tiles;
}
void CityMapPixmapItem::setTiles(QVector<uint8_t> 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_;
}

View file

@ -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);

View file

@ -1,6 +1,6 @@
#include "paletteeditor.h"
#include "ui_paletteeditor.h"
#include "paletteparser.h"
#include "paletteutil.h"
#include <QFileDialog>
#include <QMessageBox>
#include "log.h"
@ -268,7 +268,7 @@ void PaletteEditor::on_actionImport_Palette_triggered()
return;
}
PaletteParser parser;
PaletteUtil parser;
bool error = false;
QList<QRgb> palette = parser.parse(filepath, &error);
if (error) {

807
src/ui/regionmapeditor.cpp Normal file
View file

@ -0,0 +1,807 @@
#include "regionmapeditor.h"
#include "ui_regionmapeditor.h"
#include "imageexport.h"
#include "config.h"
#include "log.h"
#include <QDir>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QFormLayout>
#include <QLineEdit>
#include <QSpinBox>
#include <QColor>
#include <QMessageBox>
#include <math.h>
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<int>(pos.x()) / 8;
int y = static_cast<int>(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<int>(pos.x()) / 8;
int y = static_cast<int>(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<uint8_t>(this->project->mapSectionNameToValue.value(beforeSection));
newId = static_cast<uint8_t>(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<double>(val - initialScale) / initialScale);
QMatrix matrix;
matrix.scale(scale, scale);
int width = ceil(static_cast<double>(this->region_map->imgSize().width()) * scale);
int height = ceil(static_cast<double>(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<double>(val - initialScale) / initialScale);
QMatrix matrix;
matrix.scale(scale, scale);
int width = ceil(static_cast<double>(this->mapsquare_selector_item->pixelWidth) * scale);
int height = ceil(static_cast<double>(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<double>(val - initialScale) / initialScale);
QMatrix matrix;
matrix.scale(scale, scale);
int width = ceil(static_cast<double>(8 * city_map_item->width()) * scale);
int height = ceil(static_cast<double>(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<double>(val - initialScale) / initialScale);
QMatrix matrix;
matrix.scale(scale, scale);
int width = ceil(static_cast<double>(this->city_map_selector_item->pixelWidth) * scale);
int height = ceil(static_cast<double>(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);
}

View file

@ -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();
}

View file

@ -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<int>(pos.x()) / 8;
int y = static_cast<int>(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<int>(pos.x()) / 8;
int y = static_cast<int>(pos.y()) / 8;
this->tile_selector->select(this->region_map->getTileId(x, y));
}
void RegionMapPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
int x = static_cast<int>(event->pos().x()) / 8;
int y = static_cast<int>(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<int>(pos.x()) / 8;
int y = static_cast<int>(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);
}

View file

@ -0,0 +1,77 @@
#include "tilemaptileselector.h"
#include <QDebug>
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();
}

View file

@ -3,7 +3,7 @@
#include "log.h"
#include "imageproviders.h"
#include "metatileparser.h"
#include "paletteparser.h"
#include "paletteutil.h"
#include "imageexport.h"
#include <QFileDialog>
#include <QMessageBox>
@ -410,7 +410,7 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
return;
}
PaletteParser parser;
PaletteUtil parser;
bool error = false;
QList<QRgb> palette = parser.parse(filepath, &error);
if (error) {