diff --git a/editor.h b/editor.h new file mode 100755 index 00000000..a98c93f6 --- /dev/null +++ b/editor.h @@ -0,0 +1,440 @@ +#ifndef EDITOR_H +#define EDITOR_H + +#include +#include +#include +#include +#include +#include +#include + +#include "project.h" +#include "ui_mainwindow.h" + +class DraggablePixmapItem; +class MapPixmapItem; +class CollisionPixmapItem; +class ConnectionPixmapItem; +class MetatilesPixmapItem; +class BorderMetatilesPixmapItem; +class CurrentSelectedMetatilesPixmapItem; +class MovementPermissionsPixmapItem; + +#define SWAP(a, b) do { if (a != b) { a ^= b; b ^= a; a ^= b; } } while (0) + +class Editor : public QObject +{ + Q_OBJECT +public: + Editor(Ui::MainWindow* ui); +public: + Ui::MainWindow* ui; + QObject *parent = nullptr; + Project *project = nullptr; + Map *map = nullptr; + void saveProject(); + void save(); + void undo(); + void redo(); + void setMap(QString map_name); + void updateCurrentMetatilesSelection(); + void displayMap(); + void displayMetatiles(); + void displayBorderMetatiles(); + void displayCurrentMetatilesSelection(); + void redrawCurrentMetatilesSelection(); + void displayCollisionMetatiles(); + void displayElevationMetatiles(); + void displayMapEvents(); + void displayMapConnections(); + void displayMapBorder(); + void displayMapGrid(); + + void setEditingMap(); + void setEditingCollision(); + void setEditingObjects(); + void setEditingConnections(); + void setCurrentConnectionDirection(QString curDirection); + void updateCurrentConnectionDirection(QString curDirection); + void setConnectionsVisibility(bool visible); + void updateConnectionOffset(int offset); + void setConnectionMap(QString mapName); + void addNewConnection(); + void removeCurrentConnection(); + void updateDiveMap(QString mapName); + void updateEmergeMap(QString mapName); + void setSelectedConnectionFromMap(QString mapName); + void updatePrimaryTileset(QString tilesetLabel); + void updateSecondaryTileset(QString tilesetLabel); + void toggleBorderVisibility(bool visible); + + DraggablePixmapItem *addMapEvent(Event *event); + void selectMapEvent(DraggablePixmapItem *object); + void selectMapEvent(DraggablePixmapItem *object, bool toggle); + DraggablePixmapItem *addNewEvent(QString event_type); + Event* createNewEvent(QString event_type); + void deleteEvent(Event *); + void updateSelectedEvents(); + void redrawObject(DraggablePixmapItem *item); + QList *getObjects(); + + QGraphicsScene *scene = nullptr; + QGraphicsPixmapItem *current_view = nullptr; + MapPixmapItem *map_item = nullptr; + ConnectionPixmapItem* selected_connection_item = nullptr; + QList connection_items; + QList connection_edit_items; + CollisionPixmapItem *collision_item = nullptr; + QGraphicsItemGroup *events_group = nullptr; + QList borderItems; + QList gridLines; + + QGraphicsScene *scene_metatiles = nullptr; + QGraphicsScene *scene_current_metatile_selection = nullptr; + QGraphicsScene *scene_selected_border_metatiles = nullptr; + QGraphicsScene *scene_collision_metatiles = nullptr; + QGraphicsScene *scene_elevation_metatiles = nullptr; + MetatilesPixmapItem *metatiles_item = nullptr; + + BorderMetatilesPixmapItem *selected_border_metatiles_item = nullptr; + CurrentSelectedMetatilesPixmapItem *scene_current_metatile_selection_item = nullptr; + MovementPermissionsPixmapItem *collision_metatiles_item = nullptr; + + QList *events = nullptr; + QList *selected_events = nullptr; + + bool lastSelectedMetatilesFromMap = false; + int copiedMetatileSelectionWidth = 0; + int copiedMetatileSelectionHeight = 0; + QList *copiedMetatileSelection = new QList; + + int scale_exp = 0; + double scale_base = sqrt(2); // adjust scale factor with this + + QString map_edit_mode; + QString prev_edit_mode; + QCursor cursor; + + void objectsView_onMousePress(QMouseEvent *event); + void objectsView_onMouseMove(QMouseEvent *event); + void objectsView_onMouseRelease(QMouseEvent *event); + +private: + void setConnectionItemsVisible(bool); + void setBorderItemsVisible(bool, qreal = 1); + void setConnectionEditControlValues(Connection*); + void setConnectionEditControlsEnabled(bool); + void createConnectionItem(Connection* connection, bool hide); + void populateConnectionMapPickers(); + void setDiveEmergeControls(); + void updateDiveEmergeMap(QString mapName, QString direction); + void onConnectionOffsetChanged(int newOffset); + void removeMirroredConnection(Connection*); + void updateMirroredConnectionOffset(Connection*); + void updateMirroredConnectionDirection(Connection*, QString); + void updateMirroredConnectionMap(Connection*, QString); + void updateMirroredConnection(Connection*, QString, QString, bool isDelete = false); + Event* createNewObjectEvent(); + Event* createNewWarpEvent(); + Event* createNewHealLocationEvent(); + Event* createNewCoordScriptEvent(); + Event* createNewCoordWeatherEvent(); + Event* createNewSignEvent(); + Event* createNewHiddenItemEvent(); + Event* createNewSecretBaseEvent(); + +private slots: + void mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); + void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item); + void onConnectionMoved(Connection*); + void onConnectionItemSelected(ConnectionPixmapItem* connectionItem); + void onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem); + void onConnectionDirectionChanged(QString newDirection); + void onBorderMetatilesChanged(); + +signals: + void objectsChanged(); + void selectedObjectsChanged(); + void loadMapRequested(QString, QString); + void tilesetChanged(QString); + void warpEventDoubleClicked(QString mapName, QString warpNum); + void currentMetatilesSelectionChanged(); +}; + + + +class DraggablePixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + DraggablePixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) { + } + Editor *editor = nullptr; + Event *event = nullptr; + QGraphicsItemAnimation *pos_anim = nullptr; + DraggablePixmapItem(Event *event_, Editor *editor_) : QGraphicsPixmapItem(event_->pixmap) { + event = event_; + editor = editor_; + updatePosition(); + } + bool active; + int last_x; + int last_y; + void updatePosition() { + int x = event->getPixelX(); + int y = event->getPixelY(); + setX(x); + setY(y); + setZValue(event->y()); + } + void move(int x, int y); + void emitPositionChanged() { + emit xChanged(event->x()); + emit yChanged(event->y()); + emit elevationChanged(event->elevation()); + } + void updatePixmap() { + QList objects; + objects.append(event); + event->pixmap = QPixmap(); + editor->project->loadEventPixmaps(objects); + this->updatePosition(); + editor->redrawObject(this); + emit spriteChanged(event->pixmap); + } + void bind(QComboBox *combo, QString key) { + connect(combo, static_cast(&QComboBox::currentTextChanged), + this, [this, key](QString value){ + this->event->put(key, value); + }); + connect(this, &DraggablePixmapItem::onPropertyChanged, + this, [combo, key](QString key2, QString value){ + if (key2 == key) { + combo->addItem(value); + combo->setCurrentText(value); + } + }); + } + +signals: + void positionChanged(Event *event); + void xChanged(int); + void yChanged(int); + void elevationChanged(int); + void spriteChanged(QPixmap pixmap); + void onPropertyChanged(QString key, QString value); + +public slots: + void set_x(const QString &text) { + event->put("x", text); + updatePosition(); + } + void set_y(const QString &text) { + event->put("y", text); + updatePosition(); + } + void set_elevation(const QString &text) { + event->put("elevation", text); + updatePosition(); + } + void set_sprite(const QString &text) { + event->put("sprite", text); + updatePixmap(); + } + void set_script(const QString &text) { + event->put("script_label", text); + } + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*); +}; + +class EventGroup : public QGraphicsItemGroup { +}; + +class MapPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + MapPixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) { + } + Map *map = nullptr; + Editor *editor = nullptr; + MapPixmapItem(Map *map_, Editor *editor_) { + map = map_; + editor = editor_; + setAcceptHoverEvents(true); + } + bool active; + bool right_click; + QPoint selection_origin; + QList selection; + virtual void paint(QGraphicsSceneMouseEvent*); + virtual void floodFill(QGraphicsSceneMouseEvent*); + void _floodFill(int x, int y); + void _floodFillSmartPath(int initialX, int initialY); + virtual void pick(QGraphicsSceneMouseEvent*); + virtual void select(QGraphicsSceneMouseEvent*); + virtual void shift(QGraphicsSceneMouseEvent*); + virtual void draw(bool ignoreCache = false); + void updateMetatileSelection(QGraphicsSceneMouseEvent *event); + +private: + void updateCurHoveredTile(QPointF pos); + void paintNormal(int x, int y); + void paintSmartPath(int x, int y); + static QList smartPathTable; + +signals: + void mouseEvent(QGraphicsSceneMouseEvent *, MapPixmapItem *); + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +class CollisionPixmapItem : public MapPixmapItem { + Q_OBJECT +public: + CollisionPixmapItem(QPixmap pixmap): MapPixmapItem(pixmap) { + } + CollisionPixmapItem(Map *map_, Editor *editor_): MapPixmapItem(map_, editor_) { + } + void updateMovementPermissionSelection(QGraphicsSceneMouseEvent *event); + virtual void paint(QGraphicsSceneMouseEvent*); + virtual void floodFill(QGraphicsSceneMouseEvent*); + virtual void pick(QGraphicsSceneMouseEvent*); + virtual void draw(bool ignoreCache = false); + +signals: + void mouseEvent(QGraphicsSceneMouseEvent *, CollisionPixmapItem *); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + ConnectionPixmapItem(QPixmap pixmap, Connection* connection, int x, int y, int baseMapWidth, int baseMapHeight): QGraphicsPixmapItem(pixmap) { + this->basePixmap = pixmap; + this->connection = connection; + setFlag(ItemIsMovable); + setFlag(ItemSendsGeometryChanges); + this->initialX = x; + this->initialY = y; + this->initialOffset = connection->offset.toInt(); + this->baseMapWidth = baseMapWidth; + this->baseMapHeight = baseMapHeight; + } + void render(qreal opacity = 1) { + QPixmap newPixmap = basePixmap.copy(0, 0, basePixmap.width(), basePixmap.height()); + if (opacity < 1) { + QPainter painter(&newPixmap); + int alpha = static_cast(255 * (1 - opacity)); + painter.fillRect(0, 0, newPixmap.width(), newPixmap.height(), QColor(0, 0, 0, alpha)); + painter.end(); + } + this->setPixmap(newPixmap); + } + int getMinOffset(); + int getMaxOffset(); + QPixmap basePixmap; + Connection* connection; + int initialX; + int initialY; + int initialOffset; + int baseMapWidth; + int baseMapHeight; +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*); +signals: + void connectionItemSelected(ConnectionPixmapItem* connectionItem); + void connectionItemDoubleClicked(ConnectionPixmapItem* connectionItem); + void connectionMoved(Connection*); +}; + +class MetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + MetatilesPixmapItem(Map *map_) { + map = map_; + setAcceptHoverEvents(true); + connect(map, SIGNAL(paintTileChanged()), this, SLOT(paintTileChanged())); + } + Map* map = nullptr; + virtual void draw(); +private: + void updateSelection(QPointF pos); +protected: + virtual void updateCurHoveredMetatile(QPointF pos); +private slots: + void paintTileChanged(); +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +class BorderMetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + BorderMetatilesPixmapItem(Map *map_) { + map = map_; + setAcceptHoverEvents(true); + } + Map* map = nullptr; + virtual void draw(); +signals: + void borderMetatilesChanged(); +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); +}; + +class CurrentSelectedMetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + CurrentSelectedMetatilesPixmapItem(Map *map_) { + map = map_; + } + Map* map = nullptr; + virtual void draw(); +}; + +class MovementPermissionsPixmapItem : public MetatilesPixmapItem { + Q_OBJECT +public: + MovementPermissionsPixmapItem(Map *map_): MetatilesPixmapItem(map_) { + connect(map, SIGNAL(paintCollisionChanged(Map*)), this, SLOT(paintCollisionChanged(Map *))); + } + virtual void pick(uint16_t collision, uint16_t elevation) { + map->paint_collision = collision; + map->paint_elevation = elevation; + draw(); + } + virtual void draw() { + setPixmap(map->renderCollisionMetatiles()); + } +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); + virtual void updateCurHoveredMetatile(QPointF pos); +private slots: + void paintCollisionChanged(Map *) { + draw(); + } +}; + +#endif // EDITOR_H diff --git a/map.h b/map.h new file mode 100755 index 00000000..75e1ec37 --- /dev/null +++ b/map.h @@ -0,0 +1,234 @@ +#ifndef MAP_H +#define MAP_H + +#include "tileset.h" +#include "blockdata.h" +#include "event.h" + +#include +#include +#include +#include +#include + +class HistoryItem { +public: + Blockdata *metatiles; + int layoutWidth; + int layoutHeight; + HistoryItem(Blockdata *metatiles_, int layoutWidth_, int layoutHeight_) { + this->metatiles = metatiles_; + this->layoutWidth = layoutWidth_; + this->layoutHeight = layoutHeight_; + } + ~HistoryItem() { + if (metatiles) delete metatiles; + } +}; + +template +class History { +public: + History() { + + } + T back() { + if (head > 0) { + return history.at(--head); + } + return NULL; + } + T next() { + if (head + 1 < history.length()) { + return history.at(++head); + } + return NULL; + } + void push(T commit) { + while (head + 1 < history.length()) { + HistoryItem *item = history.last(); + history.removeLast(); + delete item; + } + if (saved > head) { + saved = -1; + } + history.append(commit); + head++; + } + T current() { + if (head < 0 || history.length() == 0) { + return NULL; + } + return history.at(head); + } + void save() { + saved = head; + } + bool isSaved() { + return saved == head; + } + +private: + QList history; + int head = -1; + int saved = -1; +}; + +class Connection { +public: + Connection() { + } +public: + QString direction; + QString offset; + QString map_name; +}; + +class MapLayout { +public: + MapLayout() {} + int index; + QString name; + QString label; + QString width; + QString height; + QString border_label; + QString border_path; + QString blockdata_label; + QString blockdata_path; + QString tileset_primary_label; + QString tileset_secondary_label; + Tileset *tileset_primary = nullptr; + Tileset *tileset_secondary = nullptr; + Blockdata* blockdata = nullptr; + QImage border_image; + QPixmap border_pixmap; + Blockdata *border = nullptr; + Blockdata *cached_blockdata = nullptr; + Blockdata *cached_collision = nullptr; + Blockdata *cached_border = nullptr; + bool has_unsaved_changes = false; +public: + static QString getNameFromLabel(QString label) { + // ASSUMPTION: strip off "_Layout" from layout label. Directories in 'data/layouts/' must be well-formed. + return label.replace(label.lastIndexOf("_Layout"), label.length(), ""); + } +}; + +class Map : public QObject +{ + Q_OBJECT +public: + explicit Map(QObject *parent = nullptr); + +public: + QString name; + QString constantName; + QString group_num; + QString layout_label; + QString events_label; + QString scripts_label; + QString connections_label; + QString song; + QString layout_id; + QString location; + QString requiresFlash; + QString isFlyable; // TODO: implement this + QString weather; + QString type; + QString unknown; + QString show_location; + QString battle_scene; + MapLayout *layout; + + bool isPersistedToFile = true; + +public: + void setName(QString mapName); + static QString mapConstantFromName(QString mapName); + static QString objectEventsLabelFromName(QString mapName); + static QString warpEventsLabelFromName(QString mapName); + static QString coordEventsLabelFromName(QString mapName); + static QString bgEventsLabelFromName(QString mapName); + int getWidth(); + int getHeight(); + uint16_t getSelectedBlockIndex(int); + int getDisplayedBlockIndex(int); + QPixmap render(bool ignoreCache); + QPixmap renderMetatiles(); + + QPixmap renderCollision(bool ignoreCache); + QImage collision_image; + QPixmap collision_pixmap; + QImage getCollisionMetatileImage(Block); + QImage getCollisionMetatileImage(int, int); + QPixmap renderCollisionMetatiles(); + + void drawSelection(int i, int w, int selectionWidth, int selectionHeight, QPainter *painter, int gridWidth); + + bool blockChanged(int, Blockdata*); + void cacheBlockdata(); + void cacheCollision(); + QImage image; + QPixmap pixmap; + QList metatile_images; + bool smart_paths_enabled = false; + int paint_metatile_initial_x; + int paint_metatile_initial_y; + int paint_tile_index; + int paint_tile_width = 1; + int paint_tile_height = 1; + int paint_tile_initial_x; + int paint_tile_initial_y; + int selected_metatiles_width; + int selected_metatiles_height; + QList *selected_metatiles = nullptr; + uint16_t paint_collision; + uint16_t paint_elevation; + + Block *getBlock(int x, int y); + void setBlock(int x, int y, Block block); + void _setBlock(int x, int y, Block block); + + void floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); + void _floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); + + History history; + void undo(); + void redo(); + void commit(); + + QList getAllEvents(); + void removeEvent(Event *event); + void addEvent(Event *event); + QMap> events; + + QList connections; + QPixmap renderConnection(Connection); + void setNewDimensionsBlockdata(int newWidth, int newHeight); + void setDimensions(int newWidth, int newHeight, bool setNewBlockData = true); + + QPixmap renderBorder(); + void cacheBorder(); + + bool hasUnsavedChanges(); + void hoveredTileChanged(int x, int y, int block); + void clearHoveredTile(); + void hoveredMetatileChanged(int block); + void clearHoveredMetatile(); + void hoveredMovementPermissionTileChanged(int collision, int elevation); + void clearHoveredMovementPermissionTile(); + void setSelectedMetatilesFromTilePicker(); + +signals: + void paintTileChanged(); + void paintCollisionChanged(Map *map); + void mapChanged(Map *map); + void mapNeedsRedrawing(); + void statusBarMessage(QString); + +public slots: +}; + +#endif // MAP_H diff --git a/porymap.pro b/porymap.pro new file mode 100755 index 00000000..ba778baf --- /dev/null +++ b/porymap.pro @@ -0,0 +1,58 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-08-31T15:19:13 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = porymap +TEMPLATE = app +RC_ICONS = resources/icons/porymap-icon-1.ico +ICON = resources/icons/porymap-icon-1.ico + + +SOURCES += main.cpp\ + mainwindow.cpp \ + project.cpp \ + map.cpp \ + blockdata.cpp \ + block.cpp \ + tileset.cpp \ + tile.cpp \ + event.cpp \ + editor.cpp \ + objectpropertiesframe.cpp \ + graphicsview.cpp \ + parseutil.cpp \ + neweventtoolbutton.cpp \ + noscrollcombobox.cpp \ + noscrollspinbox.cpp \ + heallocation.cpp \ + mapsceneeventfilter.cpp + +HEADERS += mainwindow.h \ + project.h \ + map.h \ + blockdata.h \ + block.h \ + tileset.h \ + tile.h \ + event.h \ + editor.h \ + objectpropertiesframe.h \ + graphicsview.h \ + parseutil.h \ + neweventtoolbutton.h \ + noscrollcombobox.h \ + noscrollspinbox.h \ + heallocation.h \ + mapsceneeventfilter.h + +FORMS += mainwindow.ui \ + objectpropertiesframe.ui + +RESOURCES += \ + resources/images.qrc diff --git a/src/core/map.h b/src/core/map.h index 851045e2..a77865e8 100644 --- a/src/core/map.h +++ b/src/core/map.h @@ -40,8 +40,6 @@ public: QString show_location; QString battle_scene; MapLayout *layout; - int scale_exp = 0; - double scale_base = sqrt(2); // adjust scale factor with this bool isPersistedToFile = true; QImage collision_image; QPixmap collision_pixmap; diff --git a/src/editor.cpp b/src/editor.cpp index c9d5a5ea..5dd5222b 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -3,6 +3,7 @@ #include "imageproviders.h" #include "mapconnection.h" #include "currentselectedmetatilespixmapitem.h" +#include "mapsceneeventfilter.h" #include #include #include @@ -348,7 +349,7 @@ void Editor::onHoveredMapMetatileChanged(int x, int y) { .arg(x) .arg(y) .arg(QString("%1").arg(tile, 3, 16, QChar('0')).toUpper()) - .arg(QString::number(pow(map->scale_base, map->scale_exp)))); + .arg(QString::number(pow(scale_base, scale_exp)))); } } @@ -461,8 +462,12 @@ void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixm } void Editor::displayMap() { - if (!scene) + if (!scene) { scene = new QGraphicsScene; + MapSceneEventFilter *filter = new MapSceneEventFilter(); + scene->installEventFilter(filter); + connect(filter, &MapSceneEventFilter::wheelZoom, this, &Editor::wheelZoom); + } if (map_item && scene) { scene->removeItem(map_item); diff --git a/src/editor.h b/src/editor.h index df2714d5..a9433e95 100644 --- a/src/editor.h +++ b/src/editor.h @@ -109,6 +109,9 @@ public: QString map_edit_mode; QString prev_edit_mode; + + int scale_exp = 0; + double scale_base = sqrt(2); // adjust scale factor with this void objectsView_onMousePress(QMouseEvent *event); void objectsView_onMouseMove(QMouseEvent *event); @@ -164,6 +167,7 @@ signals: void tilesetChanged(QString); void warpEventDoubleClicked(QString mapName, QString warpNum); void currentMetatilesSelectionChanged(); + void wheelZoom(int delta); }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d3a29682..f5ccda64 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -32,6 +32,7 @@ MainWindow::MainWindow(QWidget *parent) : { QCoreApplication::setOrganizationName("pret"); QCoreApplication::setApplicationName("porymap"); + QApplication::setApplicationDisplayName("porymap"); QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-1.ico")); ui->setupUi(this); @@ -50,6 +51,8 @@ MainWindow::~MainWindow() void MainWindow::initExtraShortcuts() { new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo())); + new QShortcut(QKeySequence("Ctrl+0"), this, SLOT(resetMapViewScale())); + ui->actionZoom_In->setShortcuts({QKeySequence("Ctrl++"), QKeySequence("Ctrl+=")}); } void MainWindow::initExtraSignals() { @@ -64,6 +67,7 @@ void MainWindow::initEditor() { connect(this->editor, SIGNAL(tilesetChanged(QString)), this, SLOT(onTilesetChanged(QString))); connect(this->editor, SIGNAL(warpEventDoubleClicked(QString,QString)), this, SLOT(openWarpMap(QString,QString))); connect(this->editor, SIGNAL(currentMetatilesSelectionChanged()), this, SLOT(currentMetatilesSelectionChanged())); + connect(this->editor, &Editor::wheelZoom, this, &MainWindow::scaleMapView); this->loadUserSettings(); } @@ -103,12 +107,12 @@ void MainWindow::openProject(QString dir) { if (!already_open) { editor->project = new Project; editor->project->root = dir; - setWindowTitle(editor->project->getProjectTitle() + " - porymap"); + setWindowTitle(editor->project->getProjectTitle()); loadDataStructures(); populateMapList(); setMap(getDefaultMap()); } else { - setWindowTitle(editor->project->getProjectTitle() + " - porymap"); + setWindowTitle(editor->project->getProjectTitle()); loadDataStructures(); populateMapList(); } @@ -180,7 +184,7 @@ void MainWindow::setMap(QString map_name) { redrawMapScene(); displayMapProperties(); - setWindowTitle(map_name + " - " + editor->project->getProjectTitle() + " - porymap"); + setWindowTitle(map_name + " - " + editor->project->getProjectTitle()); connect(editor->map, SIGNAL(mapChanged(Map*)), this, SLOT(onMapChanged(Map *))); connect(editor->map, SIGNAL(mapNeedsRedrawing()), this, SLOT(onMapNeedsRedrawing())); @@ -194,18 +198,24 @@ void MainWindow::redrawMapScene() editor->displayMap(); on_tabWidget_currentChanged(ui->tabWidget->currentIndex()); + double base = editor->scale_base; + double exp = editor->scale_exp; + + int width = static_cast(ceil((editor->scene->width()) * pow(base,exp))) + 2; + int height = static_cast(ceil((editor->scene->height()) * pow(base,exp))) + 2; + ui->graphicsView_Map->setScene(editor->scene); ui->graphicsView_Map->setSceneRect(editor->scene->sceneRect()); - ui->graphicsView_Map->setFixedSize(static_cast(editor->scene->width()) + 2, static_cast(editor->scene->height()) + 2); + ui->graphicsView_Map->setFixedSize(width, height); ui->graphicsView_Objects_Map->setScene(editor->scene); ui->graphicsView_Objects_Map->setSceneRect(editor->scene->sceneRect()); - ui->graphicsView_Objects_Map->setFixedSize(static_cast(editor->scene->width()) + 2, static_cast(editor->scene->height()) + 2); + ui->graphicsView_Objects_Map->setFixedSize(width, height); ui->graphicsView_Objects_Map->editor = editor; ui->graphicsView_Connections->setScene(editor->scene); ui->graphicsView_Connections->setSceneRect(editor->scene->sceneRect()); - ui->graphicsView_Connections->setFixedSize(static_cast(editor->scene->width()) + 2, static_cast(editor->scene->height()) + 2); + ui->graphicsView_Connections->setFixedSize(width, height); ui->graphicsView_Metatiles->setScene(editor->scene_metatiles); //ui->graphicsView_Metatiles->setSceneRect(editor->scene_metatiles->sceneRect()); @@ -520,7 +530,7 @@ void MainWindow::currentMetatilesSelectionChanged() void MainWindow::on_mapList_activated(const QModelIndex &index) { QVariant data = index.data(Qt::UserRole); - if (!data.isNull()) { + if (index.data(MapListUserRoles::TypeRole) == "map_name" && !data.isNull()) { setMap(data.toString()); } } @@ -665,19 +675,33 @@ void MainWindow::on_actionMap_Shift_triggered() } void MainWindow::scaleMapView(int s) { - editor->map->scale_exp += s; + if ((editor->scale_exp + s) <= 5 && (editor->scale_exp + s) >= -2) // sane limits + { + if (s == 0) + { + s = -editor->scale_exp; + } - double base = editor->map->scale_base; - double exp = editor->map->scale_exp; - double sfactor = pow(base,s); + editor->scale_exp += s; - ui->graphicsView_Map->scale(sfactor,sfactor); - ui->graphicsView_Objects_Map->scale(sfactor,sfactor); + double base = editor->scale_base; + double exp = editor->scale_exp; + double sfactor = pow(base,s); - int width = static_cast((editor->scene->width() + 2) * pow(base,exp)); - int height = static_cast((editor->scene->height() + 2) * pow(base,exp)); - ui->graphicsView_Map->setFixedSize(width, height); - ui->graphicsView_Objects_Map->setFixedSize(width, height); + ui->graphicsView_Map->scale(sfactor,sfactor); + ui->graphicsView_Objects_Map->scale(sfactor,sfactor); + ui->graphicsView_Connections->scale(sfactor,sfactor); + + int width = static_cast(ceil((editor->scene->width()) * pow(base,exp))) + 2; + int height = static_cast(ceil((editor->scene->height()) * pow(base,exp))) + 2; + ui->graphicsView_Map->setFixedSize(width, height); + ui->graphicsView_Objects_Map->setFixedSize(width, height); + ui->graphicsView_Connections->setFixedSize(width, height); + } +} + +void MainWindow::resetMapViewScale() { + scaleMapView(0); } void MainWindow::addNewEvent(QString event_type) @@ -981,7 +1005,7 @@ void MainWindow::on_toolButton_Paint_clicked() { editor->map_edit_mode = "paint"; editor->settings->mapCursor = QCursor(QPixmap(":/icons/pencil_cursor.ico"), 10, 10); - + ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); QScroller::ungrabGesture(ui->scrollArea); @@ -993,7 +1017,7 @@ void MainWindow::on_toolButton_Select_clicked() { editor->map_edit_mode = "select"; editor->settings->mapCursor = QCursor(QPixmap(":/icons/cursor.ico"), 0, 0); - + ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); QScroller::ungrabGesture(ui->scrollArea); @@ -1005,7 +1029,7 @@ void MainWindow::on_toolButton_Fill_clicked() { editor->map_edit_mode = "fill"; editor->settings->mapCursor = QCursor(QPixmap(":/icons/fill_color_cursor.ico"), 10, 10); - + ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); QScroller::ungrabGesture(ui->scrollArea); @@ -1029,11 +1053,11 @@ void MainWindow::on_toolButton_Move_clicked() { editor->map_edit_mode = "move"; editor->settings->mapCursor = QCursor(QPixmap(":/icons/move.ico"), 7, 7); - + ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QScroller::grabGesture(ui->scrollArea, QScroller::LeftMouseButtonGesture); - + checkToolButtons(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 9b39debe..b836b744 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -25,6 +25,9 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); +public slots: + void scaleMapView(int); + private slots: void on_action_Open_Project_triggered(); void on_mapList_activated(const QModelIndex &index); @@ -118,6 +121,8 @@ private slots: void on_checkBox_ToggleBorder_stateChanged(int arg1); + void resetMapViewScale(); + private: Ui::MainWindow *ui; QStandardItemModel *mapListModel; @@ -141,7 +146,6 @@ private: void displayMapProperties(); void checkToolButtons(); - void scaleMapView(int); void initExtraShortcuts(); void initExtraSignals(); void initEditor(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index a01fb7c4..b42acbaf 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -2195,7 +2195,7 @@ Zoom In - + + Ctrl++ @@ -2203,7 +2203,7 @@ Zoom Out - - + Ctrl+- diff --git a/src/porymap.pro b/src/porymap.pro index 7589cc75..2bfd2dcc 100644 --- a/src/porymap.pro +++ b/src/porymap.pro @@ -43,7 +43,8 @@ SOURCES += core/block.cpp \ main.cpp \ mainwindow.cpp \ project.cpp \ - settings.cpp + settings.cpp \ + ui/mapsceneeventfilter.cpp HEADERS += core/block.h \ core/blockdata.h \ @@ -75,7 +76,8 @@ HEADERS += core/block.h \ editor.h \ mainwindow.h \ project.h \ - settings.h + settings.h \ + ui/mapsceneeventfilter.h FORMS += mainwindow.ui \ eventpropertiesframe.ui diff --git a/src/ui/imageproviders.cpp b/src/ui/imageproviders.cpp index f4d45ae8..d5873800 100644 --- a/src/ui/imageproviders.cpp +++ b/src/ui/imageproviders.cpp @@ -38,7 +38,11 @@ QImage getMetatileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTil if (tile_image.isNull()) { // Some metatiles specify tiles that are outside the valid range. // These are treated as completely transparent, so they can be skipped without - // being drawn. + // being drawn unless they're on the bottom layer, in which case we need + // a placeholder because garbage will be drawn otherwise. + if (layer == 0) { + metatile_painter.fillRect(x * 8, y * 8, 8, 8, palettes.value(0).value(0)); + } continue; } diff --git a/src/ui/mapsceneeventfilter.cpp b/src/ui/mapsceneeventfilter.cpp new file mode 100644 index 00000000..4c53e614 --- /dev/null +++ b/src/ui/mapsceneeventfilter.cpp @@ -0,0 +1,23 @@ +#include "mapsceneeventfilter.h" +#include +#include + +MapSceneEventFilter::MapSceneEventFilter(QObject *parent) : QObject(parent) +{ + +} + +bool MapSceneEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::GraphicsSceneWheel) + { + QGraphicsSceneWheelEvent *wheelEvent = static_cast(event); + if (wheelEvent->modifiers() & Qt::ControlModifier) + { + emit wheelZoom(wheelEvent->delta() > 0 ? 1 : -1); + event->accept(); + return true; + } + } + return false; +} diff --git a/src/ui/mapsceneeventfilter.h b/src/ui/mapsceneeventfilter.h new file mode 100644 index 00000000..7de427e3 --- /dev/null +++ b/src/ui/mapsceneeventfilter.h @@ -0,0 +1,19 @@ +#ifndef MAPSCENEEVENTFILTER_H +#define MAPSCENEEVENTFILTER_H + +#include + +class MapSceneEventFilter : public QObject +{ + Q_OBJECT +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +public: + explicit MapSceneEventFilter(QObject *parent = nullptr); + +signals: + void wheelZoom(int delta); +public slots: +}; + +#endif // MAPSCENEEVENTFILTER_H diff --git a/tileset.cpp b/tileset.cpp new file mode 100755 index 00000000..3a03ca30 --- /dev/null +++ b/tileset.cpp @@ -0,0 +1,120 @@ +#include "tileset.h" +#include "project.h" + +#include +#include +#include + +Tileset::Tileset() +{ + +} + +Metatile::Metatile() +{ + tiles = new QList; +} + +QImage Metatile::getMetatileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTileset) { + QImage metatile_image(16, 16, QImage::Format_RGBA8888); + + Metatile* metatile = Metatile::getMetatile(tile, primaryTileset, secondaryTileset); + if (!metatile || !metatile->tiles) { + metatile_image.fill(0xffffffff); + return metatile_image; + } + + Tileset* blockTileset = Metatile::getBlockTileset(tile, primaryTileset, secondaryTileset); + if (!blockTileset) { + metatile_image.fill(0xffffffff); + return metatile_image; + } + QList> palettes = Metatile::getBlockPalettes(primaryTileset, secondaryTileset); + + QPainter metatile_painter(&metatile_image); + for (int layer = 0; layer < 2; layer++) + for (int y = 0; y < 2; y++) + for (int x = 0; x < 2; x++) { + Tile tile_ = metatile->tiles->value((y * 2) + x + (layer * 4)); + QImage tile_image = Metatile::getMetatileTile(tile_.tile, primaryTileset, secondaryTileset); + if (tile_image.isNull()) { + // Some metatiles specify tiles that are outside the valid range. + // These are treated as completely transparent, so they can be skipped without + // being drawn unless they're on the bottom layer, in which case we need + // a placeholder. + if (layer == 0) { + metatile_painter.fillRect(x * 8, y * 8, 8, 8, palettes.value(0).value(0)); + } + continue; + } + + // Colorize the metatile tiles with its palette. + if (tile_.palette < palettes.length()) { + QList palette = palettes.value(tile_.palette); + for (int j = 0; j < palette.length(); j++) { + tile_image.setColor(j, palette.value(j)); + } + } else { + qDebug() << "Tile is referring to invalid palette number: " << tile_.palette; + } + + // The top layer of the metatile has its first color displayed at transparent. + if (layer > 0) { + QColor color(tile_image.color(0)); + color.setAlpha(0); + tile_image.setColor(0, color.rgba()); + } + + QPoint origin = QPoint(x*8, y*8); + metatile_painter.drawImage(origin, tile_image.mirrored(tile_.xflip == 1, tile_.yflip == 1)); + } + metatile_painter.end(); + + return metatile_image; +} + +Metatile* Metatile::getMetatile(int index, Tileset *primaryTileset, Tileset *secondaryTileset) { + Tileset *tileset = Metatile::getBlockTileset(index, primaryTileset, secondaryTileset); + int local_index = Metatile::getBlockIndex(index); + if (!tileset || !tileset->metatiles) { + return nullptr; + } + Metatile *metatile = tileset->metatiles->value(local_index, nullptr); + return metatile; +} + +QImage Metatile::getMetatileTile(int tile, Tileset *primaryTileset, Tileset *secondaryTileset) { + Tileset *tileset = Metatile::getBlockTileset(tile, primaryTileset, secondaryTileset); + int local_index = Metatile::getBlockIndex(tile); + if (!tileset || !tileset->tiles) { + return QImage(); + } + return tileset->tiles->value(local_index, QImage()); +} + +Tileset* Metatile::getBlockTileset(int metatile_index, Tileset *primaryTileset, Tileset *secondaryTileset) { + if (metatile_index < Project::getNumMetatilesPrimary()) { + return primaryTileset; + } else { + return secondaryTileset; + } +} + +int Metatile::getBlockIndex(int index) { + if (index < Project::getNumMetatilesPrimary()) { + return index; + } else { + return index - Project::getNumMetatilesPrimary(); + } +} + +QList> Metatile::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset) { + QList> palettes; + for (int i = 0; i < Project::getNumPalettesPrimary(); i++) { + palettes.append(primaryTileset->palettes->at(i)); + } + for (int i = Project::getNumPalettesPrimary(); i < Project::getNumPalettesTotal(); i++) { + palettes.append(secondaryTileset->palettes->at(i)); + } + return palettes; +}