diff --git a/editor.h b/editor.h deleted file mode 100755 index eca36d5e..00000000 --- a/editor.h +++ /dev/null @@ -1,437 +0,0 @@ -#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; - - 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/objectpropertiesframe.ui b/forms/eventpropertiesframe.ui old mode 100755 new mode 100644 similarity index 95% rename from objectpropertiesframe.ui rename to forms/eventpropertiesframe.ui index 5ff1fa32..5007d951 --- a/objectpropertiesframe.ui +++ b/forms/eventpropertiesframe.ui @@ -1,286 +1,286 @@ - - - ObjectPropertiesFrame - - - - 0 - 0 - 284 - 146 - - - - - 0 - 0 - - - - - 284 - 90 - - - - Frame - - - Qt::LeftToRight - - - QFrame::Box - - - QFrame::Raised - - - 1 - - - - - - - 0 - 0 - - - - - - - - 0 - 0 - - - - - 64 - 64 - - - - QFrame::Box - - - QFrame::Sunken - - - - - - false - - - Qt::AlignCenter - - - -1 - - - - - - - - - Object 1 - - - - - - - - - - - X - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::StrongFocus - - - <html><head/><body><p>The X coordinate of this object.</p></body></html> - - - -32768 - - - 32767 - - - - - - - - - - - Y - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::StrongFocus - - - <html><head/><body><p>The Y coordinate of this object.</p></body></html> - - - -32768 - - - 32767 - - - - - - - - - - - Z - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::StrongFocus - - - <html><head/><body><p>The elevation of this object.</p></body></html> - - - 15 - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - 0 - 0 - - - - - 9 - - - 0 - - - 0 - - - - - Sprite - - - comboBox_sprite - - - - - - - true - - - - 0 - 0 - - - - Qt::StrongFocus - - - <html><head/><body><p>The sprite graphics to use for this object.</p></body></html> - - - true - - - - - - 25 - - - QComboBox::AdjustToContentsOnFirstShow - - - - - - - - - - - NoScrollComboBox - QComboBox -
noscrollcombobox.h
-
- - NoScrollSpinBox - QSpinBox -
noscrollspinbox.h
-
-
- - spinBox_x - spinBox_y - spinBox_z - comboBox_sprite - - - -
+ + + EventPropertiesFrame + + + + 0 + 0 + 284 + 146 + + + + + 0 + 0 + + + + + 284 + 90 + + + + Frame + + + Qt::LeftToRight + + + QFrame::Box + + + QFrame::Raised + + + 1 + + + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + + 64 + 64 + + + + QFrame::Box + + + QFrame::Sunken + + + + + + false + + + Qt::AlignCenter + + + -1 + + + + + + + + + Object 1 + + + + + + + + + + + X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::StrongFocus + + + <html><head/><body><p>The X coordinate of this object.</p></body></html> + + + -32768 + + + 32767 + + + + + + + + + + + Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::StrongFocus + + + <html><head/><body><p>The Y coordinate of this object.</p></body></html> + + + -32768 + + + 32767 + + + + + + + + + + + Z + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::StrongFocus + + + <html><head/><body><p>The elevation of this object.</p></body></html> + + + 15 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + 0 + 0 + + + + + 9 + + + 0 + + + 0 + + + + + Sprite + + + comboBox_sprite + + + + + + + true + + + + 0 + 0 + + + + Qt::StrongFocus + + + <html><head/><body><p>The sprite graphics to use for this object.</p></body></html> + + + true + + + + + + 25 + + + QComboBox::AdjustToContentsOnFirstShow + + + + + + + + + + + NoScrollComboBox + QComboBox +
noscrollcombobox.h
+
+ + NoScrollSpinBox + QSpinBox +
noscrollspinbox.h
+
+
+ + spinBox_x + spinBox_y + spinBox_z + comboBox_sprite + + + +
diff --git a/mainwindow.ui b/forms/mainwindow.ui old mode 100755 new mode 100644 similarity index 96% rename from mainwindow.ui rename to forms/mainwindow.ui index 36600ee5..4c4a0277 --- a/mainwindow.ui +++ b/forms/mainwindow.ui @@ -1,2294 +1,2301 @@ - - - MainWindow - - - - 0 - 0 - 1117 - 747 - - - - - 0 - 0 - - - - porymap - - - - - - - Qt::Horizontal - - - - - 0 - 0 - - - - - 100 - 0 - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectItems - - - false - - - - - true - - - - 1 - 0 - - - - 0 - - - false - - - false - - - - - - - - :/icons/map.ico:/icons/map.ico - - - Map - - - <html><head/><body><p>Edit the map layout.</p><p>Select metatiles or collision attributes from the right panel, and paint them onto the map.</p></body></html> - - - - 0 - - - 0 - - - 0 - - - 0 - - - 6 - - - - - Qt::Horizontal - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 1 - - - - 3 - - - 3 - - - 3 - - - 3 - - - 6 - - - - - - 0 - 0 - - - - - 0 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - true - - - <html><head/><body><p>Pencil</p><p><span style=" font-weight:600;">Click</span> and drag to draw on the map.</p><p><span style=" font-weight:600;">Right-click</span> and drag to select tiles.</p></body></html> - - - Paint - - - - :/icons/pencil.ico:/icons/pencil.ico - - - true - - - true - - - false - - - - - - - true - - - <html><head/><body><p>Pointer</p><p>Does nothing</p></body></html> - - - Select - - - - :/icons/cursor.ico:/icons/cursor.ico - - - true - - - - - - - <html><head/><body><p>Bucket Fill</p><p>Fills all similar tiles in a region with the selected metatiles or collision attributes</p></body></html> - - - Fill - - - - :/icons/fill_color.ico:/icons/fill_color.ico - - - true - - - - - - - <html><head/><body><p>Eyedropper</p><p><span style=" font-weight:600;">Click</span> to select a metatile or collision attribute.</p></body></html> - - - Dropper - - - - :/icons/pipette.ico:/icons/pipette.ico - - - true - - - - - - - <html><head/><body><p>Move</p><p>Click to drag map around.</p></body></html> - - - ... - - - - :/icons/move.ico:/icons/move.ico - - - true - - - - - - - <html><head/><body><p>Map Shift</p><p><span style=" font-weight:600;">Click and drag</span> on the map to shift the positions all metatiles at once. This is useful after resizing a map.</p></body></html> - - - Shift - - - - :/icons/shift.ico:/icons/shift.ico - - - true - - - - - - - <html><head/><body><p>Smart-path mode allows easier drawing of paths. If a 3x3 metatile block is selcted in the right panel, then smart path mode will automatically form a pathway using those selected blocks.</p><p>When smart-path mode is <span style=" font-weight:600;">not</span> enabled, clicking and dragging a selection will tile it in a grid.</p><p>Hold down the <span style=" font-weight:600;">shift</span> key while editing to quickly enable smart-path mode.</p></body></html> - - - margin-left: 10px - - - Smart Paths - - - - - - - <html><head/><body><p>Toggles a grid over the map's metatile boundaries.</p></body></html> - - - - - - Grid - - - - - - - Border - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - <html><head/><body><p>Change a map layout's width and height.</p></body></html> - - - Change Dimensions - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 1 - 0 - - - - true - - - - - 0 - 0 - 469 - 608 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - false - - - false - - - QAbstractScrollArea::AdjustIgnored - - - QGraphicsView::NoDrag - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 3 - - - 3 - - - 3 - - - 3 - - - - - true - - - - 0 - 0 - - - - - 156 - 0 - - - - 0 - - - - - 0 - 0 - - - - Metatiles - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Primary Tileset - - - - - - - Qt::StrongFocus - - - <html><head/><body><p>Primary Tileset</p><p>Defines the first 0x200 metatiles available for the map.</p></body></html> - - - true - - - - - - - Secondary Tileset - - - - - - - Qt::StrongFocus - - - <html><head/><body><p>Secondary Tileset</p><p>Defines the second 0x200 metatiles available for the map.</p></body></html> - - - true - - - - - - - - - - - 0 - 0 - - - - - 0 - 92 - - - - - 16777215 - 92 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - QLayout::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Selection - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - true - - - - - 0 - 0 - 324 - 77 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAsNeeded - - - QAbstractScrollArea::AdjustIgnored - - - true - - - Qt::AlignHCenter|Qt::AlignTop - - - - true - - - - 0 - 0 - 307 - 387 - - - - - 0 - 0 - - - - - QLayout::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - true - - - - 0 - 0 - - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustIgnored - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Border - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 48 - - - - <html><head/><body><p>The border is a 2x2 metatile which is repeated outside of the map layout's boundary. Draw on this border area to modify it.</p></body></html> - - - QFrame::StyledPanel - - - QFrame::Sunken - - - Qt::ScrollBarAsNeeded - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - true - - - - 0 - 0 - - - - Collision - - - - QLayout::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 64 - 512 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - true - - - Events - - - <html><head/><body><p>Edit the map's events.</p><p>View and modify objects, warps, signs, etc.</p></body></html> - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Qt::Horizontal - - - - - 1 - 0 - - - - true - - - - - 0 - 0 - 381 - 657 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - - 0 - 0 - - - - - - - - - - true - - - - 0 - 0 - - - - - 444 - 0 - - - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Selected - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QAbstractScrollArea::AdjustIgnored - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - 0 - 0 - 432 - 596 - - - - - 0 - 0 - - - - - - - - - - - - - - - - 16777215 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 40 - 32 - - - - <html><head/><body><p>Add a new event to the map.</p></body></html> - - - New Object - - - - :/icons/add.ico:/icons/add.ico - - - Qt::ToolButtonTextBesideIcon - - - - - - - - 0 - 0 - - - - - 40 - 32 - - - - <html><head/><body><p>Delete the selected event from the map.</p></body></html> - - - Delete - - - - :/icons/delete.ico:/icons/delete.ico - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Open Map Scripts - - - false - - - - - - - - - - - - - - - Header - - - - false - - - - 10 - 10 - 381 - 311 - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 12 - - - - - Song - - - - - - - <html><head/><body><p>The default background music for this map.</p></body></html> - - - true - - - - - - - Location - - - - - - - <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is display when the player enters it.</p></body></html> - - - true - - - - - - - Requires Flash - - - - - - - Weather - - - - - - - <html><head/><body><p>The default weather for this map.</p></body></html> - - - true - - - - - - - Type - - - - - - - <html><head/><body><p>The map type is a general attribute, which is used for many different things. For example. it determines whether biking or running is allowed.</p></body></html> - - - true - - - - - - - Show Location Name - - - - - - - <html><head/><body><p>Whether or not to display the location name when the player enters the map.</p></body></html> - - - - - - - - - - Battle scene - - - - - - - <html><head/><body><p>Determines the type of battle scene graphics to use.</p></body></html> - - - true - - - - - - - <html><head/><body><p>Whether or not the map is dark and requires Flash to illuminate.</p></body></html> - - - - - - - - - - - - Connections - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 0 - 0 - - - - <html><head/><body><p>Add a new connection.</p></body></html> - - - - - - - :/icons/add.ico - - - - - - - - <html><head/><body><p>Remove the currently-selected connection.</p></body></html> - - - - - - - :/icons/delete.ico - - - - - - - - Number of Connections: - - - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - <html><head/><body><p>If enabled, connections will automatically be updated on the connected map.</p></body></html> - - - Mirror to Connecting Maps - - - true - - - - - - - - - - - 0 - 0 - - - - - 0 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 6 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - Map - - - - - - - <html><head/><body><p>The destination map name of the connection.</p></body></html> - - - true - - - - - - - Offset - - - - - - - <html><head/><body><p>The number of metatiles to offset the connection.</p></body></html> - - - -999 - - - 999 - - - - - - - <html><head/><body><p>The direction of the connection.</p></body></html> - - - - up - - - - - right - - - - - down - - - - - left - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 1 - 0 - - - - true - - - - - 0 - 0 - 826 - 557 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 0 - 0 - 0 - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - Dive Map - - - - - - - <html><head/><body><p>Destination map name when using <span style=" font-weight:600;">Dive</span>. If empty, no such connection will exist.</p></body></html> - - - true - - - - - - - Emerge Map - - - - - - - <html><head/><body><p>Destination map name when emerging using <span style=" font-weight:600;">Dive</span>. If empty, no such connection will exist.</p></body></html> - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 1117 - 21 - - - - - File - - - - - - - - - - - - Edit - - - - - - - View - - - - - - - - Tools - - - - - - - - - - - - - - - - - Save All - - - Ctrl+Shift+S - - - - - Exit - - - Ctrl+Q - - - - - Open Project... - - - Ctrl+O - - - - - Save - - - Ctrl+S - - - - - Undo - - - Ctrl+Z - - - - - Redo - - - - - - Ctrl+Y - - - - - Export Map Image... - - - - - Zoom In - - - + - - - - - Zoom Out - - - - - - - - - true - - - true - - - Cursor Icons - - - <html><head/><body><p>Use reticule-styled cursors with icon showing currently selected tool.</p></body></html> - - - - - Pencil - - - N - - - - - Bucket Fill - - - B - - - - - Eyedropper - - - E - - - - - Move - - - M - - - - - Map Shift - - - S - - - - - Pointer - - - P - - - - - - - GraphicsView - QGraphicsView -
graphicsview.h
-
- - NewEventToolButton - QToolButton -
neweventtoolbutton.h
-
- - NoScrollComboBox - QComboBox -
noscrollcombobox.h
-
-
- - - - -
+ + + MainWindow + + + + 0 + 0 + 1117 + 747 + + + + + 0 + 0 + + + + porymap + + + + + + + Qt::Horizontal + + + + + 0 + 0 + + + + + 100 + 0 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectItems + + + false + + + + + true + + + + 1 + 0 + + + + 0 + + + false + + + false + + + + + + + + :/icons/map.ico:/icons/map.ico + + + Map + + + <html><head/><body><p>Edit the map layout.</p><p>Select metatiles or collision attributes from the right panel, and paint them onto the map.</p></body></html> + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + Qt::Horizontal + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 1 + + + + 3 + + + 3 + + + 3 + + + 3 + + + 6 + + + + + + 0 + 0 + + + + + 0 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + true + + + <html><head/><body><p>Pencil</p><p><span style=" font-weight:600;">Click</span> and drag to draw on the map.</p><p><span style=" font-weight:600;">Right-click</span> and drag to select tiles.</p></body></html> + + + Paint + + + + :/icons/pencil.ico:/icons/pencil.ico + + + true + + + true + + + false + + + + + + + true + + + <html><head/><body><p>Pointer</p><p>Does nothing</p></body></html> + + + Select + + + + :/icons/cursor.ico:/icons/cursor.ico + + + true + + + + + + + <html><head/><body><p>Bucket Fill</p><p>Fills all similar tiles in a region with the selected metatiles or collision attributes</p></body></html> + + + Fill + + + + :/icons/fill_color.ico:/icons/fill_color.ico + + + true + + + + + + + <html><head/><body><p>Eyedropper</p><p><span style=" font-weight:600;">Click</span> to select a metatile or collision attribute.</p></body></html> + + + Dropper + + + + :/icons/pipette.ico:/icons/pipette.ico + + + true + + + + + + + <html><head/><body><p>Move</p><p>Click to drag map around.</p></body></html> + + + ... + + + + :/icons/move.ico:/icons/move.ico + + + true + + + + + + + <html><head/><body><p>Map Shift</p><p><span style=" font-weight:600;">Click and drag</span> on the map to shift the positions all metatiles at once. This is useful after resizing a map.</p></body></html> + + + Shift + + + + :/icons/shift.ico:/icons/shift.ico + + + true + + + + + + + <html><head/><body><p>Smart-path mode allows easier drawing of paths. If a 3x3 metatile block is selcted in the right panel, then smart path mode will automatically form a pathway using those selected blocks.</p><p>When smart-path mode is <span style=" font-weight:600;">not</span> enabled, clicking and dragging a selection will tile it in a grid.</p><p>Hold down the <span style=" font-weight:600;">shift</span> key while editing to quickly enable smart-path mode.</p></body></html> + + + margin-left: 10px + + + Smart Paths + + + + + + + <html><head/><body><p>Toggles a grid over the map's metatile boundaries.</p></body></html> + + + + + + Grid + + + + + + + Border + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p>Change a map layout's width and height.</p></body></html> + + + Change Dimensions + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 469 + 608 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + false + + + false + + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + true + + + + 0 + 0 + + + + + 156 + 0 + + + + 0 + + + + + 0 + 0 + + + + Metatiles + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Primary Tileset + + + + + + + Qt::StrongFocus + + + <html><head/><body><p>Primary Tileset</p><p>Defines the first 0x200 metatiles available for the map.</p></body></html> + + + true + + + + + + + Secondary Tileset + + + + + + + Qt::StrongFocus + + + <html><head/><body><p>Secondary Tileset</p><p>Defines the second 0x200 metatiles available for the map.</p></body></html> + + + true + + + + + + + + + + + 0 + 0 + + + + + 0 + 92 + + + + + 16777215 + 92 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Selection + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + + + 0 + 0 + 324 + 77 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + true + + + Qt::AlignHCenter|Qt::AlignTop + + + + true + + + + 0 + 0 + 307 + 387 + + + + + 0 + 0 + + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustIgnored + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Border + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 48 + + + + <html><head/><body><p>The border is a 2x2 metatile which is repeated outside of the map layout's boundary. Draw on this border area to modify it.</p></body></html> + + + QFrame::StyledPanel + + + QFrame::Sunken + + + Qt::ScrollBarAsNeeded + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + true + + + + 0 + 0 + + + + Collision + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 64 + 512 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + true + + + Events + + + <html><head/><body><p>Edit the map's events.</p><p>View and modify objects, warps, signs, etc.</p></body></html> + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 381 + 657 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + 0 + 0 + + + + + + + + + + true + + + + 0 + 0 + + + + + 444 + 0 + + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Selected + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QAbstractScrollArea::AdjustIgnored + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 432 + 596 + + + + + 0 + 0 + + + + + + + + + + + + + + + + 16777215 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 40 + 32 + + + + <html><head/><body><p>Add a new event to the map.</p></body></html> + + + New Object + + + + :/icons/add.ico:/icons/add.ico + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + + 40 + 32 + + + + <html><head/><body><p>Delete the selected event from the map.</p></body></html> + + + Delete + + + + :/icons/delete.ico:/icons/delete.ico + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Open Map Scripts + + + false + + + + + + + + + + + + + + + Header + + + + false + + + + 10 + 10 + 381 + 311 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 12 + + + + + Song + + + + + + + <html><head/><body><p>The default background music for this map.</p></body></html> + + + true + + + + + + + Location + + + + + + + <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is display when the player enters it.</p></body></html> + + + true + + + + + + + Requires Flash + + + + + + + Weather + + + + + + + <html><head/><body><p>The default weather for this map.</p></body></html> + + + true + + + + + + + Type + + + + + + + <html><head/><body><p>The map type is a general attribute, which is used for many different things. For example. it determines whether biking or running is allowed.</p></body></html> + + + true + + + + + + + Show Location Name + + + + + + + <html><head/><body><p>Whether or not to display the location name when the player enters the map.</p></body></html> + + + + + + + + + + Battle scene + + + + + + + <html><head/><body><p>Determines the type of battle scene graphics to use.</p></body></html> + + + true + + + + + + + <html><head/><body><p>Whether or not the map is dark and requires Flash to illuminate.</p></body></html> + + + + + + + + + + + + Connections + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 0 + + + + <html><head/><body><p>Add a new connection.</p></body></html> + + + + + + + :/icons/add.ico + + + + + + + + <html><head/><body><p>Remove the currently-selected connection.</p></body></html> + + + + + + + :/icons/delete.ico + + + + + + + + Number of Connections: + + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>If enabled, connections will automatically be updated on the connected map.</p></body></html> + + + Mirror to Connecting Maps + + + true + + + + + + + + + + + 0 + 0 + + + + + 0 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 6 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Map + + + + + + + <html><head/><body><p>The destination map name of the connection.</p></body></html> + + + true + + + + + + + Offset + + + + + + + <html><head/><body><p>The number of metatiles to offset the connection.</p></body></html> + + + -999 + + + 999 + + + + + + + <html><head/><body><p>The direction of the connection.</p></body></html> + + + + up + + + + + right + + + + + down + + + + + left + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 826 + 557 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Dive Map + + + + + + + <html><head/><body><p>Destination map name when using <span style=" font-weight:600;">Dive</span>. If empty, no such connection will exist.</p></body></html> + + + true + + + + + + + Emerge Map + + + + + + + <html><head/><body><p>Destination map name when emerging using <span style=" font-weight:600;">Dive</span>. If empty, no such connection will exist.</p></body></html> + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1117 + 21 + + + + + File + + + + + + + + + + + + Edit + + + + + + + View + + + + + + + + Tools + + + + + + + + + + + + + + + + + + + Save All + + + Ctrl+Shift+S + + + + + Exit + + + Ctrl+Q + + + + + Open Project... + + + Ctrl+O + + + + + Save + + + Ctrl+S + + + + + Undo + + + Ctrl+Z + + + + + Redo + + + + + + Ctrl+Y + + + + + Export Map Image... + + + + + Zoom In + + + Ctrl++ + + + + + Zoom Out + + + Ctrl+- + + + + + true + + + true + + + Cursor Icons + + + <html><head/><body><p>Use reticule-styled cursors with icon showing currently selected tool.</p></body></html> + + + + + Pencil + + + N + + + + + Bucket Fill + + + B + + + + + Eyedropper + + + E + + + + + Move + + + M + + + + + Map Shift + + + S + + + + + Pointer + + + P + + + + + Tileset Editor + + + + + + + GraphicsView + QGraphicsView +
graphicsview.h
+
+ + NewEventToolButton + QToolButton +
neweventtoolbutton.h
+
+ + NoScrollComboBox + QComboBox +
noscrollcombobox.h
+
+
+ + + + +
diff --git a/forms/paletteeditor.ui b/forms/paletteeditor.ui new file mode 100644 index 00000000..a8b18de4 --- /dev/null +++ b/forms/paletteeditor.ui @@ -0,0 +1,1536 @@ + + + PaletteEditor + + + + 0 + 0 + 817 + 739 + + + + MainWindow + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Palette + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Color 4 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 5 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 7 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 6 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 10 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 8 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 9 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 11 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 12 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 14 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 13 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 15 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 0 + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + 31 + + + Qt::Horizontal + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + Color 1 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + 31 + + + Qt::Horizontal + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 2 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + Color 3 + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Red + + + + + + + 31 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + + + + + Green + + + + + + + Blue + + + + + + + 31 + + + Qt::Horizontal + + + + + + + 31 + + + Qt::Horizontal + + + + + + + + + + + + + + + 0 + 0 + 817 + 21 + + + + + + + + diff --git a/forms/tileseteditor.ui b/forms/tileseteditor.ui new file mode 100644 index 00000000..f8b6947f --- /dev/null +++ b/forms/tileseteditor.ui @@ -0,0 +1,445 @@ + + + TilesetEditor + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + true + + + + + 0 + 0 + 386 + 539 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Tile Properties + + + false + + + + + + Palette + + + + + + + + + + X Flip + + + + + + + Y Flip + + + + + + + + 0 + 0 + + + + Selection + + + + + + + Qt::LeftToRight + + + + + + + + + + + + + + + + + + 16 + 16 + + + + + 32 + 32 + + + + QFrame::StyledPanel + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + + + + + + + Metatile Properties + + + false + + + false + + + + + + Bottom/Top + + + + + + + + 64 + 32 + + + + + 64 + 32 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + + + + Metatile Behavior + + + + + + + + + + Layer Type + + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 386 + 359 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + File + + + + + + + Tools + + + + + + + + + + + + + Save Tileset + + + Ctrl+S + + + + + Import Primary Tiles + + + + + Import Secondary Tiles + + + + + Change Number of Metatiles + + + + + Change Palettes + + + + + + diff --git a/block.h b/include/core/block.h old mode 100755 new mode 100644 similarity index 93% rename from block.h rename to include/core/block.h index 8b3943d5..b31f18b4 --- a/block.h +++ b/include/core/block.h @@ -1,20 +1,20 @@ -#ifndef BLOCK_H -#define BLOCK_H - -#include - -class Block -{ -public: - Block(); - Block(uint16_t); - Block(const Block&); - bool operator ==(Block); - bool operator !=(Block); - uint16_t tile:10; - uint16_t collision:2; - uint16_t elevation:4; - uint16_t rawValue(); -}; - -#endif // BLOCK_H +#ifndef BLOCK_H +#define BLOCK_H + +#include + +class Block +{ +public: + Block(); + Block(uint16_t); + Block(const Block&); + bool operator ==(Block); + bool operator !=(Block); + uint16_t tile:10; + uint16_t collision:2; + uint16_t elevation:4; + uint16_t rawValue(); +}; + +#endif // BLOCK_H diff --git a/blockdata.h b/include/core/blockdata.h old mode 100755 new mode 100644 similarity index 94% rename from blockdata.h rename to include/core/blockdata.h index 8d87eaa4..0bb6a7b6 --- a/blockdata.h +++ b/include/core/blockdata.h @@ -1,32 +1,32 @@ -#ifndef BLOCKDATA_H -#define BLOCKDATA_H - -#include "block.h" - -#include -#include - -class Blockdata : public QObject -{ - Q_OBJECT -public: - explicit Blockdata(QObject *parent = nullptr); - ~Blockdata() { - if (blocks) delete blocks; - } - -public: - QList *blocks = nullptr; - void addBlock(uint16_t); - void addBlock(Block); - QByteArray serialize(); - void copyFrom(Blockdata*); - Blockdata* copy(); - bool equals(Blockdata *); - -signals: - -public slots: -}; - -#endif // BLOCKDATA_H +#ifndef BLOCKDATA_H +#define BLOCKDATA_H + +#include "block.h" + +#include +#include + +class Blockdata : public QObject +{ + Q_OBJECT +public: + explicit Blockdata(QObject *parent = nullptr); + ~Blockdata() { + if (blocks) delete blocks; + } + +public: + QList *blocks = nullptr; + void addBlock(uint16_t); + void addBlock(Block); + QByteArray serialize(); + void copyFrom(Blockdata*); + Blockdata* copy(); + bool equals(Blockdata *); + +signals: + +public slots: +}; + +#endif // BLOCKDATA_H diff --git a/event.h b/include/core/event.h old mode 100755 new mode 100644 similarity index 93% rename from event.h rename to include/core/event.h index fcb54d24..ac5c4579 --- a/event.h +++ b/include/core/event.h @@ -1,87 +1,85 @@ -#ifndef EVENT_H -#define EVENT_H - -#include "heallocation.h" -#include -#include -#include -#include - -class EventType -{ -public: - static QString Object; - static QString Warp; - static QString CoordScript; - static QString CoordWeather; - static QString Sign; - static QString HiddenItem; - static QString SecretBase; - static QString HealLocation; -}; - -class Event -{ -public: - Event(); -public: - int x() { - return getInt("x"); - } - int y() { - return getInt("y"); - } - int elevation() { - return getInt("elevation"); - } - void setX(int x) { - put("x", x); - } - void setY(int y) { - put("y", y); - } - QString get(QString key) { - return values.value(key); - } - int getInt(QString key) { - return values.value(key).toInt(nullptr, 0); - } - uint16_t getU16(QString key) { - return values.value(key).toUShort(nullptr, 0); - } - void put(QString key, int value) { - put(key, QString("%1").arg(value)); - } - void put(QString key, QString value) { - values.insert(key, value); - } - - static Event* createNewEvent(QString, QString); - static Event* createNewObjectEvent(); - static Event* createNewWarpEvent(QString); - static Event* createNewHealLocationEvent(QString); - static Event* createNewCoordScriptEvent(); - static Event* createNewCoordWeatherEvent(); - static Event* createNewSignEvent(); - static Event* createNewHiddenItemEvent(); - static Event* createNewSecretBaseEvent(); - - QString buildObjectEventMacro(int); - QString buildWarpEventMacro(QMap*); - HealLocation buildHealLocation(); - QString buildCoordScriptEventMacro(); - QString buildCoordWeatherEventMacro(); - QString buildSignEventMacro(); - QString buildHiddenItemEventMacro(); - QString buildSecretBaseEventMacro(); - void setPixmapFromSpritesheet(QImage, int, int); - int getPixelX(); - int getPixelY(); - - QMap values; - QPixmap pixmap; - int spriteWidth; - int spriteHeight; -}; - -#endif // EVENT_H +#ifndef EVENT_H +#define EVENT_H + +#include +#include +#include +#include + +class EventType +{ +public: + static QString Object; + static QString Warp; + static QString CoordScript; + static QString CoordWeather; + static QString Sign; + static QString HiddenItem; + static QString SecretBase; + static QString HealLocation; +}; + +class Event +{ +public: + Event(); +public: + int x() { + return getInt("x"); + } + int y() { + return getInt("y"); + } + int elevation() { + return getInt("elevation"); + } + void setX(int x) { + put("x", x); + } + void setY(int y) { + put("y", y); + } + QString get(QString key) { + return values.value(key); + } + int getInt(QString key) { + return values.value(key).toInt(nullptr, 0); + } + uint16_t getU16(QString key) { + return values.value(key).toUShort(nullptr, 0); + } + void put(QString key, int value) { + put(key, QString("%1").arg(value)); + } + void put(QString key, QString value) { + values.insert(key, value); + } + + static Event* createNewEvent(QString, QString); + static Event* createNewObjectEvent(); + static Event* createNewWarpEvent(QString); + static Event* createNewHealLocationEvent(QString); + static Event* createNewCoordScriptEvent(); + static Event* createNewCoordWeatherEvent(); + static Event* createNewSignEvent(); + static Event* createNewHiddenItemEvent(); + static Event* createNewSecretBaseEvent(); + + QString buildObjectEventMacro(int); + QString buildWarpEventMacro(QMap*); + QString buildCoordScriptEventMacro(); + QString buildCoordWeatherEventMacro(); + QString buildSignEventMacro(); + QString buildHiddenItemEventMacro(); + QString buildSecretBaseEventMacro(); + void setPixmapFromSpritesheet(QImage, int, int); + int getPixelX(); + int getPixelY(); + + QMap values; + QPixmap pixmap; + int spriteWidth; + int spriteHeight; +}; + +#endif // EVENT_H diff --git a/heallocation.h b/include/core/heallocation.h similarity index 86% rename from heallocation.h rename to include/core/heallocation.h index 14571a3d..e8e702ae 100644 --- a/heallocation.h +++ b/include/core/heallocation.h @@ -1,6 +1,7 @@ #ifndef HEALLOCATION_H #define HEALLOCATION_H +#include "event.h" #include #include @@ -17,7 +18,7 @@ public: int index; uint16_t x; uint16_t y; - + static HealLocation fromEvent(Event*); }; #endif // HEALLOCATION_H diff --git a/include/core/history.h b/include/core/history.h new file mode 100644 index 00000000..40576ff2 --- /dev/null +++ b/include/core/history.h @@ -0,0 +1,58 @@ +#ifndef HISTORY_H +#define HISTORY_H + +#include + +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()) { + T 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; +}; + +#endif // HISTORY_H diff --git a/include/core/historyitem.h b/include/core/historyitem.h new file mode 100644 index 00000000..d56392d2 --- /dev/null +++ b/include/core/historyitem.h @@ -0,0 +1,15 @@ +#ifndef HISTORYITEM_H +#define HISTORYITEM_H + +#include "blockdata.h" + +class HistoryItem { +public: + Blockdata *metatiles; + int layoutWidth; + int layoutHeight; + HistoryItem(Blockdata *metatiles, int layoutWidth, int layoutHeight); + ~HistoryItem(); +}; + +#endif // HISTORYITEM_H diff --git a/include/core/map.h b/include/core/map.h new file mode 100644 index 00000000..a77865e8 --- /dev/null +++ b/include/core/map.h @@ -0,0 +1,89 @@ +#ifndef MAP_H +#define MAP_H + +#include "blockdata.h" +#include "history.h" +#include "historyitem.h" +#include "mapconnection.h" +#include "maplayout.h" +#include "tileset.h" +#include "event.h" + +#include +#include +#include +#include +#include + +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; + QImage collision_image; + QPixmap collision_pixmap; + QImage image; + QPixmap pixmap; + History metatileHistory; + QMap> events; + QList connections; + 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(); + QPixmap render(bool ignoreCache); + QPixmap renderCollision(bool ignoreCache); + bool blockChanged(int, Blockdata*); + void cacheBlockdata(); + void cacheCollision(); + 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); + void undo(); + void redo(); + void commit(); + QList getAllEvents(); + void removeEvent(Event*); + void addEvent(Event*); + QPixmap renderConnection(MapConnection); + QPixmap renderBorder(); + void setDimensions(int newWidth, int newHeight, bool setNewBlockData = true); + void cacheBorder(); + bool hasUnsavedChanges(); + +private: + void setNewDimensionsBlockdata(int newWidth, int newHeight); + +signals: + void mapChanged(Map *map); + void mapNeedsRedrawing(); +}; + +#endif // MAP_H diff --git a/include/core/mapconnection.h b/include/core/mapconnection.h new file mode 100644 index 00000000..020d2905 --- /dev/null +++ b/include/core/mapconnection.h @@ -0,0 +1,14 @@ +#ifndef MAPCONNECTION_H +#define MAPCONNECTION_H + +#include +#include + +class MapConnection { +public: + QString direction; + QString offset; + QString map_name; +}; + +#endif // MAPCONNECTION_H diff --git a/include/core/maplayout.h b/include/core/maplayout.h new file mode 100644 index 00000000..ad178ece --- /dev/null +++ b/include/core/maplayout.h @@ -0,0 +1,41 @@ +#ifndef MAPLAYOUT_H +#define MAPLAYOUT_H + +#include "blockdata.h" +#include "tileset.h" +#include +#include +#include + +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(), ""); + } +}; + +#endif // MAPLAYOUT_H diff --git a/include/core/metatile.h b/include/core/metatile.h new file mode 100644 index 00000000..4f280608 --- /dev/null +++ b/include/core/metatile.h @@ -0,0 +1,20 @@ +#ifndef METATILE_H +#define METATILE_H + +#include "tile.h" +#include + +class Metatile +{ +public: + Metatile(); +public: + QList *tiles = nullptr; + uint8_t behavior; + uint8_t layerType; + + Metatile *copy(); + static int getBlockIndex(int); +}; + +#endif // METATILE_H diff --git a/parseutil.h b/include/core/parseutil.h old mode 100755 new mode 100644 similarity index 96% rename from parseutil.h rename to include/core/parseutil.h index 23e095fc..e85469c1 --- a/parseutil.h +++ b/include/core/parseutil.h @@ -1,47 +1,47 @@ -#ifndef PARSEUTIL_H -#define PARSEUTIL_H - -#include "heallocation.h" - -#include -#include -#include - -enum TokenType { - Number, - Operator, -}; - -class Token { -public: - Token(QString value = "", QString type = "") { - this->value = value; - this->type = TokenType::Operator; - if (type == "decimal" || type == "hex") { - this->type = TokenType::Number; - this->operatorPrecedence = -1; - } else if (type == "operator") { - this->operatorPrecedence = precedenceMap[value]; - } - } - static QMap precedenceMap; - QString value; - TokenType type; - int operatorPrecedence; // only relevant for operator tokens -}; - -class ParseUtil -{ -public: - ParseUtil(); - void strip_comment(QString*); - QList* parseAsm(QString); - int evaluateDefine(QString, QMap*); - QList* parseHealLocs(QString); -private: - QList tokenizeExpression(QString expression, QMap* knownIdentifiers); - QList generatePostfix(QList tokens); - int evaluatePostfix(QList postfix); -}; - -#endif // PARSEUTIL_H +#ifndef PARSEUTIL_H +#define PARSEUTIL_H + +#include "heallocation.h" + +#include +#include +#include + +enum TokenType { + Number, + Operator, +}; + +class Token { +public: + Token(QString value = "", QString type = "") { + this->value = value; + this->type = TokenType::Operator; + if (type == "decimal" || type == "hex") { + this->type = TokenType::Number; + this->operatorPrecedence = -1; + } else if (type == "operator") { + this->operatorPrecedence = precedenceMap[value]; + } + } + static QMap precedenceMap; + QString value; + TokenType type; + int operatorPrecedence; // only relevant for operator tokens +}; + +class ParseUtil +{ +public: + ParseUtil(); + void strip_comment(QString*); + QList* parseAsm(QString); + int evaluateDefine(QString, QMap*); + QList* parseHealLocs(QString); +private: + QList tokenizeExpression(QString expression, QMap* knownIdentifiers); + QList generatePostfix(QList tokens); + int evaluatePostfix(QList postfix); +}; + +#endif // PARSEUTIL_H diff --git a/tile.h b/include/core/tile.h old mode 100755 new mode 100644 similarity index 90% rename from tile.h rename to include/core/tile.h index f511a7cb..d1c231cc --- a/tile.h +++ b/include/core/tile.h @@ -1,16 +1,16 @@ -#ifndef TILE_H -#define TILE_H - - -class Tile -{ -public: - Tile(); -public: - int tile; - int xflip; - int yflip; - int palette; -}; - -#endif // TILE_H +#ifndef TILE_H +#define TILE_H + + +class Tile +{ +public: + Tile(); +public: + int tile; + int xflip; + int yflip; + int palette; +}; + +#endif // TILE_H diff --git a/tileset.h b/include/core/tileset.h old mode 100755 new mode 100644 similarity index 68% rename from tileset.h rename to include/core/tileset.h index 5cd22daa..ce735e35 --- a/tileset.h +++ b/include/core/tileset.h @@ -1,45 +1,40 @@ -#ifndef TILESET_H -#define TILESET_H - -#include "tile.h" -#include - -class Metatile; - -class Tileset -{ -public: - Tileset(); -public: - QString name; - QString is_compressed; - QString is_secondary; - QString padding; - QString tiles_label; - QString palettes_label; - QString metatiles_label; - QString callback_label; - QString metatile_attrs_label; - - QList *tiles = nullptr; - QList *metatiles = nullptr; - QList> *palettes = nullptr; -}; - -class Metatile -{ -public: - Metatile(); -public: - QList *tiles = nullptr; - int attr; - - static QImage getMetatileImage(int, Tileset*, Tileset*); - static Metatile* getMetatile(int, Tileset*, Tileset*); - static QImage getMetatileTile(int, Tileset*, Tileset*); - static Tileset* getBlockTileset(int, Tileset*, Tileset*); - static int getBlockIndex(int); - static QList> getBlockPalettes(Tileset*, Tileset*); -}; - -#endif // TILESET_H +#ifndef TILESET_H +#define TILESET_H + +#include "metatile.h" +#include "tile.h" +#include + +class Tileset +{ +public: + Tileset(); +public: + QString name; + QString is_compressed; + QString is_secondary; + QString padding; + QString tiles_label; + QString palettes_label; + QString metatiles_label; + QString metatiles_path; + QString callback_label; + QString metatile_attrs_label; + QString metatile_attrs_path; + QString tilesImagePath; + QImage tilesImage; + QList palettePaths; + + QList *tiles = nullptr; + QList *metatiles = nullptr; + QList> *palettes = nullptr; + + Tileset* copy(); + + static Tileset* getBlockTileset(int, Tileset*, Tileset*); + static Metatile* getMetatile(int, Tileset*, Tileset*); + static QList> getBlockPalettes(Tileset*, Tileset*); + static QList getPalette(int, Tileset*, Tileset*); +}; + +#endif // TILESET_H diff --git a/include/editor.h b/include/editor.h new file mode 100644 index 00000000..ac95c761 --- /dev/null +++ b/include/editor.h @@ -0,0 +1,264 @@ +#ifndef EDITOR_H +#define EDITOR_H + +#include +#include +#include +#include +#include +#include +#include + +#include "mapconnection.h" +#include "metatileselector.h" +#include "movementpermissionsselector.h" +#include "project.h" +#include "ui_mainwindow.h" +#include "bordermetatilespixmapitem.h" +#include "connectionpixmapitem.h" +#include "currentselectedmetatilespixmapitem.h" +#include "collisionpixmapitem.h" +#include "mappixmapitem.h" +#include "settings.h" + +class DraggablePixmapItem; +class MetatilesPixmapItem; + +class Editor : public QObject +{ + Q_OBJECT +public: + Editor(Ui::MainWindow* ui); +public: + Ui::MainWindow* ui; + QObject *parent = nullptr; + Project *project = nullptr; + Map *map = nullptr; + Settings *settings; + void saveProject(); + void save(); + void undo(); + void redo(); + void setMap(QString map_name); + void displayMap(); + void displayMetatileSelector(); + void displayMapMetatiles(); + void displayMapMovementPermissions(); + void displayBorderMetatiles(); + void displayCurrentMetatilesSelection(); + void redrawCurrentMetatilesSelection(); + void displayMovementPermissionSelector(); + 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, bool forceLoad = false); + void updateSecondaryTileset(QString tilesetLabel, bool forceLoad = false); + void toggleBorderVisibility(bool visible); + Tileset *getCurrentMapPrimaryTileset(); + + 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; + MetatileSelector *metatile_selector_item = nullptr; + + BorderMetatilesPixmapItem *selected_border_metatiles_item = nullptr; + CurrentSelectedMetatilesPixmapItem *scene_current_metatile_selection_item = nullptr; + MovementPermissionsSelector *movement_permissions_selector_item = nullptr; + + QList *events = nullptr; + QList *selected_events = nullptr; + + 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); + void objectsView_onMouseRelease(QMouseEvent *event); + +private: + void setConnectionItemsVisible(bool); + void setBorderItemsVisible(bool, qreal = 1); + void setConnectionEditControlValues(MapConnection*); + void setConnectionEditControlsEnabled(bool); + void createConnectionItem(MapConnection* connection, bool hide); + void populateConnectionMapPickers(); + void setDiveEmergeControls(); + void updateDiveEmergeMap(QString mapName, QString direction); + void onConnectionOffsetChanged(int newOffset); + void removeMirroredConnection(MapConnection*); + void updateMirroredConnectionOffset(MapConnection*); + void updateMirroredConnectionDirection(MapConnection*, QString); + void updateMirroredConnectionMap(MapConnection*, QString); + void updateMirroredConnection(MapConnection*, QString, QString, bool isDelete = false); + Event* createNewObjectEvent(); + Event* createNewWarpEvent(); + Event* createNewHealLocationEvent(); + Event* createNewCoordScriptEvent(); + Event* createNewCoordWeatherEvent(); + Event* createNewSignEvent(); + Event* createNewHiddenItemEvent(); + Event* createNewSecretBaseEvent(); + QString getMovementPermissionText(uint16_t collision, uint16_t elevation); + +private slots: + void mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); + void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item); + void onConnectionMoved(MapConnection*); + void onConnectionItemSelected(ConnectionPixmapItem* connectionItem); + void onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem); + void onConnectionDirectionChanged(QString newDirection); + void onBorderMetatilesChanged(); + void onHoveredMovementPermissionChanged(uint16_t, uint16_t); + void onHoveredMovementPermissionCleared(); + void onHoveredMetatileSelectionChanged(uint16_t); + void onHoveredMetatileSelectionCleared(); + void onHoveredMapMetatileChanged(int, int); + void onHoveredMapMetatileCleared(); + void onHoveredMapMovementPermissionChanged(int, int); + void onHoveredMapMovementPermissionCleared(); + void onSelectedMetatilesChanged(); + +signals: + void objectsChanged(); + void selectedObjectsChanged(); + void loadMapRequested(QString, QString); + void tilesetChanged(QString); + void warpEventDoubleClicked(QString mapName, QString warpNum); + void currentMetatilesSelectionChanged(); + void wheelZoom(int delta); +}; + + + +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*); +}; + +#endif // EDITOR_H diff --git a/mainwindow.h b/include/mainwindow.h old mode 100755 new mode 100644 similarity index 89% rename from mainwindow.h rename to include/mainwindow.h index e1191c3a..3ddc3c39 --- a/mainwindow.h +++ b/include/mainwindow.h @@ -1,155 +1,169 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include "project.h" -#include "map.h" -#include "editor.h" - -namespace Ui { -class MainWindow; -} - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = nullptr); - ~MainWindow(); - -public slots: - void setStatusBarMessage(QString message, int timeout = 0); - -private slots: - void on_action_Open_Project_triggered(); - void on_mapList_activated(const QModelIndex &index); - void on_action_Save_Project_triggered(); - void openWarpMap(QString map_name, QString warp_num); - - void undo(); - void redo(); - - void openInTextEditor(); - - void onLoadMapRequested(QString, QString); - void onMapChanged(Map *map); - void onMapNeedsRedrawing(); - - void on_action_Save_triggered(); - void on_tabWidget_2_currentChanged(int index); - void on_action_Exit_triggered(); - void on_comboBox_Song_activated(const QString &arg1); - void on_comboBox_Location_activated(const QString &arg1); - void on_comboBox_Visibility_activated(const QString &arg1); - void on_comboBox_Weather_activated(const QString &arg1); - void on_comboBox_Type_activated(const QString &arg1); - void on_comboBox_BattleScene_activated(const QString &arg1); - void on_checkBox_ShowLocation_clicked(bool checked); - - void on_tabWidget_currentChanged(int index); - - void on_actionUndo_triggered(); - - void on_actionRedo_triggered(); - - void on_actionZoom_In_triggered(); - void on_actionZoom_Out_triggered(); - void on_actionBetter_Cursors_triggered(); - void on_actionPencil_triggered(); - void on_actionPointer_triggered(); - void on_actionFlood_Fill_triggered(); - void on_actionEyedropper_triggered(); - void on_actionMove_triggered(); - void on_actionMap_Shift_triggered(); - - void on_toolButton_deleteObject_clicked(); - void on_toolButton_Open_Scripts_clicked(); - - void addNewEvent(QString); - void updateSelectedObjects(); - - void on_toolButton_Paint_clicked(); - - void on_toolButton_Select_clicked(); - - void on_toolButton_Fill_clicked(); - - void on_toolButton_Dropper_clicked(); - - void on_toolButton_Move_clicked(); - - void on_toolButton_Shift_clicked(); - - void onOpenMapListContextMenu(const QPoint &point); - void onAddNewMapToGroupClick(QAction* triggeredAction); - void onTilesetChanged(QString); - void currentMetatilesSelectionChanged(); - - void on_action_Export_Map_Image_triggered(); - - void on_comboBox_ConnectionDirection_currentIndexChanged(const QString &arg1); - - void on_spinBox_ConnectionOffset_valueChanged(int offset); - - void on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName); - - void on_pushButton_AddConnection_clicked(); - - void on_pushButton_RemoveConnection_clicked(); - - void on_comboBox_DiveMap_currentTextChanged(const QString &mapName); - - void on_comboBox_EmergeMap_currentTextChanged(const QString &mapName); - - void on_comboBox_PrimaryTileset_activated(const QString &arg1); - - void on_comboBox_SecondaryTileset_activated(const QString &arg1); - - void on_pushButton_clicked(); - - void on_checkBox_smartPaths_stateChanged(int selected); - - void on_checkBox_Visibility_clicked(bool checked); - - void on_checkBox_ToggleBorder_stateChanged(int arg1); - -private: - Ui::MainWindow *ui; - QStandardItemModel *mapListModel; - QList *mapGroupsModel; - Editor *editor = nullptr; - QIcon* mapIcon; - void setMap(QString); - void redrawMapScene(); - void loadDataStructures(); - void populateMapList(); - QString getExistingDirectory(QString); - void openProject(QString dir); - QString getDefaultMap(); - void setRecentMap(QString map_name); - QStandardItem* createMapItem(QString mapName, int groupNum, int inGroupNum); - - void markAllEdited(QAbstractItemModel *model); - void markEdited(QModelIndex index); - void updateMapList(); - - void displayMapProperties(); - void checkToolButtons(); - - void scaleMapView(int); -}; - -enum MapListUserRoles { - GroupRole = Qt::UserRole + 1, // Used to hold the map group number. - TypeRole = Qt::UserRole + 2, // Used to differentiate between the different layers of the map list tree view. -}; - -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "project.h" +#include "map.h" +#include "editor.h" +#include "tileseteditor.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +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); + void on_action_Save_Project_triggered(); + void openWarpMap(QString map_name, QString warp_num); + + void undo(); + void redo(); + + void openInTextEditor(); + + void onLoadMapRequested(QString, QString); + void onMapChanged(Map *map); + void onMapNeedsRedrawing(); + void onTilesetsSaved(QString, QString); + + void on_action_Save_triggered(); + void on_tabWidget_2_currentChanged(int index); + void on_action_Exit_triggered(); + void on_comboBox_Song_activated(const QString &arg1); + void on_comboBox_Location_activated(const QString &arg1); + void on_comboBox_Visibility_activated(const QString &arg1); + void on_comboBox_Weather_activated(const QString &arg1); + void on_comboBox_Type_activated(const QString &arg1); + void on_comboBox_BattleScene_activated(const QString &arg1); + void on_checkBox_ShowLocation_clicked(bool checked); + + void on_tabWidget_currentChanged(int index); + + void on_actionUndo_triggered(); + + void on_actionRedo_triggered(); + + void on_actionZoom_In_triggered(); + void on_actionZoom_Out_triggered(); + void on_actionBetter_Cursors_triggered(); + void on_actionPencil_triggered(); + void on_actionPointer_triggered(); + void on_actionFlood_Fill_triggered(); + void on_actionEyedropper_triggered(); + void on_actionMove_triggered(); + void on_actionMap_Shift_triggered(); + + void on_toolButton_deleteObject_clicked(); + void on_toolButton_Open_Scripts_clicked(); + + void addNewEvent(QString); + void updateSelectedObjects(); + + void on_toolButton_Paint_clicked(); + + void on_toolButton_Select_clicked(); + + void on_toolButton_Fill_clicked(); + + void on_toolButton_Dropper_clicked(); + + void on_toolButton_Move_clicked(); + + void on_toolButton_Shift_clicked(); + + void onOpenMapListContextMenu(const QPoint &point); + void onAddNewMapToGroupClick(QAction* triggeredAction); + void onTilesetChanged(QString); + void currentMetatilesSelectionChanged(); + void onTilesetEditorClosed(); + + void on_action_Export_Map_Image_triggered(); + + void on_comboBox_ConnectionDirection_currentIndexChanged(const QString &arg1); + + void on_spinBox_ConnectionOffset_valueChanged(int offset); + + void on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName); + + void on_pushButton_AddConnection_clicked(); + + void on_pushButton_RemoveConnection_clicked(); + + void on_comboBox_DiveMap_currentTextChanged(const QString &mapName); + + void on_comboBox_EmergeMap_currentTextChanged(const QString &mapName); + + void on_comboBox_PrimaryTileset_activated(const QString &arg1); + + void on_comboBox_SecondaryTileset_activated(const QString &arg1); + + void on_pushButton_clicked(); + + void on_checkBox_smartPaths_stateChanged(int selected); + + void on_checkBox_Visibility_clicked(bool checked); + + void on_checkBox_ToggleBorder_stateChanged(int arg1); + + void resetMapViewScale(); + + void on_actionTileset_Editor_triggered(); + +private: + Ui::MainWindow *ui; + TilesetEditor *tilesetEditor = nullptr; + QStandardItemModel *mapListModel; + QList *mapGroupsModel; + QMap mapListIndexes; + Editor *editor = nullptr; + QIcon* mapIcon; + void setMap(QString, bool scrollTreeView = false); + void redrawMapScene(); + void loadDataStructures(); + void populateMapList(); + QString getExistingDirectory(QString); + void openProject(QString dir); + QString getDefaultMap(); + void setRecentMap(QString map_name); + QStandardItem* createMapItem(QString mapName, int groupNum, int inGroupNum); + + void markAllEdited(QAbstractItemModel *model); + void markEdited(QModelIndex index); + void updateMapList(); + + void displayMapProperties(); + void checkToolButtons(); + + void initExtraShortcuts(); + void initExtraSignals(); + void initEditor(); + void loadUserSettings(); + void openRecentProject(); + void updateTilesetEditor(); +}; + +enum MapListUserRoles { + GroupRole = Qt::UserRole + 1, // Used to hold the map group number. + TypeRole = Qt::UserRole + 2, // Used to differentiate between the different layers of the map list tree view. +}; + +#endif // MAINWINDOW_H diff --git a/project.h b/include/project.h old mode 100755 new mode 100644 similarity index 88% rename from project.h rename to include/project.h index cb1a10ee..fa4ee2ed --- a/project.h +++ b/include/project.h @@ -1,152 +1,163 @@ -#ifndef PROJECT_H -#define PROJECT_H - -#include "map.h" -#include "blockdata.h" -#include "heallocation.h" - -#include -#include -#include - -static QString NONE_MAP_CONSTANT = "MAP_NONE"; -static QString NONE_MAP_NAME = "None"; - -class Project -{ -public: - Project(); - QString root; - QStringList *groupNames = nullptr; - QMap *map_groups; - QList groupedMapNames; - QStringList *mapNames = nullptr; - QList flyableMaps; - QMap* mapConstantsToMapNames; - QMap* mapNamesToMapConstants; - QList mapLayoutsTable; - QList mapLayoutsTableMaster; - QMap mapLayouts; - QMap mapLayoutsMaster; - QStringList *regionMapSections = nullptr; - QStringList *itemNames = nullptr; - QStringList *flagNames = nullptr; - QStringList *varNames = nullptr; - QStringList *movementTypes = nullptr; - QStringList *mapTypes = nullptr; - QStringList *mapBattleScenes = nullptr; - QStringList *weatherNames = nullptr; - QStringList *coordEventWeatherNames = nullptr; - QStringList *secretBaseIds = nullptr; - QStringList *bgEventFacingDirections = nullptr; - QStringList mapsWithConnections; - - QMap *map_cache; - Map* loadMap(QString); - Map* getMap(QString); - - QMap *tileset_cache = nullptr; - Tileset* loadTileset(QString); - Tileset* getTileset(QString); - - Blockdata* readBlockdata(QString); - void loadBlockdata(Map*); - - QString readTextFile(QString path); - void saveTextFile(QString path, QString text); - void appendTextFile(QString path, QString text); - void deleteFile(QString path); - - void readMapGroups(); - Map* addNewMapToGroup(QString mapName, int groupNum); - QString getNewMapName(); - QString getProjectTitle(); - - QList* getLabelMacros(QList*, QString); - QStringList* getLabelValues(QList*, QString); - void readMapHeader(Map*); - void readMapLayoutsTable(); - void readAllMapLayouts(); - QStringList* readLayoutValues(QString layoutName); - void readMapLayout(Map*); - void readMapsWithConnections(); - void loadMapTilesets(Map*); - void loadTilesetAssets(Tileset*); - - void saveBlockdata(Map*); - void saveMapBorder(Map*); - void writeBlockdata(QString, Blockdata*); - void saveAllMaps(); - void saveMap(Map*); - void saveAllDataStructures(); - void saveAllMapLayouts(); - void saveMapGroupsTable(); - void saveMapConstantsHeader(); - void saveHealLocationStruct(Map*); - - QList* parseAsm(QString text); - QStringList getSongNames(); - QStringList getVisibilities(); - QMap getTilesets(); - void readTilesetProperties(); - void readRegionMapSections(); - void readItemNames(); - void readFlagNames(); - void readVarNames(); - void readMovementTypes(); - void readMapTypes(); - void readMapBattleScenes(); - void readWeatherNames(); - void readCoordEventWeatherNames(); - void readSecretBaseIds(); - void readBgEventFacingDirections(); - - void loadEventPixmaps(QList objects); - QMap getEventObjGfxConstants(); - QString fixGraphicPath(QString path); - - void readMapEvents(Map *map); - void loadMapConnections(Map *map); - - void loadMapBorder(Map *map); - - void saveMapEvents(Map *map); - - QStringList readCArray(QString text, QString label); - QString readCIncbin(QString text, QString label); - QMap readCDefines(QString text, QStringList prefixes); - - static int getNumTilesPrimary(); - static int getNumTilesTotal(); - static int getNumMetatilesPrimary(); - static int getNumMetatilesTotal(); - static int getNumPalettesPrimary(); - static int getNumPalettesTotal(); -private: - QString getMapLayoutsTableFilepath(); - QString getMapLayoutFilepath(QString); - void saveMapHeader(Map*); - void saveMapConnections(Map*); - void updateMapsWithConnections(Map*); - void saveMapsWithConnections(); - void saveMapLayoutsTable(); - void updateMapLayout(Map*); - void readCDefinesSorted(QString, QStringList, QStringList*); - void readCDefinesSorted(QString, QStringList, QStringList*, QString, int); - - void setNewMapHeader(Map* map, int mapIndex); - void setNewMapLayout(Map* map); - void setNewMapBlockdata(Map* map); - void setNewMapBorder(Map *map); - void setNewMapEvents(Map *map); - void setNewMapConnections(Map *map); - - static int num_tiles_primary; - static int num_tiles_total; - static int num_metatiles_primary; - static int num_metatiles_total; - static int num_pals_primary; - static int num_pals_total; -}; - -#endif // PROJECT_H +#ifndef PROJECT_H +#define PROJECT_H + +#include "map.h" +#include "blockdata.h" +#include "heallocation.h" + +#include +#include +#include + +static QString NONE_MAP_CONSTANT = "MAP_NONE"; +static QString NONE_MAP_NAME = "None"; + +class Project +{ +public: + Project(); + QString root; + QStringList *groupNames = nullptr; + QMap *map_groups; + QList groupedMapNames; + QStringList *mapNames = nullptr; + QList flyableMaps; + QMap* mapConstantsToMapNames; + QMap* mapNamesToMapConstants; + QList mapLayoutsTable; + QList mapLayoutsTableMaster; + QMap mapLayouts; + QMap mapLayoutsMaster; + QStringList *regionMapSections = nullptr; + QStringList *itemNames = nullptr; + QStringList *flagNames = nullptr; + QStringList *varNames = nullptr; + QStringList *movementTypes = nullptr; + QStringList *mapTypes = nullptr; + QStringList *mapBattleScenes = nullptr; + QStringList *weatherNames = nullptr; + QStringList *coordEventWeatherNames = nullptr; + QStringList *secretBaseIds = nullptr; + QStringList *bgEventFacingDirections = nullptr; + QStringList mapsWithConnections; + QMap metatileBehaviorMap; + QMap metatileBehaviorMapInverse; + + QMap *map_cache; + Map* loadMap(QString); + Map* getMap(QString); + + QMap *tileset_cache = nullptr; + Tileset* loadTileset(QString, Tileset *tileset = nullptr); + Tileset* getTileset(QString, bool forceLoad = false); + + Blockdata* readBlockdata(QString); + void loadBlockdata(Map*); + + QString readTextFile(QString path); + void saveTextFile(QString path, QString text); + void appendTextFile(QString path, QString text); + void deleteFile(QString path); + + void readMapGroups(); + Map* addNewMapToGroup(QString mapName, int groupNum); + QString getNewMapName(); + QString getProjectTitle(); + + QList* getLabelMacros(QList*, QString); + QStringList* getLabelValues(QList*, QString); + void readMapHeader(Map*); + void readMapLayoutsTable(); + void readAllMapLayouts(); + QStringList* readLayoutValues(QString layoutName); + void readMapLayout(Map*); + void readMapsWithConnections(); + void loadMapTilesets(Map*); + void loadTilesetAssets(Tileset*); + void loadTilesetTiles(Tileset*, QImage); + void loadTilesetMetatiles(Tileset*); + + void saveBlockdata(Map*); + void saveMapBorder(Map*); + void writeBlockdata(QString, Blockdata*); + void saveAllMaps(); + void saveMap(Map*); + void saveAllDataStructures(); + void saveAllMapLayouts(); + void saveMapGroupsTable(); + void saveMapConstantsHeader(); + void saveHealLocationStruct(Map*); + void saveTilesets(Tileset*, Tileset*); + + QList* parseAsm(QString text); + QStringList getSongNames(); + QStringList getVisibilities(); + QMap getTilesets(); + void readTilesetProperties(); + void readRegionMapSections(); + void readItemNames(); + void readFlagNames(); + void readVarNames(); + void readMovementTypes(); + void readMapTypes(); + void readMapBattleScenes(); + void readWeatherNames(); + void readCoordEventWeatherNames(); + void readSecretBaseIds(); + void readBgEventFacingDirections(); + void readMetatileBehaviors(); + + void loadEventPixmaps(QList objects); + QMap getEventObjGfxConstants(); + QString fixPalettePath(QString path); + QString fixGraphicPath(QString path); + + void readMapEvents(Map *map); + void loadMapConnections(Map *map); + + void loadMapBorder(Map *map); + + void saveMapEvents(Map *map); + + QStringList readCArray(QString text, QString label); + QString readCIncbin(QString text, QString label); + QMap readCDefines(QString text, QStringList prefixes); + + static int getNumTilesPrimary(); + static int getNumTilesTotal(); + static int getNumMetatilesPrimary(); + static int getNumMetatilesTotal(); + static int getNumPalettesPrimary(); + static int getNumPalettesTotal(); +private: + QString getMapLayoutsTableFilepath(); + QString getMapLayoutFilepath(QString); + void saveMapHeader(Map*); + void saveMapConnections(Map*); + void saveTilesetMetatileAttributes(Tileset*); + void saveTilesetMetatiles(Tileset*); + void saveTilesetTilesImage(Tileset*); + void saveTilesetPalettes(Tileset*, bool); + void updateMapsWithConnections(Map*); + void saveMapsWithConnections(); + void saveMapLayoutsTable(); + void updateMapLayout(Map*); + void readCDefinesSorted(QString, QStringList, QStringList*); + void readCDefinesSorted(QString, QStringList, QStringList*, QString, int); + + void setNewMapHeader(Map* map, int mapIndex); + void setNewMapLayout(Map* map); + void setNewMapBlockdata(Map* map); + void setNewMapBorder(Map *map); + void setNewMapEvents(Map *map); + void setNewMapConnections(Map *map); + + static int num_tiles_primary; + static int num_tiles_total; + static int num_metatiles_primary; + static int num_metatiles_total; + static int num_pals_primary; + static int num_pals_total; +}; + +#endif // PROJECT_H diff --git a/include/settings.h b/include/settings.h new file mode 100644 index 00000000..91699542 --- /dev/null +++ b/include/settings.h @@ -0,0 +1,15 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +class Settings +{ +public: + Settings(); + bool smartPathsEnabled; + bool betterCursors; + QCursor mapCursor; +}; + +#endif // SETTINGS_H diff --git a/include/ui/bordermetatilespixmapitem.h b/include/ui/bordermetatilespixmapitem.h new file mode 100644 index 00000000..11aae827 --- /dev/null +++ b/include/ui/bordermetatilespixmapitem.h @@ -0,0 +1,25 @@ +#ifndef BORDERMETATILESPIXMAPITEM_H +#define BORDERMETATILESPIXMAPITEM_H + +#include "map.h" +#include "metatileselector.h" +#include + +class BorderMetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + BorderMetatilesPixmapItem(Map *map_, MetatileSelector *metatileSelector) { + this->map = map_; + this->metatileSelector = metatileSelector; + setAcceptHoverEvents(true); + } + MetatileSelector *metatileSelector; + Map* map; + void draw(); +signals: + void borderMetatilesChanged(); +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); +}; + +#endif // BORDERMETATILESPIXMAPITEM_H diff --git a/include/ui/collisionpixmapitem.h b/include/ui/collisionpixmapitem.h new file mode 100644 index 00000000..82f4aed3 --- /dev/null +++ b/include/ui/collisionpixmapitem.h @@ -0,0 +1,37 @@ +#ifndef COLLISIONPIXMAPITEM_H +#define COLLISIONPIXMAPITEM_H + +#include "metatileselector.h" +#include "movementpermissionsselector.h" +#include "mappixmapitem.h" +#include "map.h" +#include "settings.h" + +class CollisionPixmapItem : public MapPixmapItem { + Q_OBJECT +public: + CollisionPixmapItem(Map *map, MovementPermissionsSelector *movementPermissionsSelector, MetatileSelector *metatileSelector, Settings *settings) + : MapPixmapItem(map, metatileSelector, settings){ + this->movementPermissionsSelector = movementPermissionsSelector; + } + MovementPermissionsSelector *movementPermissionsSelector; + 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 *); + void hoveredMapMovementPermissionChanged(int, int); + void hoveredMapMovementPermissionCleared(); + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +#endif // COLLISIONPIXMAPITEM_H diff --git a/include/ui/connectionpixmapitem.h b/include/ui/connectionpixmapitem.h new file mode 100644 index 00000000..a4cdf4e2 --- /dev/null +++ b/include/ui/connectionpixmapitem.h @@ -0,0 +1,44 @@ +#ifndef CONNECTIONPIXMAPITEM_H +#define CONNECTIONPIXMAPITEM_H + +#include "mapconnection.h" +#include +#include + +class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + ConnectionPixmapItem(QPixmap pixmap, MapConnection* 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; + } + QPixmap basePixmap; + MapConnection* connection; + int initialX; + int initialY; + int initialOffset; + int baseMapWidth; + int baseMapHeight; + void render(qreal opacity = 1); + int getMinOffset(); + int getMaxOffset(); + +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(MapConnection*); +}; + +#endif // CONNECTIONPIXMAPITEM_H diff --git a/include/ui/currentselectedmetatilespixmapitem.h b/include/ui/currentselectedmetatilespixmapitem.h new file mode 100644 index 00000000..70b3b29e --- /dev/null +++ b/include/ui/currentselectedmetatilespixmapitem.h @@ -0,0 +1,20 @@ +#ifndef CURRENTSELECTEDMETATILESPIXMAPITEM_H +#define CURRENTSELECTEDMETATILESPIXMAPITEM_H + +#include "map.h" +#include "metatileselector.h" +#include + +class CurrentSelectedMetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + CurrentSelectedMetatilesPixmapItem(Map *map, MetatileSelector *metatileSelector) { + this->map = map; + this->metatileSelector = metatileSelector; + } + Map* map = nullptr; + MetatileSelector *metatileSelector; + void draw(); +}; + +#endif // CURRENTSELECTEDMETATILESPIXMAPITEM_H diff --git a/include/ui/eventpropertiesframe.h b/include/ui/eventpropertiesframe.h new file mode 100644 index 00000000..49add45b --- /dev/null +++ b/include/ui/eventpropertiesframe.h @@ -0,0 +1,22 @@ +#ifndef EVENTPROPERTIESFRAME_H +#define EVENTPROPERTIESFRAME_H + +#include + +namespace Ui { +class EventPropertiesFrame; +} + +class EventPropertiesFrame : public QFrame +{ + Q_OBJECT + +public: + explicit EventPropertiesFrame(QWidget *parent = nullptr); + ~EventPropertiesFrame(); + +public: + Ui::EventPropertiesFrame *ui; +}; + +#endif // EVENTPROPERTIESFRAME_H diff --git a/graphicsview.h b/include/ui/graphicsview.h old mode 100755 new mode 100644 similarity index 95% rename from graphicsview.h rename to include/ui/graphicsview.h index d4e41b25..0b3a12c7 --- a/graphicsview.h +++ b/include/ui/graphicsview.h @@ -1,26 +1,26 @@ -#ifndef GRAPHICSVIEW_H -#define GRAPHICSVIEW_H - -#include -#include - -class Editor; - -class GraphicsView : public QGraphicsView -{ -public: - GraphicsView() : QGraphicsView() {} - GraphicsView(QWidget *parent) : QGraphicsView(parent) {} - -public: -// GraphicsView_Object object; - Editor *editor; -protected: - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); -}; - -//Q_DECLARE_METATYPE(GraphicsView) - -#endif // GRAPHICSVIEW_H +#ifndef GRAPHICSVIEW_H +#define GRAPHICSVIEW_H + +#include +#include + +class Editor; + +class GraphicsView : public QGraphicsView +{ +public: + GraphicsView() : QGraphicsView() {} + GraphicsView(QWidget *parent) : QGraphicsView(parent) {} + +public: +// GraphicsView_Object object; + Editor *editor; +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); +}; + +//Q_DECLARE_METATYPE(GraphicsView) + +#endif // GRAPHICSVIEW_H diff --git a/include/ui/imageproviders.h b/include/ui/imageproviders.h new file mode 100644 index 00000000..96896e78 --- /dev/null +++ b/include/ui/imageproviders.h @@ -0,0 +1,15 @@ +#ifndef IMAGEPROVIDERS_H +#define IMAGEPROVIDERS_H + +#include "block.h" +#include "tileset.h" +#include +#include + +QImage getCollisionMetatileImage(Block); +QImage getCollisionMetatileImage(int, int); +QImage getMetatileImage(uint16_t, Tileset*, Tileset*); +QImage getTileImage(uint16_t, Tileset*, Tileset*); +QImage getColoredTileImage(uint16_t, Tileset*, Tileset*, int); + +#endif // IMAGEPROVIDERS_H diff --git a/include/ui/mappixmapitem.h b/include/ui/mappixmapitem.h new file mode 100644 index 00000000..812cb356 --- /dev/null +++ b/include/ui/mappixmapitem.h @@ -0,0 +1,55 @@ +#ifndef MAPPIXMAPITEM_H +#define MAPPIXMAPITEM_H + +#include "map.h" +#include "settings.h" +#include "metatileselector.h" +#include + +class MapPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + MapPixmapItem(Map *map_, MetatileSelector *metatileSelector, Settings *settings) { + this->map = map_; + this->metatileSelector = metatileSelector; + this->settings = settings; + setAcceptHoverEvents(true); + } + Map *map; + MetatileSelector *metatileSelector; + Settings *settings; + bool active; + bool right_click; + int paint_tile_initial_x; + int paint_tile_initial_y; + 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 paintNormal(int x, int y); + void paintSmartPath(int x, int y); + static QList smartPathTable; + +signals: + void mouseEvent(QGraphicsSceneMouseEvent *, MapPixmapItem *); + void hoveredMapMetatileChanged(int x, int y); + void hoveredMapMetatileCleared(); + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +#endif // MAPPIXMAPITEM_H diff --git a/include/ui/mapsceneeventfilter.h b/include/ui/mapsceneeventfilter.h new file mode 100644 index 00000000..7de427e3 --- /dev/null +++ b/include/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/include/ui/metatilelayersitem.h b/include/ui/metatilelayersitem.h new file mode 100644 index 00000000..b6599e58 --- /dev/null +++ b/include/ui/metatilelayersitem.h @@ -0,0 +1,29 @@ +#ifndef METATILELAYERSITEM_H +#define METATILELAYERSITEM_H + +#include "tileset.h" +#include +#include + +class MetatileLayersItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset) { + this->metatile = metatile; + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + } + void draw(); + void setTilesets(Tileset*, Tileset*); + void setMetatile(Metatile*); +private: + Metatile* metatile; + Tileset *primaryTileset; + Tileset *secondaryTileset; +signals: + void tileChanged(int, int); +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); +}; + +#endif // METATILELAYERSITEM_H diff --git a/include/ui/metatileselector.h b/include/ui/metatileselector.h new file mode 100644 index 00000000..2027d9fd --- /dev/null +++ b/include/ui/metatileselector.h @@ -0,0 +1,51 @@ +#ifndef METATILESELECTOR_H +#define METATILESELECTOR_H + +#include "selectablepixmapitem.h" +#include "tileset.h" + +class MetatileSelector: public SelectablePixmapItem { + Q_OBJECT +public: + MetatileSelector(int numMetatilesWide, Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(16, 16) { + this->externalSelection = false; + this->numMetatilesWide = numMetatilesWide; + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->selectedMetatiles = new QList(); + this->externalSelectedMetatiles = new QList(); + setAcceptHoverEvents(true); + } + QPoint getSelectionDimensions(); + void draw(); + void select(uint16_t metatile); + void setTilesets(Tileset*, Tileset*); + QList* getSelectedMetatiles(); + void setExternalSelection(int, int, QList*); +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); +private: + bool externalSelection; + int numMetatilesWide; + Tileset *primaryTileset; + Tileset *secondaryTileset; + QList *selectedMetatiles; + int externalSelectionWidth; + int externalSelectionHeight; + QList *externalSelectedMetatiles; + + void updateSelectedMetatiles(); + uint16_t getMetatileId(int x, int y); + QPoint getMetatileIdCoords(uint16_t); + +signals: + void hoveredMetatileSelectionChanged(uint16_t); + void hoveredMetatileSelectionCleared(); + void selectedMetatilesChanged(); +}; + +#endif // METATILESELECTOR_H diff --git a/include/ui/movementpermissionsselector.h b/include/ui/movementpermissionsselector.h new file mode 100644 index 00000000..a962f01a --- /dev/null +++ b/include/ui/movementpermissionsselector.h @@ -0,0 +1,29 @@ +#ifndef MOVEMENTPERMISSIONSSELECTOR_H +#define MOVEMENTPERMISSIONSSELECTOR_H + +#include "selectablepixmapitem.h" + +class MovementPermissionsSelector: public SelectablePixmapItem { + Q_OBJECT +public: + MovementPermissionsSelector(): SelectablePixmapItem(32, 32, 1, 1) { + setAcceptHoverEvents(true); + } + void draw(); + uint16_t getSelectedCollision(); + uint16_t getSelectedElevation(); + void select(uint16_t collision, uint16_t elevation); + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + +private: + void setSelectedMovementPermissions(QPointF); + +signals: + void hoveredMovementPermissionChanged(uint16_t, uint16_t); + void hoveredMovementPermissionCleared(); +}; + +#endif // MOVEMENTPERMISSIONSSELECTOR_H diff --git a/neweventtoolbutton.h b/include/ui/neweventtoolbutton.h similarity index 97% rename from neweventtoolbutton.h rename to include/ui/neweventtoolbutton.h index 4d5d0a9c..7ff446fd 100644 --- a/neweventtoolbutton.h +++ b/include/ui/neweventtoolbutton.h @@ -9,7 +9,6 @@ class NewEventToolButton : public QToolButton Q_OBJECT public: explicit NewEventToolButton(QWidget *parent = nullptr); - void initButton(); QString getSelectedEventType(); public slots: void newObject(); @@ -32,6 +31,7 @@ private: QAction *newSignAction; QAction *newHiddenItemAction; QAction *newSecretBaseAction; + void init(); }; #endif // NEWEVENTTOOLBUTTON_H diff --git a/noscrollcombobox.h b/include/ui/noscrollcombobox.h similarity index 100% rename from noscrollcombobox.h rename to include/ui/noscrollcombobox.h diff --git a/noscrollspinbox.h b/include/ui/noscrollspinbox.h similarity index 100% rename from noscrollspinbox.h rename to include/ui/noscrollspinbox.h diff --git a/include/ui/paletteeditor.h b/include/ui/paletteeditor.h new file mode 100644 index 00000000..d8866f24 --- /dev/null +++ b/include/ui/paletteeditor.h @@ -0,0 +1,43 @@ +#ifndef PALETTEEDITOR_H +#define PALETTEEDITOR_H + +#include +#include +#include +#include "project.h" + +namespace Ui { +class PaletteEditor; +} + +class PaletteEditor : public QMainWindow { + Q_OBJECT +public: + explicit PaletteEditor(Project*, Tileset*, Tileset*, QWidget *parent = nullptr); + ~PaletteEditor(); + void setPaletteId(int); + +private: + Ui::PaletteEditor *ui; + Project *project = nullptr; + QList> sliders; + QList frames; + Tileset *primaryTileset; + Tileset *secondaryTileset; + void disableSliderSignals(); + void enableSliderSignals(); + void initColorSliders(); + void refreshColorSliders(); + void refreshColors(); + void refreshColor(int); + void setColor(int); + +signals: + void closed(); + void changedPaletteColor(); + void changedPalette(int); +private slots: + void on_spinBox_PaletteId_valueChanged(int arg1); +}; + +#endif // PALETTEEDITOR_H diff --git a/include/ui/selectablepixmapitem.h b/include/ui/selectablepixmapitem.h new file mode 100644 index 00000000..cdab4a6a --- /dev/null +++ b/include/ui/selectablepixmapitem.h @@ -0,0 +1,40 @@ +#ifndef SELECTABLEPIXMAPITEM_H +#define SELECTABLEPIXMAPITEM_H + +#include +#include + +class SelectablePixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + SelectablePixmapItem(int cellWidth, int cellHeight): SelectablePixmapItem(cellWidth, cellHeight, INT_MAX, INT_MAX) {} + SelectablePixmapItem(int cellWidth, int cellHeight, int maxSelectionWidth, int maxSelectionHeight) { + this->cellWidth = cellWidth; + this->cellHeight = cellHeight; + this->maxSelectionWidth = maxSelectionWidth; + this->maxSelectionHeight = maxSelectionHeight; + } + virtual QPoint getSelectionDimensions(); + virtual void draw() = 0; + +protected: + int cellWidth; + int cellHeight; + int maxSelectionWidth; + int maxSelectionHeight; + int selectionInitialX; + int selectionInitialY; + int selectionOffsetX; + int selectionOffsetY; + + QPoint getSelectionStart(); + void select(int, int, int, int); + void updateSelection(int, int); + QPoint getCellPos(QPointF); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); + virtual void drawSelection(); +}; + +#endif // SELECTABLEPIXMAPITEM_H diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h new file mode 100644 index 00000000..f2d1f54e --- /dev/null +++ b/include/ui/tileseteditor.h @@ -0,0 +1,90 @@ +#ifndef TILESETEDITOR_H +#define TILESETEDITOR_H + +#include +#include "project.h" +#include "paletteeditor.h" +#include "tileseteditormetatileselector.h" +#include "tileseteditortileselector.h" +#include "metatilelayersitem.h" + +namespace Ui { +class TilesetEditor; +} + +class TilesetEditor : public QMainWindow +{ + Q_OBJECT + +public: + explicit TilesetEditor(Project*, QString, QString, QWidget *parent = nullptr); + ~TilesetEditor(); + void setTilesets(QString, QString); + void init(Project*, QString, QString); + +private slots: + void onHoveredMetatileChanged(uint16_t); + void onHoveredMetatileCleared(); + void onSelectedMetatileChanged(uint16_t); + void onHoveredTileChanged(uint16_t); + void onHoveredTileCleared(); + void onSelectedTilesChanged(); + void onMetatileLayerTileChanged(int, int); + void onPaletteEditorClosed(); + void onPaletteEditorChangedPaletteColor(); + void onPaletteEditorChangedPalette(int); + + void on_spinBox_paletteSelector_valueChanged(int arg1); + + void on_checkBox_xFlip_stateChanged(int arg1); + + void on_checkBox_yFlip_stateChanged(int arg1); + + void on_comboBox_metatileBehaviors_currentIndexChanged(const QString &arg1); + + void on_comboBox_layerType_currentIndexChanged(int index); + + void on_actionSave_Tileset_triggered(); + + void on_actionImport_Primary_Tiles_triggered(); + + void on_actionImport_Secondary_Tiles_triggered(); + + void on_actionChange_Metatiles_Count_triggered(); + + void on_actionChange_Palettes_triggered(); + +private: + void closeEvent(QCloseEvent*); + void initMetatileSelector(); + void initTileSelector(); + void initSelectedTileItem(); + void initMetatileLayersItem(); + void drawSelectedTiles(); + void importTilesetTiles(Tileset*, bool); + void refresh(); + Ui::TilesetEditor *ui; + TilesetEditorMetatileSelector *metatileSelector = nullptr; + TilesetEditorTileSelector *tileSelector = nullptr; + MetatileLayersItem *metatileLayersItem = nullptr; + PaletteEditor *paletteEditor = nullptr; + Project *project = nullptr; + Metatile *metatile = nullptr; + int paletteId; + bool tileXFlip; + bool tileYFlip; + bool hasUnsavedChanges; + Tileset *primaryTileset = nullptr; + Tileset *secondaryTileset = nullptr; + QGraphicsScene *metatilesScene = nullptr; + QGraphicsScene *tilesScene = nullptr; + QGraphicsScene *selectedTileScene = nullptr; + QGraphicsPixmapItem *selectedTilePixmapItem = nullptr; + QGraphicsScene *metatileLayersScene = nullptr; + +signals: + void tilesetsSaved(QString, QString); + void closed(); +}; + +#endif // TILESETEDITOR_H diff --git a/include/ui/tileseteditormetatileselector.h b/include/ui/tileseteditormetatileselector.h new file mode 100644 index 00000000..3da82721 --- /dev/null +++ b/include/ui/tileseteditormetatileselector.h @@ -0,0 +1,44 @@ +#ifndef TILESETEDITORMETATILESELECTOR_H +#define TILESETEDITORMETATILESELECTOR_H + +#include "selectablepixmapitem.h" +#include "tileset.h" + +class TilesetEditorMetatileSelector: public SelectablePixmapItem { + Q_OBJECT +public: + TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(32, 32, 1, 1) { + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->numMetatilesWide = 8; + setAcceptHoverEvents(true); + } + void draw(); + void select(uint16_t metatileId); + void setTilesets(Tileset*, Tileset*); + uint16_t getSelectedMetatile(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + +private: + Tileset *primaryTileset = nullptr; + Tileset *secondaryTileset = nullptr; + uint16_t selectedMetatile; + int numMetatilesWide; + void updateSelectedMetatile(); + uint16_t getMetatileId(int x, int y); + QPoint getMetatileIdCoords(uint16_t); + uint16_t getValidMetatileId(uint16_t); + +signals: + void hoveredMetatileChanged(uint16_t); + void hoveredMetatileCleared(); + void selectedMetatileChanged(uint16_t); +}; + +#endif // TILESETEDITORMETATILESELECTOR_H diff --git a/include/ui/tileseteditortileselector.h b/include/ui/tileseteditortileselector.h new file mode 100644 index 00000000..fe8d6bc2 --- /dev/null +++ b/include/ui/tileseteditortileselector.h @@ -0,0 +1,52 @@ +#ifndef TILESETEDITORTILESELECTOR_H +#define TILESETEDITORTILESELECTOR_H + +#include "selectablepixmapitem.h" +#include "tileset.h" + +class TilesetEditorTileSelector: public SelectablePixmapItem { + Q_OBJECT +public: + TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(16, 16, 2, 2) { + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->numTilesWide = 16; + this->paletteId = 0; + this->xFlip = false; + this->yFlip = false; + setAcceptHoverEvents(true); + } + void draw(); + void select(uint16_t metatileId); + void setTilesets(Tileset*, Tileset*); + void setPaletteId(int); + void setTileFlips(bool, bool); + QList getSelectedTiles(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); + void hoverMoveEvent(QGraphicsSceneHoverEvent*); + void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + +private: + Tileset *primaryTileset; + Tileset *secondaryTileset; + QList selectedTiles; + int numTilesWide; + int paletteId; + bool xFlip; + bool yFlip; + void updateSelectedTiles(); + uint16_t getTileId(int x, int y); + QPoint getTileCoords(uint16_t); + QList getCurPaletteTable(); + +signals: + void hoveredTileChanged(uint16_t); + void hoveredTileCleared(); + void selectedTilesChanged(); +}; + +#endif // TILESETEDITORTILESELECTOR_H diff --git a/map.h b/map.h deleted file mode 100755 index b7c36b2e..00000000 --- a/map.h +++ /dev/null @@ -1,236 +0,0 @@ -#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; - int scale_exp = 0; - double scale_base = sqrt(2); // adjust scale factor with this - - 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/objectpropertiesframe.cpp b/objectpropertiesframe.cpp deleted file mode 100755 index 09e71ffa..00000000 --- a/objectpropertiesframe.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "objectpropertiesframe.h" -#include "ui_objectpropertiesframe.h" - -ObjectPropertiesFrame::ObjectPropertiesFrame(QWidget *parent) : - QFrame(parent), - ui(new Ui::ObjectPropertiesFrame) -{ - ui->setupUi(this); -} - -ObjectPropertiesFrame::~ObjectPropertiesFrame() -{ - delete ui; -} diff --git a/objectpropertiesframe.h b/objectpropertiesframe.h deleted file mode 100755 index 63a23bb0..00000000 --- a/objectpropertiesframe.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef OBJECTPROPERTIESFRAME_H -#define OBJECTPROPERTIESFRAME_H - -#include - -namespace Ui { -class ObjectPropertiesFrame; -} - -class ObjectPropertiesFrame : public QFrame -{ - Q_OBJECT - -public: - explicit ObjectPropertiesFrame(QWidget *parent = nullptr); - ~ObjectPropertiesFrame(); - -public: - Ui::ObjectPropertiesFrame *ui; -}; - -#endif // OBJECTPROPERTIESFRAME_H diff --git a/porymap.pro b/porymap.pro old mode 100755 new mode 100644 index f1d68efb..70767deb --- a/porymap.pro +++ b/porymap.pro @@ -14,43 +14,89 @@ 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 +SOURCES += src/core/block.cpp \ + src/core/blockdata.cpp \ + src/core/event.cpp \ + src/core/heallocation.cpp \ + src/core/historyitem.cpp \ + src/core/map.cpp \ + src/core/maplayout.cpp \ + src/core/metatile.cpp \ + src/core/parseutil.cpp \ + src/core/tile.cpp \ + src/core/tileset.cpp \ + src/ui/bordermetatilespixmapitem.cpp \ + src/ui/collisionpixmapitem.cpp \ + src/ui/connectionpixmapitem.cpp \ + src/ui/currentselectedmetatilespixmapitem.cpp \ + src/ui/eventpropertiesframe.cpp \ + src/ui/graphicsview.cpp \ + src/ui/imageproviders.cpp \ + src/ui/mappixmapitem.cpp \ + src/ui/mapsceneeventfilter.cpp \ + src/ui/metatilelayersitem.cpp \ + src/ui/metatileselector.cpp \ + src/ui/movementpermissionsselector.cpp \ + src/ui/neweventtoolbutton.cpp \ + src/ui/noscrollcombobox.cpp \ + src/ui/noscrollspinbox.cpp \ + src/ui/paletteeditor.cpp \ + src/ui/selectablepixmapitem.cpp \ + src/ui/tileseteditor.cpp \ + src/ui/tileseteditormetatileselector.cpp \ + src/ui/tileseteditortileselector.cpp \ + src/editor.cpp \ + src/main.cpp \ + src/mainwindow.cpp \ + src/project.cpp \ + src/settings.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 +HEADERS += include/core/block.h \ + include/core/blockdata.h \ + include/core/event.h \ + include/core/heallocation.h \ + include/core/history.h \ + include/core/historyitem.h \ + include/core/map.h \ + include/core/mapconnection.h \ + include/core/maplayout.h \ + include/core/metatile.h \ + include/core/parseutil.h \ + include/core/tile.h \ + include/core/tileset.h \ + include/ui/bordermetatilespixmapitem.h \ + include/ui/collisionpixmapitem.h \ + include/ui/connectionpixmapitem.h \ + include/ui/currentselectedmetatilespixmapitem.h \ + include/ui/eventpropertiesframe.h \ + include/ui/graphicsview.h \ + include/ui/imageproviders.h \ + include/ui/mappixmapitem.h \ + include/ui/mapsceneeventfilter.h \ + include/ui/metatilelayersitem.h \ + include/ui/metatileselector.h \ + include/ui/movementpermissionsselector.h \ + include/ui/neweventtoolbutton.h \ + include/ui/noscrollcombobox.h \ + include/ui/noscrollspinbox.h \ + include/ui/paletteeditor.h \ + include/ui/selectablepixmapitem.h \ + include/ui/tileseteditor.h \ + include/ui/tileseteditormetatileselector.h \ + include/ui/tileseteditortileselector.h \ + include/editor.h \ + include/mainwindow.h \ + include/project.h \ + include/settings.h -FORMS += mainwindow.ui \ - objectpropertiesframe.ui +FORMS += forms/mainwindow.ui \ + forms/eventpropertiesframe.ui \ + forms/tileseteditor.ui \ + forms/paletteeditor.ui RESOURCES += \ resources/images.qrc + +INCLUDEPATH += include +INCLUDEPATH += include/core +INCLUDEPATH += include/ui diff --git a/resources/icons/add.ico b/resources/icons/add.ico old mode 100755 new mode 100644 diff --git a/resources/icons/cursor.ico b/resources/icons/cursor.ico old mode 100755 new mode 100644 diff --git a/resources/icons/delete.ico b/resources/icons/delete.ico old mode 100755 new mode 100644 diff --git a/resources/icons/fill_color.ico b/resources/icons/fill_color.ico old mode 100755 new mode 100644 diff --git a/resources/icons/folder.ico b/resources/icons/folder.ico old mode 100755 new mode 100644 diff --git a/resources/icons/folder_closed.ico b/resources/icons/folder_closed.ico old mode 100755 new mode 100644 diff --git a/resources/icons/folder_closed_map.ico b/resources/icons/folder_closed_map.ico old mode 100755 new mode 100644 diff --git a/resources/icons/folder_image.ico b/resources/icons/folder_image.ico old mode 100755 new mode 100644 diff --git a/resources/icons/folder_map.ico b/resources/icons/folder_map.ico old mode 100755 new mode 100644 diff --git a/resources/icons/image.ico b/resources/icons/image.ico old mode 100755 new mode 100644 diff --git a/resources/icons/map.ico b/resources/icons/map.ico old mode 100755 new mode 100644 diff --git a/resources/icons/pencil.ico b/resources/icons/pencil.ico old mode 100755 new mode 100644 diff --git a/resources/icons/pipette.ico b/resources/icons/pipette.ico old mode 100755 new mode 100644 diff --git a/resources/icons/viewsprites.ico b/resources/icons/viewsprites.ico old mode 100755 new mode 100644 diff --git a/resources/images.qrc b/resources/images.qrc old mode 100755 new mode 100644 index 78fbe6a1..ac1ff8cb --- a/resources/images.qrc +++ b/resources/images.qrc @@ -1,27 +1,27 @@ - - - icons/folder.ico - icons/folder_closed.ico - icons/folder_closed_map.ico - icons/folder_image.ico - icons/folder_map.ico - icons/image.ico - icons/map.ico - icons/cursor.ico - icons/fill_color.ico - icons/move.ico - icons/pencil.ico - icons/pipette.ico - images/Entities_16x16.png - icons/add.ico - icons/delete.ico - icons/viewsprites.ico - images/collisions.png - icons/fill_color_cursor.ico - icons/pencil_cursor.ico - icons/pipette_cursor.ico - icons/shift.ico - icons/shift_cursor.ico - icons/porymap-icon-1.ico - - + + + icons/folder.ico + icons/folder_closed.ico + icons/folder_closed_map.ico + icons/folder_image.ico + icons/folder_map.ico + icons/image.ico + icons/map.ico + icons/cursor.ico + icons/fill_color.ico + icons/move.ico + icons/pencil.ico + icons/pipette.ico + images/Entities_16x16.png + icons/add.ico + icons/delete.ico + icons/viewsprites.ico + images/collisions.png + icons/fill_color_cursor.ico + icons/pencil_cursor.ico + icons/pipette_cursor.ico + icons/shift.ico + icons/shift_cursor.ico + icons/porymap-icon-1.ico + + diff --git a/resources/images/Entities_16x16.png b/resources/images/Entities_16x16.png old mode 100755 new mode 100644 diff --git a/block.cpp b/src/core/block.cpp old mode 100755 new mode 100644 similarity index 95% rename from block.cpp rename to src/core/block.cpp index ceff25da..c064399a --- a/block.cpp +++ b/src/core/block.cpp @@ -1,32 +1,32 @@ -#include "block.h" - -Block::Block() { -} - -Block::Block(uint16_t word) -{ - tile = word & 0x3ff; - collision = (word >> 10) & 0x3; - elevation = (word >> 12) & 0xf; -} - -Block::Block(const Block &block) { - tile = block.tile; - collision = block.collision; - elevation = block.elevation; -} - -uint16_t Block::rawValue() { - return static_cast( - (tile & 0x3ff) + - ((collision & 0x3) << 10) + - ((elevation & 0xf) << 12)); -} - -bool Block::operator ==(Block other) { - return (tile == other.tile) && (collision == other.collision) && (elevation == other.elevation); -} - -bool Block::operator !=(Block other) { - return !(operator ==(other)); -} +#include "block.h" + +Block::Block() { +} + +Block::Block(uint16_t word) +{ + tile = word & 0x3ff; + collision = (word >> 10) & 0x3; + elevation = (word >> 12) & 0xf; +} + +Block::Block(const Block &block) { + tile = block.tile; + collision = block.collision; + elevation = block.elevation; +} + +uint16_t Block::rawValue() { + return static_cast( + (tile & 0x3ff) + + ((collision & 0x3) << 10) + + ((elevation & 0xf) << 12)); +} + +bool Block::operator ==(Block other) { + return (tile == other.tile) && (collision == other.collision) && (elevation == other.elevation); +} + +bool Block::operator !=(Block other) { + return !(operator ==(other)); +} diff --git a/blockdata.cpp b/src/core/blockdata.cpp old mode 100755 new mode 100644 similarity index 94% rename from blockdata.cpp rename to src/core/blockdata.cpp index 10901741..34aff306 --- a/blockdata.cpp +++ b/src/core/blockdata.cpp @@ -1,55 +1,54 @@ -#include "blockdata.h" -#include - -Blockdata::Blockdata(QObject *parent) : QObject(parent) -{ - blocks = new QList; -} - -void Blockdata::addBlock(uint16_t word) { - Block block(word); - blocks->append(block); -} - -void Blockdata::addBlock(Block block) { - blocks->append(block); -} - -QByteArray Blockdata::serialize() { - QByteArray data; - for (int i = 0; i < blocks->length(); i++) { - Block block = blocks->value(i); - uint16_t word = block.rawValue(); - data.append(static_cast(word & 0xff)); - data.append(static_cast((word >> 8) & 0xff)); - } - return data; -} - -void Blockdata::copyFrom(Blockdata* other) { - blocks->clear(); - for (int i = 0; i < other->blocks->length(); i++) { - addBlock(other->blocks->value(i)); - } -} - -Blockdata* Blockdata::copy() { - Blockdata* blockdata = new Blockdata; - blockdata->copyFrom(this); - return blockdata; -} - -bool Blockdata::equals(Blockdata *other) { - if (!other) { - return false; - } - if (blocks->length() != other->blocks->length()) { - return false; - } - for (int i = 0; i < blocks->length(); i++) { - if (blocks->value(i) != other->blocks->value(i)) { - return false; - } - } - return true; -} +#include "blockdata.h" + +Blockdata::Blockdata(QObject *parent) : QObject(parent) +{ + blocks = new QList; +} + +void Blockdata::addBlock(uint16_t word) { + Block block(word); + blocks->append(block); +} + +void Blockdata::addBlock(Block block) { + blocks->append(block); +} + +QByteArray Blockdata::serialize() { + QByteArray data; + for (int i = 0; i < blocks->length(); i++) { + Block block = blocks->value(i); + uint16_t word = block.rawValue(); + data.append(static_cast(word & 0xff)); + data.append(static_cast((word >> 8) & 0xff)); + } + return data; +} + +void Blockdata::copyFrom(Blockdata* other) { + blocks->clear(); + for (int i = 0; i < other->blocks->length(); i++) { + addBlock(other->blocks->value(i)); + } +} + +Blockdata* Blockdata::copy() { + Blockdata* blockdata = new Blockdata; + blockdata->copyFrom(this); + return blockdata; +} + +bool Blockdata::equals(Blockdata *other) { + if (!other) { + return false; + } + if (blocks->length() != other->blocks->length()) { + return false; + } + for (int i = 0; i < blocks->length(); i++) { + if (blocks->value(i) != other->blocks->value(i)) { + return false; + } + } + return true; +} diff --git a/event.cpp b/src/core/event.cpp old mode 100755 new mode 100644 similarity index 93% rename from event.cpp rename to src/core/event.cpp index 80928d97..47c42166 --- a/event.cpp +++ b/src/core/event.cpp @@ -1,260 +1,245 @@ -#include "event.h" -#include "map.h" - -QString EventType::Object = "event_object"; -QString EventType::Warp = "event_warp"; -QString EventType::CoordScript = "event_trap"; -QString EventType::CoordWeather = "event_trap_weather"; -QString EventType::Sign = "event_sign"; -QString EventType::HiddenItem = "event_hidden_item"; -QString EventType::SecretBase = "event_secret_base"; -QString EventType::HealLocation = "event_heal_location"; - -Event::Event() -{ - this->spriteWidth = 16; - this->spriteHeight = 16; -} - -Event* Event::createNewEvent(QString event_type, QString map_name) -{ - Event *event = new Event; - if (event_type == EventType::Object) { - event = createNewObjectEvent(); - } else if (event_type == EventType::Warp) { - event = createNewWarpEvent(map_name); - } else if (event_type == EventType::HealLocation) { - event = createNewHealLocationEvent(map_name); - } else if (event_type == EventType::CoordScript) { - event = createNewCoordScriptEvent(); - } else if (event_type == EventType::CoordWeather) { - event = createNewCoordWeatherEvent(); - } else if (event_type == EventType::Sign) { - event = createNewSignEvent(); - } else if (event_type == EventType::HiddenItem) { - event = createNewHiddenItemEvent(); - } else if (event_type == EventType::SecretBase) { - event = createNewSecretBaseEvent(); - } - - event->setX(0); - event->setY(0); - event->put("elevation", 3); - return event; -} - -Event* Event::createNewObjectEvent() -{ - Event *event = new Event; - event->put("event_group_type", "object_event_group"); - event->put("event_type", EventType::Object); - event->put("sprite", "EVENT_OBJ_GFX_BOY_1"); - event->put("movement_type", "MOVEMENT_TYPE_LOOK_AROUND"); - event->put("radius_x", 0); - event->put("radius_y", 0); - event->put("script_label", "NULL"); - event->put("event_flag", "0"); - event->put("replacement", "0"); - event->put("is_trainer", "FALSE"); - event->put("sight_radius_tree_id", 0); - return event; -} - -Event* Event::createNewWarpEvent(QString map_name) -{ - Event *event = new Event; - event->put("event_group_type", "warp_event_group"); - event->put("event_type", EventType::Warp); - event->put("destination_warp", 0); - event->put("destination_map_name", map_name); - return event; -} - -Event* Event::createNewHealLocationEvent(QString map_name) -{ - Event *event = new Event; - event->put("event_group_type", "heal_event_group"); - event->put("event_type", EventType::HealLocation); - event->put("loc_name", QString(Map::mapConstantFromName(map_name)).remove(0,4)); - return event; -} - -Event* Event::createNewCoordScriptEvent() -{ - Event *event = new Event; - event->put("event_group_type", "coord_event_group"); - event->put("event_type", EventType::CoordScript); - event->put("script_label", "NULL"); - event->put("script_var", "VAR_TEMP_0"); - event->put("script_var_value", "0"); - return event; -} - -Event* Event::createNewCoordWeatherEvent() -{ - Event *event = new Event; - event->put("event_group_type", "coord_event_group"); - event->put("event_type", EventType::CoordWeather); - event->put("weather", "COORD_EVENT_WEATHER_SUNNY"); - return event; -} - -Event* Event::createNewSignEvent() -{ - Event *event = new Event; - event->put("event_group_type", "bg_event_group"); - event->put("event_type", EventType::Sign); - event->put("player_facing_direction", "BG_EVENT_PLAYER_FACING_ANY"); - event->put("script_label", "NULL"); - return event; -} - -Event* Event::createNewHiddenItemEvent() -{ - Event *event = new Event; - event->put("event_group_type", "bg_event_group"); - event->put("event_type", EventType::HiddenItem); - event->put("item", "ITEM_POTION"); - event->put("flag", "FLAG_HIDDEN_ITEM_0"); - return event; -} - -Event* Event::createNewSecretBaseEvent() -{ - Event *event = new Event; - event->put("event_group_type", "bg_event_group"); - event->put("event_type", EventType::SecretBase); - event->put("secret_base_id", "SECRET_BASE_RED_CAVE2_1"); - return event; -} - -int Event::getPixelX() -{ - return (this->x() * 16) - qMax(0, (this->spriteWidth - 16) / 2); -} - -int Event::getPixelY() -{ - return (this->y() * 16) - qMax(0, this->spriteHeight - 16); -} - -QString Event::buildObjectEventMacro(int item_index) -{ - int radius_x = this->getInt("radius_x"); - int radius_y = this->getInt("radius_y"); - uint16_t x = this->getU16("x"); - uint16_t y = this->getU16("y"); - - QString text = ""; - text += QString("\tobject_event %1").arg(item_index + 1); - text += QString(", %1").arg(this->get("sprite")); - text += QString(", %1").arg(this->get("replacement")); - text += QString(", %1").arg(x); - text += QString(", %1").arg(y); - text += QString(", %1").arg(this->get("elevation")); - text += QString(", %1").arg(this->get("movement_type")); - text += QString(", %1").arg(radius_x); - text += QString(", %1").arg(radius_y); - text += QString(", %1").arg(this->get("is_trainer")); - text += QString(", %1").arg(this->get("sight_radius_tree_id")); - text += QString(", %1").arg(this->get("script_label")); - text += QString(", %1").arg(this->get("event_flag")); - text += "\n"; - return text; -} - -QString Event::buildWarpEventMacro(QMap *mapNamesToMapConstants) -{ - QString text = ""; - text += QString("\twarp_def %1").arg(this->get("x")); - text += QString(", %1").arg(this->get("y")); - text += QString(", %1").arg(this->get("elevation")); - text += QString(", %1").arg(this->get("destination_warp")); - text += QString(", %1").arg(mapNamesToMapConstants->value(this->get("destination_map_name"))); - text += "\n"; - return text; -} - -HealLocation Event::buildHealLocation() -{ - HealLocation hl; - hl.name = this->get("loc_name"); - try { - hl.index = this->get("index").toInt(); - } - catch(...) { - hl.index = 0; - } - hl.x = this->getU16("x"); - hl.y = this->getU16("y"); - return hl; -} - -QString Event::buildCoordScriptEventMacro() -{ - QString text = ""; - text += QString("\tcoord_event %1").arg(this->get("x")); - text += QString(", %1").arg(this->get("y")); - text += QString(", %1").arg(this->get("elevation")); - text += QString(", %1").arg(this->get("script_var")); - text += QString(", %1").arg(this->get("script_var_value")); - text += QString(", %1").arg(this->get("script_label")); - text += "\n"; - return text; -} - -QString Event::buildCoordWeatherEventMacro() -{ - QString text = ""; - text += QString("\tcoord_weather_event %1").arg(this->get("x")); - text += QString(", %1").arg(this->get("y")); - text += QString(", %1").arg(this->get("elevation")); - text += QString(", %1").arg(this->get("weather")); - text += "\n"; - return text; -} - -QString Event::buildSignEventMacro() -{ - QString text = ""; - text += QString("\tbg_event %1").arg(this->get("x")); - text += QString(", %1").arg(this->get("y")); - text += QString(", %1").arg(this->get("elevation")); - text += QString(", %1").arg(this->get("player_facing_direction")); - text += QString(", %1").arg(this->get("script_label")); - text += "\n"; - return text; -} - -QString Event::buildHiddenItemEventMacro() -{ - QString text = ""; - text += QString("\tbg_hidden_item_event %1").arg(this->get("x")); - text += QString(", %1").arg(this->get("y")); - text += QString(", %1").arg(this->get("elevation")); - text += QString(", %1").arg(this->get("item")); - text += QString(", %1").arg(this->get("flag")); - text += "\n"; - return text; -} - -QString Event::buildSecretBaseEventMacro() -{ - QString text = ""; - text += QString("\tbg_secret_base_event %1").arg(this->get("x")); - text += QString(", %1").arg(this->get("y")); - text += QString(", %1").arg(this->get("elevation")); - text += QString(", %1").arg(this->get("secret_base_id")); - text += "\n"; - return text; -} - -void Event::setPixmapFromSpritesheet(QImage spritesheet, int spriteWidth, int spriteHeight) -{ - // Set first palette color fully transparent. - QImage img = spritesheet.copy(0, 0, spriteWidth, spriteHeight); - img.setColor(0, qRgba(0, 0, 0, 0)); - pixmap = QPixmap::fromImage(img); - this->spriteWidth = spriteWidth; - this->spriteHeight = spriteHeight; -} +#include "event.h" +#include "map.h" + +QString EventType::Object = "event_object"; +QString EventType::Warp = "event_warp"; +QString EventType::CoordScript = "event_trap"; +QString EventType::CoordWeather = "event_trap_weather"; +QString EventType::Sign = "event_sign"; +QString EventType::HiddenItem = "event_hidden_item"; +QString EventType::SecretBase = "event_secret_base"; +QString EventType::HealLocation = "event_heal_location"; + +Event::Event() +{ + this->spriteWidth = 16; + this->spriteHeight = 16; +} + +Event* Event::createNewEvent(QString event_type, QString map_name) +{ + Event *event = new Event; + if (event_type == EventType::Object) { + event = createNewObjectEvent(); + } else if (event_type == EventType::Warp) { + event = createNewWarpEvent(map_name); + } else if (event_type == EventType::HealLocation) { + event = createNewHealLocationEvent(map_name); + } else if (event_type == EventType::CoordScript) { + event = createNewCoordScriptEvent(); + } else if (event_type == EventType::CoordWeather) { + event = createNewCoordWeatherEvent(); + } else if (event_type == EventType::Sign) { + event = createNewSignEvent(); + } else if (event_type == EventType::HiddenItem) { + event = createNewHiddenItemEvent(); + } else if (event_type == EventType::SecretBase) { + event = createNewSecretBaseEvent(); + } + + event->setX(0); + event->setY(0); + event->put("elevation", 3); + return event; +} + +Event* Event::createNewObjectEvent() +{ + Event *event = new Event; + event->put("event_group_type", "object_event_group"); + event->put("event_type", EventType::Object); + event->put("sprite", "EVENT_OBJ_GFX_BOY_1"); + event->put("movement_type", "MOVEMENT_TYPE_LOOK_AROUND"); + event->put("radius_x", 0); + event->put("radius_y", 0); + event->put("script_label", "NULL"); + event->put("event_flag", "0"); + event->put("replacement", "0"); + event->put("is_trainer", "FALSE"); + event->put("sight_radius_tree_id", 0); + return event; +} + +Event* Event::createNewWarpEvent(QString map_name) +{ + Event *event = new Event; + event->put("event_group_type", "warp_event_group"); + event->put("event_type", EventType::Warp); + event->put("destination_warp", 0); + event->put("destination_map_name", map_name); + return event; +} + +Event* Event::createNewHealLocationEvent(QString map_name) +{ + Event *event = new Event; + event->put("event_group_type", "heal_event_group"); + event->put("event_type", EventType::HealLocation); + event->put("loc_name", QString(Map::mapConstantFromName(map_name)).remove(0,4)); + return event; +} + +Event* Event::createNewCoordScriptEvent() +{ + Event *event = new Event; + event->put("event_group_type", "coord_event_group"); + event->put("event_type", EventType::CoordScript); + event->put("script_label", "NULL"); + event->put("script_var", "VAR_TEMP_0"); + event->put("script_var_value", "0"); + return event; +} + +Event* Event::createNewCoordWeatherEvent() +{ + Event *event = new Event; + event->put("event_group_type", "coord_event_group"); + event->put("event_type", EventType::CoordWeather); + event->put("weather", "COORD_EVENT_WEATHER_SUNNY"); + return event; +} + +Event* Event::createNewSignEvent() +{ + Event *event = new Event; + event->put("event_group_type", "bg_event_group"); + event->put("event_type", EventType::Sign); + event->put("player_facing_direction", "BG_EVENT_PLAYER_FACING_ANY"); + event->put("script_label", "NULL"); + return event; +} + +Event* Event::createNewHiddenItemEvent() +{ + Event *event = new Event; + event->put("event_group_type", "bg_event_group"); + event->put("event_type", EventType::HiddenItem); + event->put("item", "ITEM_POTION"); + event->put("flag", "FLAG_HIDDEN_ITEM_0"); + return event; +} + +Event* Event::createNewSecretBaseEvent() +{ + Event *event = new Event; + event->put("event_group_type", "bg_event_group"); + event->put("event_type", EventType::SecretBase); + event->put("secret_base_id", "SECRET_BASE_RED_CAVE2_1"); + return event; +} + +int Event::getPixelX() +{ + return (this->x() * 16) - qMax(0, (this->spriteWidth - 16) / 2); +} + +int Event::getPixelY() +{ + return (this->y() * 16) - qMax(0, this->spriteHeight - 16); +} + +QString Event::buildObjectEventMacro(int item_index) +{ + int radius_x = this->getInt("radius_x"); + int radius_y = this->getInt("radius_y"); + uint16_t x = this->getU16("x"); + uint16_t y = this->getU16("y"); + + QString text = ""; + text += QString("\tobject_event %1").arg(item_index + 1); + text += QString(", %1").arg(this->get("sprite")); + text += QString(", %1").arg(this->get("replacement")); + text += QString(", %1").arg(x); + text += QString(", %1").arg(y); + text += QString(", %1").arg(this->get("elevation")); + text += QString(", %1").arg(this->get("movement_type")); + text += QString(", %1").arg(radius_x); + text += QString(", %1").arg(radius_y); + text += QString(", %1").arg(this->get("is_trainer")); + text += QString(", %1").arg(this->get("sight_radius_tree_id")); + text += QString(", %1").arg(this->get("script_label")); + text += QString(", %1").arg(this->get("event_flag")); + text += "\n"; + return text; +} + +QString Event::buildWarpEventMacro(QMap *mapNamesToMapConstants) +{ + QString text = ""; + text += QString("\twarp_def %1").arg(this->get("x")); + text += QString(", %1").arg(this->get("y")); + text += QString(", %1").arg(this->get("elevation")); + text += QString(", %1").arg(this->get("destination_warp")); + text += QString(", %1").arg(mapNamesToMapConstants->value(this->get("destination_map_name"))); + text += "\n"; + return text; +} + +QString Event::buildCoordScriptEventMacro() +{ + QString text = ""; + text += QString("\tcoord_event %1").arg(this->get("x")); + text += QString(", %1").arg(this->get("y")); + text += QString(", %1").arg(this->get("elevation")); + text += QString(", %1").arg(this->get("script_var")); + text += QString(", %1").arg(this->get("script_var_value")); + text += QString(", %1").arg(this->get("script_label")); + text += "\n"; + return text; +} + +QString Event::buildCoordWeatherEventMacro() +{ + QString text = ""; + text += QString("\tcoord_weather_event %1").arg(this->get("x")); + text += QString(", %1").arg(this->get("y")); + text += QString(", %1").arg(this->get("elevation")); + text += QString(", %1").arg(this->get("weather")); + text += "\n"; + return text; +} + +QString Event::buildSignEventMacro() +{ + QString text = ""; + text += QString("\tbg_event %1").arg(this->get("x")); + text += QString(", %1").arg(this->get("y")); + text += QString(", %1").arg(this->get("elevation")); + text += QString(", %1").arg(this->get("player_facing_direction")); + text += QString(", %1").arg(this->get("script_label")); + text += "\n"; + return text; +} + +QString Event::buildHiddenItemEventMacro() +{ + QString text = ""; + text += QString("\tbg_hidden_item_event %1").arg(this->get("x")); + text += QString(", %1").arg(this->get("y")); + text += QString(", %1").arg(this->get("elevation")); + text += QString(", %1").arg(this->get("item")); + text += QString(", %1").arg(this->get("flag")); + text += "\n"; + return text; +} + +QString Event::buildSecretBaseEventMacro() +{ + QString text = ""; + text += QString("\tbg_secret_base_event %1").arg(this->get("x")); + text += QString(", %1").arg(this->get("y")); + text += QString(", %1").arg(this->get("elevation")); + text += QString(", %1").arg(this->get("secret_base_id")); + text += "\n"; + return text; +} + +void Event::setPixmapFromSpritesheet(QImage spritesheet, int spriteWidth, int spriteHeight) +{ + // Set first palette color fully transparent. + QImage img = spritesheet.copy(0, 0, spriteWidth, spriteHeight); + img.setColor(0, qRgba(0, 0, 0, 0)); + pixmap = QPixmap::fromImage(img); + this->spriteWidth = spriteWidth; + this->spriteHeight = spriteHeight; +} diff --git a/heallocation.cpp b/src/core/heallocation.cpp similarity index 51% rename from heallocation.cpp rename to src/core/heallocation.cpp index f611098d..ba739831 100644 --- a/heallocation.cpp +++ b/src/core/heallocation.cpp @@ -8,6 +8,21 @@ HealLocation::HealLocation(QString map, int i, uint16_t x, uint16_t y) this->y = y; } +HealLocation HealLocation::fromEvent(Event *event) +{ + HealLocation hl; + hl.name = event->get("loc_name"); + try { + hl.index = event->get("index").toInt(); + } + catch(...) { + hl.index = 0; + } + hl.x = event->getU16("x"); + hl.y = event->getU16("y"); + return hl; +} + QDebug operator<<(QDebug debug, const HealLocation &hl) { debug << "HealLocation_" + hl.name << "(" << hl.x << ',' << hl.y << ")"; diff --git a/src/core/history.cpp b/src/core/history.cpp new file mode 100644 index 00000000..0991bbd2 --- /dev/null +++ b/src/core/history.cpp @@ -0,0 +1,4 @@ +#include "history.h" +#include "historyitem.h" + + diff --git a/src/core/historyitem.cpp b/src/core/historyitem.cpp new file mode 100644 index 00000000..ded89353 --- /dev/null +++ b/src/core/historyitem.cpp @@ -0,0 +1,11 @@ +#include "historyitem.h" + +HistoryItem::HistoryItem(Blockdata *metatiles, int layoutWidth, int layoutHeight) { + this->metatiles = metatiles; + this->layoutWidth = layoutWidth; + this->layoutHeight = layoutHeight; +} + +HistoryItem::~HistoryItem() { + if (this->metatiles) delete this->metatiles; +} diff --git a/map.cpp b/src/core/map.cpp old mode 100755 new mode 100644 similarity index 64% rename from map.cpp rename to src/core/map.cpp index 54298a5c..185306e5 --- a/map.cpp +++ b/src/core/map.cpp @@ -1,580 +1,432 @@ -#include "map.h" -#include "project.h" - -#include -#include -#include -#include -#include - - -Map::Map(QObject *parent) : QObject(parent) -{ - paint_tile_index = 1; - paint_collision = 0; - paint_elevation = 3; - selected_metatiles_width = 1; - selected_metatiles_height = 1; - selected_metatiles = new QList; - selected_metatiles->append(1); -} - -void Map::setName(QString mapName) { - name = mapName; - constantName = mapConstantFromName(mapName); -} - -QString Map::mapConstantFromName(QString mapName) { - // Transform map names of the form 'GraniteCave_B1F` into map constants like 'MAP_GRANITE_CAVE_B1F'. - QString nameWithUnderscores = mapName.replace(QRegularExpression("([a-z])([A-Z])"), "\\1_\\2"); - QString withMapAndUppercase = "MAP_" + nameWithUnderscores.toUpper(); - QString constantName = withMapAndUppercase.replace(QRegularExpression("_+"), "_"); - - // Handle special cases. - // SSTidal needs to be SS_TIDAL, rather than SSTIDAL - constantName = constantName.replace("SSTIDAL", "SS_TIDAL"); - - return constantName; -} - -QString Map::objectEventsLabelFromName(QString mapName) -{ - return QString("%1_EventObjects").arg(mapName); -} - -QString Map::warpEventsLabelFromName(QString mapName) -{ - return QString("%1_MapWarps").arg(mapName); -} - -QString Map::coordEventsLabelFromName(QString mapName) -{ - return QString("%1_MapCoordEvents").arg(mapName); -} - -QString Map::bgEventsLabelFromName(QString mapName) -{ - return QString("%1_MapBGEvents").arg(mapName); -} - -int Map::getWidth() { - return layout->width.toInt(nullptr, 0); -} - -int Map::getHeight() { - return layout->height.toInt(nullptr, 0); -} - -uint16_t Map::getSelectedBlockIndex(int index) { - if (index < layout->tileset_primary->metatiles->length()) { - return static_cast(index); - } else { - return static_cast(Project::getNumMetatilesPrimary() + index - layout->tileset_primary->metatiles->length()); - } -} - -int Map::getDisplayedBlockIndex(int index) { - if (index < layout->tileset_primary->metatiles->length()) { - return index; - } else { - return index - Project::getNumMetatilesPrimary() + layout->tileset_primary->metatiles->length(); - } -} - -QImage Map::getCollisionMetatileImage(Block block) { - return getCollisionMetatileImage(block.collision, block.elevation); -} - -QImage Map::getCollisionMetatileImage(int collision, int elevation) { - int x = collision * 16; - int y = elevation * 16; - QPixmap collisionImage = QPixmap(":/images/collisions.png").copy(x, y, 16, 16); - return collisionImage.toImage(); -} - -bool Map::blockChanged(int i, Blockdata *cache) { - if (!cache) - return true; - if (!layout->blockdata) - return true; - if (!cache->blocks) - return true; - if (!layout->blockdata->blocks) - return true; - if (cache->blocks->length() <= i) - return true; - if (layout->blockdata->blocks->length() <= i) - return true; - - return layout->blockdata->blocks->value(i) != cache->blocks->value(i); -} - -void Map::cacheBorder() { - if (layout->cached_border) delete layout->cached_border; - layout->cached_border = new Blockdata; - if (layout->border && layout->border->blocks) { - for (int i = 0; i < layout->border->blocks->length(); i++) { - Block block = layout->border->blocks->value(i); - layout->cached_border->blocks->append(block); - } - } -} - -void Map::cacheBlockdata() { - if (layout->cached_blockdata) delete layout->cached_blockdata; - layout->cached_blockdata = new Blockdata; - if (layout->blockdata && layout->blockdata->blocks) { - for (int i = 0; i < layout->blockdata->blocks->length(); i++) { - Block block = layout->blockdata->blocks->value(i); - layout->cached_blockdata->blocks->append(block); - } - } -} - -void Map::cacheCollision() { - if (layout->cached_collision) delete layout->cached_collision; - layout->cached_collision = new Blockdata; - if (layout->blockdata && layout->blockdata->blocks) { - for (int i = 0; i < layout->blockdata->blocks->length(); i++) { - Block block = layout->blockdata->blocks->value(i); - layout->cached_collision->blocks->append(block); - } - } -} - -QPixmap Map::renderCollision(bool ignoreCache) { - bool changed_any = false; - int width_ = getWidth(); - int height_ = getHeight(); - if ( - collision_image.isNull() - || collision_image.width() != width_ * 16 - || collision_image.height() != height_ * 16 - ) { - collision_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - changed_any = true; - } - if (!(layout->blockdata && layout->blockdata->blocks && width_ && height_)) { - collision_pixmap = collision_pixmap.fromImage(collision_image); - return collision_pixmap; - } - QPainter painter(&collision_image); - for (int i = 0; i < layout->blockdata->blocks->length(); i++) { - if (!ignoreCache && layout->cached_collision && !blockChanged(i, layout->cached_collision)) { - continue; - } - changed_any = true; - Block block = layout->blockdata->blocks->value(i); - QImage metatile_image = Metatile::getMetatileImage(block.tile, layout->tileset_primary, layout->tileset_secondary); - QImage collision_metatile_image = getCollisionMetatileImage(block); - int map_y = width_ ? i / width_ : 0; - int map_x = width_ ? i % width_ : 0; - QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); - painter.setOpacity(1); - painter.drawImage(metatile_origin, metatile_image); - painter.save(); - painter.setOpacity(0.55); - painter.drawImage(metatile_origin, collision_metatile_image); - painter.restore(); - } - painter.end(); - cacheCollision(); - if (changed_any) { - collision_pixmap = collision_pixmap.fromImage(collision_image); - } - return collision_pixmap; -} - -QPixmap Map::render(bool ignoreCache = false) { - bool changed_any = false; - int width_ = getWidth(); - int height_ = getHeight(); - if ( - image.isNull() - || image.width() != width_ * 16 - || image.height() != height_ * 16 - ) { - image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - changed_any = true; - } - if (!(layout->blockdata && layout->blockdata->blocks && width_ && height_)) { - pixmap = pixmap.fromImage(image); - return pixmap; - } - - QPainter painter(&image); - for (int i = 0; i < layout->blockdata->blocks->length(); i++) { - if (!ignoreCache && !blockChanged(i, layout->cached_blockdata)) { - continue; - } - changed_any = true; - Block block = layout->blockdata->blocks->value(i); - QImage metatile_image = Metatile::getMetatileImage(block.tile, layout->tileset_primary, layout->tileset_secondary); - int map_y = width_ ? i / width_ : 0; - int map_x = width_ ? i % width_ : 0; - QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); - painter.drawImage(metatile_origin, metatile_image); - } - painter.end(); - if (changed_any) { - cacheBlockdata(); - pixmap = pixmap.fromImage(image); - } - - return pixmap; -} - -QPixmap Map::renderBorder() { - bool changed_any = false; - int width_ = 2; - int height_ = 2; - if (layout->border_image.isNull()) { - layout->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - changed_any = true; - } - if (!(layout->border && layout->border->blocks)) { - layout->border_pixmap = layout->border_pixmap.fromImage(layout->border_image); - return layout->border_pixmap; - } - QPainter painter(&layout->border_image); - for (int i = 0; i < layout->border->blocks->length(); i++) { - if (!blockChanged(i, layout->cached_border)) { - continue; - } - changed_any = true; - Block block = layout->border->blocks->value(i); - QImage metatile_image = Metatile::getMetatileImage(block.tile, layout->tileset_primary, layout->tileset_secondary); - int map_y = i / width_; - int map_x = i % width_; - painter.drawImage(QPoint(map_x * 16, map_y * 16), metatile_image); - } - painter.end(); - if (changed_any) { - cacheBorder(); - layout->border_pixmap = layout->border_pixmap.fromImage(layout->border_image); - } - return layout->border_pixmap; -} - -QPixmap Map::renderConnection(Connection connection) { - render(); - int x, y, w, h; - if (connection.direction == "up") { - x = 0; - y = getHeight() - 6; - w = getWidth(); - h = 6; - } else if (connection.direction == "down") { - x = 0; - y = 0; - w = getWidth(); - h = 6; - } else if (connection.direction == "left") { - x = getWidth() - 6; - y = 0; - w = 6; - h = getHeight(); - } else if (connection.direction == "right") { - x = 0; - y = 0; - w = 6; - h = getHeight(); - } else { - // this should not happen - x = 0; - y = 0; - w = getWidth(); - h = getHeight(); - } - QImage connection_image = image.copy(x * 16, y * 16, w * 16, h * 16); - //connection_image = connection_image.convertToFormat(QImage::Format_Grayscale8); - return QPixmap::fromImage(connection_image); -} - -QPixmap Map::renderCollisionMetatiles() { - int width_ = 2; - int height_ = 16; - QImage image(width_ * 32, height_ * 32, QImage::Format_RGBA8888); - QPainter painter(&image); - for (int i = 0; i < width_; i++) { - for (int j = 0; j < height_; j++) { - QPoint origin(i * 32, j * 32); - QImage metatile_image = getCollisionMetatileImage(i, j).scaled(32, 32); - painter.drawImage(origin, metatile_image); - } - } - drawSelection(paint_collision + paint_elevation * width_, width_, 1, 1, &painter, 32); - painter.end(); - return QPixmap::fromImage(image); -} - -void Map::drawSelection(int i, int w, int selectionWidth, int selectionHeight, QPainter *painter, int gridWidth) { - int x = i % w; - int y = i / w; - painter->save(); - - QColor penColor = QColor(0xff, 0xff, 0xff); - painter->setPen(penColor); - int rectWidth = selectionWidth * gridWidth; - int rectHeight = selectionHeight * gridWidth; - painter->drawRect(x * gridWidth, y * gridWidth, rectWidth - 1, rectHeight -1); - painter->setPen(QColor(0, 0, 0)); - painter->drawRect(x * gridWidth - 1, y * gridWidth - 1, rectWidth + 1, rectHeight + 1); - painter->drawRect(x * gridWidth + 1, y * gridWidth + 1, rectWidth - 3, rectHeight - 3); - painter->restore(); -} - -QPixmap Map::renderMetatiles() { - if (!layout->tileset_primary || !layout->tileset_primary->metatiles - || !layout->tileset_secondary || !layout->tileset_secondary->metatiles) { - return QPixmap(); - } - int primary_length = layout->tileset_primary->metatiles->length(); - int length_ = primary_length + layout->tileset_secondary->metatiles->length(); - int width_ = 8; - int height_ = length_ / width_; - QImage image(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - QPainter painter(&image); - for (int i = 0; i < length_; i++) { - int tile = i; - if (i >= primary_length) { - tile += Project::getNumMetatilesPrimary() - primary_length; - } - QImage metatile_image = Metatile::getMetatileImage(tile, layout->tileset_primary, layout->tileset_secondary); - int map_y = i / width_; - int map_x = i % width_; - QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); - painter.drawImage(metatile_origin, metatile_image); - } - - drawSelection(paint_tile_index, width_, paint_tile_width, paint_tile_height, &painter, 16); - - painter.end(); - return QPixmap::fromImage(image); -} - -void Map::setNewDimensionsBlockdata(int newWidth, int newHeight) { - int oldWidth = getWidth(); - int oldHeight = getHeight(); - - Blockdata* newBlockData = new Blockdata; - - for (int y = 0; y < newHeight; y++) - for (int x = 0; x < newWidth; x++) { - if (x < oldWidth && y < oldHeight) { - int index = y * oldWidth + x; - newBlockData->addBlock(layout->blockdata->blocks->value(index)); - } else { - newBlockData->addBlock(0); - } - } - - layout->blockdata->copyFrom(newBlockData); -} - -void Map::setDimensions(int newWidth, int newHeight, bool setNewBlockdata) { - if (setNewBlockdata) { - setNewDimensionsBlockdata(newWidth, newHeight); - } - - layout->width = QString::number(newWidth); - layout->height = QString::number(newHeight); - - emit mapChanged(this); -} - -Block* Map::getBlock(int x, int y) { - if (layout->blockdata && layout->blockdata->blocks) { - if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { - int i = y * getWidth() + x; - return new Block(layout->blockdata->blocks->value(i)); - } - } - return nullptr; -} - -void Map::_setBlock(int x, int y, Block block) { - int i = y * getWidth() + x; - if (layout->blockdata && layout->blockdata->blocks) { - layout->blockdata->blocks->replace(i, block); - } -} - -void Map::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { - QList todo; - todo.append(QPoint(x, y)); - while (todo.length()) { - QPoint point = todo.takeAt(0); - x = point.x(); - y = point.y(); - Block *block = getBlock(x, y); - if (!block) { - continue; - } - - uint old_coll = block->collision; - uint old_elev = block->elevation; - if (old_coll == collision && old_elev == elevation) { - continue; - } - - block->collision = collision; - block->elevation = elevation; - _setBlock(x, y, *block); - if ((block = getBlock(x + 1, y)) && block->collision == old_coll && block->elevation == old_elev) { - todo.append(QPoint(x + 1, y)); - } - if ((block = getBlock(x - 1, y)) && block->collision == old_coll && block->elevation == old_elev) { - todo.append(QPoint(x - 1, y)); - } - if ((block = getBlock(x, y + 1)) && block->collision == old_coll && block->elevation == old_elev) { - todo.append(QPoint(x, y + 1)); - } - if ((block = getBlock(x, y - 1)) && block->collision == old_coll && block->elevation == old_elev) { - todo.append(QPoint(x, y - 1)); - } - } -} - - -void Map::undo() { - HistoryItem *commit = history.back(); - if (!commit) - return; - - if (layout->blockdata) { - layout->blockdata->copyFrom(commit->metatiles); - if (commit->layoutWidth != this->getWidth() || commit->layoutHeight != this->getHeight()) - { - this->setDimensions(commit->layoutWidth, commit->layoutHeight, false); - emit mapNeedsRedrawing(); - } - - emit mapChanged(this); - } -} - -void Map::redo() { - HistoryItem *commit = history.next(); - if (!commit) - return; - - if (layout->blockdata) { - layout->blockdata->copyFrom(commit->metatiles); - if (commit->layoutWidth != this->getWidth() || commit->layoutHeight != this->getHeight()) - { - this->setDimensions(commit->layoutWidth, commit->layoutHeight, false); - emit mapNeedsRedrawing(); - } - - emit mapChanged(this); - } -} - -void Map::commit() { - if (layout->blockdata) { - HistoryItem *item = history.current(); - bool atCurrentHistory = item - && layout->blockdata->equals(item->metatiles) - && this->getWidth() == item->layoutWidth - && this->getHeight() == item->layoutHeight; - if (!atCurrentHistory) { - HistoryItem *commit = new HistoryItem(layout->blockdata->copy(), this->getWidth(), this->getHeight()); - history.push(commit); - emit mapChanged(this); - } - } -} - -void Map::setBlock(int x, int y, Block block) { - Block *old_block = getBlock(x, y); - if (old_block && (*old_block) != block) { - _setBlock(x, y, block); - commit(); - } -} - -void Map::floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { - Block *block = getBlock(x, y); - if (block && (block->collision != collision || block->elevation != elevation)) { - _floodFillCollisionElevation(x, y, collision, elevation); - commit(); - } -} - -QList Map::getAllEvents() { - QList all; - for (QList list : events.values()) { - all += list; - } - return all; -} - -void Map::removeEvent(Event *event) { - for (QString key : events.keys()) { - events[key].removeAll(event); - } -} - -void Map::addEvent(Event *event) { - events[event->get("event_group_type")].append(event); -} - -bool Map::hasUnsavedChanges() { - return !history.isSaved() || !isPersistedToFile || layout->has_unsaved_changes; -} - -void Map::hoveredTileChanged(int x, int y, int block) { - emit statusBarMessage(QString("X: %1, Y: %2, Metatile: 0x%3, Scale = %4x") - .arg(x) - .arg(y) - .arg(QString("%1").arg(block, 3, 16, QChar('0')).toUpper()) - .arg(QString::number(pow(this->scale_base,this->scale_exp)))); -} - -void Map::clearHoveredTile() { - emit statusBarMessage(QString("")); -} - -void Map::hoveredMetatileChanged(int blockIndex) { - uint16_t tile = getSelectedBlockIndex(blockIndex); - emit statusBarMessage(QString("Metatile: 0x%1") - .arg(QString("%1").arg(tile, 3, 16, QChar('0')).toUpper())); -} - -void Map::clearHoveredMetatile() { - emit statusBarMessage(QString("")); -} - -void Map::hoveredMovementPermissionTileChanged(int collision, int elevation) { - QString message; - if (collision == 0 && elevation == 0) { - message = "Collision: Transition between elevations"; - } else if (collision == 0 && elevation == 15) { - message = "Collision: Multi-Level (Bridge)"; - } else if (collision == 0 && elevation == 1) { - message = "Collision: Surf"; - } else if (collision == 0) { - message = QString("Collision: Passable, Elevation: %1").arg(elevation); - } else { - message = QString("Collision: Impassable, Elevation: %1").arg(elevation); - } - emit statusBarMessage(message); -} - -void Map::clearHoveredMovementPermissionTile() { - emit statusBarMessage(QString("")); -} - -void Map::setSelectedMetatilesFromTilePicker() { - this->selected_metatiles_width = this->paint_tile_width; - this->selected_metatiles_height = this->paint_tile_height; - this->selected_metatiles->clear(); - for (int j = 0; j < this->paint_tile_height; j++) { - for (int i = 0; i < this->paint_tile_width; i++) { - uint16_t metatile = this->getSelectedBlockIndex(this->paint_tile_index + i + (j * 8)); - this->selected_metatiles->append(metatile); - } - } -} - +#include "history.h" +#include "historyitem.h" +#include "map.h" +#include "project.h" +#include "imageproviders.h" + +#include +#include +#include +#include +#include + + +Map::Map(QObject *parent) : QObject(parent) +{ +} + +void Map::setName(QString mapName) { + name = mapName; + constantName = mapConstantFromName(mapName); +} + +QString Map::mapConstantFromName(QString mapName) { + // Transform map names of the form 'GraniteCave_B1F` into map constants like 'MAP_GRANITE_CAVE_B1F'. + QString nameWithUnderscores = mapName.replace(QRegularExpression("([a-z])([A-Z])"), "\\1_\\2"); + QString withMapAndUppercase = "MAP_" + nameWithUnderscores.toUpper(); + QString constantName = withMapAndUppercase.replace(QRegularExpression("_+"), "_"); + + // Handle special cases. + // SSTidal needs to be SS_TIDAL, rather than SSTIDAL + constantName = constantName.replace("SSTIDAL", "SS_TIDAL"); + + return constantName; +} + +QString Map::objectEventsLabelFromName(QString mapName) +{ + return QString("%1_EventObjects").arg(mapName); +} + +QString Map::warpEventsLabelFromName(QString mapName) +{ + return QString("%1_MapWarps").arg(mapName); +} + +QString Map::coordEventsLabelFromName(QString mapName) +{ + return QString("%1_MapCoordEvents").arg(mapName); +} + +QString Map::bgEventsLabelFromName(QString mapName) +{ + return QString("%1_MapBGEvents").arg(mapName); +} + +int Map::getWidth() { + return layout->width.toInt(nullptr, 0); +} + +int Map::getHeight() { + return layout->height.toInt(nullptr, 0); +} + +bool Map::blockChanged(int i, Blockdata *cache) { + if (!cache) + return true; + if (!layout->blockdata) + return true; + if (!cache->blocks) + return true; + if (!layout->blockdata->blocks) + return true; + if (cache->blocks->length() <= i) + return true; + if (layout->blockdata->blocks->length() <= i) + return true; + + return layout->blockdata->blocks->value(i) != cache->blocks->value(i); +} + +void Map::cacheBorder() { + if (layout->cached_border) delete layout->cached_border; + layout->cached_border = new Blockdata; + if (layout->border && layout->border->blocks) { + for (int i = 0; i < layout->border->blocks->length(); i++) { + Block block = layout->border->blocks->value(i); + layout->cached_border->blocks->append(block); + } + } +} + +void Map::cacheBlockdata() { + if (layout->cached_blockdata) delete layout->cached_blockdata; + layout->cached_blockdata = new Blockdata; + if (layout->blockdata && layout->blockdata->blocks) { + for (int i = 0; i < layout->blockdata->blocks->length(); i++) { + Block block = layout->blockdata->blocks->value(i); + layout->cached_blockdata->blocks->append(block); + } + } +} + +void Map::cacheCollision() { + if (layout->cached_collision) delete layout->cached_collision; + layout->cached_collision = new Blockdata; + if (layout->blockdata && layout->blockdata->blocks) { + for (int i = 0; i < layout->blockdata->blocks->length(); i++) { + Block block = layout->blockdata->blocks->value(i); + layout->cached_collision->blocks->append(block); + } + } +} + +QPixmap Map::renderCollision(bool ignoreCache) { + bool changed_any = false; + int width_ = getWidth(); + int height_ = getHeight(); + if ( + collision_image.isNull() + || collision_image.width() != width_ * 16 + || collision_image.height() != height_ * 16 + ) { + collision_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + if (!(layout->blockdata && layout->blockdata->blocks && width_ && height_)) { + collision_pixmap = collision_pixmap.fromImage(collision_image); + return collision_pixmap; + } + QPainter painter(&collision_image); + for (int i = 0; i < layout->blockdata->blocks->length(); i++) { + if (!ignoreCache && layout->cached_collision && !blockChanged(i, layout->cached_collision)) { + continue; + } + changed_any = true; + Block block = layout->blockdata->blocks->value(i); + QImage metatile_image = getMetatileImage(block.tile, layout->tileset_primary, layout->tileset_secondary); + QImage collision_metatile_image = getCollisionMetatileImage(block); + int map_y = width_ ? i / width_ : 0; + int map_x = width_ ? i % width_ : 0; + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + painter.setOpacity(1); + painter.drawImage(metatile_origin, metatile_image); + painter.save(); + painter.setOpacity(0.55); + painter.drawImage(metatile_origin, collision_metatile_image); + painter.restore(); + } + painter.end(); + cacheCollision(); + if (changed_any) { + collision_pixmap = collision_pixmap.fromImage(collision_image); + } + return collision_pixmap; +} + +QPixmap Map::render(bool ignoreCache = false) { + bool changed_any = false; + int width_ = getWidth(); + int height_ = getHeight(); + if ( + image.isNull() + || image.width() != width_ * 16 + || image.height() != height_ * 16 + ) { + image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + if (!(layout->blockdata && layout->blockdata->blocks && width_ && height_)) { + pixmap = pixmap.fromImage(image); + return pixmap; + } + + QPainter painter(&image); + for (int i = 0; i < layout->blockdata->blocks->length(); i++) { + if (!ignoreCache && !blockChanged(i, layout->cached_blockdata)) { + continue; + } + changed_any = true; + Block block = layout->blockdata->blocks->value(i); + QImage metatile_image = getMetatileImage(block.tile, layout->tileset_primary, layout->tileset_secondary); + int map_y = width_ ? i / width_ : 0; + int map_x = width_ ? i % width_ : 0; + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + painter.drawImage(metatile_origin, metatile_image); + } + painter.end(); + if (changed_any) { + cacheBlockdata(); + pixmap = pixmap.fromImage(image); + } + + return pixmap; +} + +QPixmap Map::renderBorder() { + bool changed_any = false; + int width_ = 2; + int height_ = 2; + if (layout->border_image.isNull()) { + layout->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + if (!(layout->border && layout->border->blocks)) { + layout->border_pixmap = layout->border_pixmap.fromImage(layout->border_image); + return layout->border_pixmap; + } + QPainter painter(&layout->border_image); + for (int i = 0; i < layout->border->blocks->length(); i++) { + if (!blockChanged(i, layout->cached_border)) { + continue; + } + changed_any = true; + Block block = layout->border->blocks->value(i); + QImage metatile_image = getMetatileImage(block.tile, layout->tileset_primary, layout->tileset_secondary); + int map_y = i / width_; + int map_x = i % width_; + painter.drawImage(QPoint(map_x * 16, map_y * 16), metatile_image); + } + painter.end(); + if (changed_any) { + cacheBorder(); + layout->border_pixmap = layout->border_pixmap.fromImage(layout->border_image); + } + return layout->border_pixmap; +} + +QPixmap Map::renderConnection(MapConnection connection) { + render(true); + int x, y, w, h; + if (connection.direction == "up") { + x = 0; + y = getHeight() - 6; + w = getWidth(); + h = 6; + } else if (connection.direction == "down") { + x = 0; + y = 0; + w = getWidth(); + h = 6; + } else if (connection.direction == "left") { + x = getWidth() - 6; + y = 0; + w = 6; + h = getHeight(); + } else if (connection.direction == "right") { + x = 0; + y = 0; + w = 6; + h = getHeight(); + } else { + // this should not happen + x = 0; + y = 0; + w = getWidth(); + h = getHeight(); + } + QImage connection_image = image.copy(x * 16, y * 16, w * 16, h * 16); + //connection_image = connection_image.convertToFormat(QImage::Format_Grayscale8); + return QPixmap::fromImage(connection_image); +} + +void Map::setNewDimensionsBlockdata(int newWidth, int newHeight) { + int oldWidth = getWidth(); + int oldHeight = getHeight(); + + Blockdata* newBlockData = new Blockdata; + + for (int y = 0; y < newHeight; y++) + for (int x = 0; x < newWidth; x++) { + if (x < oldWidth && y < oldHeight) { + int index = y * oldWidth + x; + newBlockData->addBlock(layout->blockdata->blocks->value(index)); + } else { + newBlockData->addBlock(0); + } + } + + layout->blockdata->copyFrom(newBlockData); +} + +void Map::setDimensions(int newWidth, int newHeight, bool setNewBlockdata) { + if (setNewBlockdata) { + setNewDimensionsBlockdata(newWidth, newHeight); + } + + layout->width = QString::number(newWidth); + layout->height = QString::number(newHeight); + + emit mapChanged(this); +} + +Block* Map::getBlock(int x, int y) { + if (layout->blockdata && layout->blockdata->blocks) { + if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { + int i = y * getWidth() + x; + return new Block(layout->blockdata->blocks->value(i)); + } + } + return nullptr; +} + +void Map::_setBlock(int x, int y, Block block) { + int i = y * getWidth() + x; + if (layout->blockdata && layout->blockdata->blocks) { + layout->blockdata->blocks->replace(i, block); + } +} + +void Map::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { + QList todo; + todo.append(QPoint(x, y)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + x = point.x(); + y = point.y(); + Block *block = getBlock(x, y); + if (!block) { + continue; + } + + uint old_coll = block->collision; + uint old_elev = block->elevation; + if (old_coll == collision && old_elev == elevation) { + continue; + } + + block->collision = collision; + block->elevation = elevation; + _setBlock(x, y, *block); + if ((block = getBlock(x + 1, y)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x + 1, y)); + } + if ((block = getBlock(x - 1, y)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x - 1, y)); + } + if ((block = getBlock(x, y + 1)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x, y + 1)); + } + if ((block = getBlock(x, y - 1)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x, y - 1)); + } + } +} + + +void Map::undo() { + HistoryItem *commit = metatileHistory.back(); + if (!commit) + return; + + if (layout->blockdata) { + layout->blockdata->copyFrom(commit->metatiles); + if (commit->layoutWidth != this->getWidth() || commit->layoutHeight != this->getHeight()) + { + this->setDimensions(commit->layoutWidth, commit->layoutHeight, false); + emit mapNeedsRedrawing(); + } + + emit mapChanged(this); + } +} + +void Map::redo() { + HistoryItem *commit = metatileHistory.next(); + if (!commit) + return; + + if (layout->blockdata) { + layout->blockdata->copyFrom(commit->metatiles); + if (commit->layoutWidth != this->getWidth() || commit->layoutHeight != this->getHeight()) + { + this->setDimensions(commit->layoutWidth, commit->layoutHeight, false); + emit mapNeedsRedrawing(); + } + + emit mapChanged(this); + } +} + +void Map::commit() { + if (layout->blockdata) { + HistoryItem *item = metatileHistory.current(); + bool atCurrentHistory = item + && layout->blockdata->equals(item->metatiles) + && this->getWidth() == item->layoutWidth + && this->getHeight() == item->layoutHeight; + if (!atCurrentHistory) { + HistoryItem *commit = new HistoryItem(layout->blockdata->copy(), this->getWidth(), this->getHeight()); + metatileHistory.push(commit); + emit mapChanged(this); + } + } +} + +void Map::setBlock(int x, int y, Block block) { + Block *old_block = getBlock(x, y); + if (old_block && (*old_block) != block) { + _setBlock(x, y, block); + commit(); + } +} + +void Map::floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { + Block *block = getBlock(x, y); + if (block && (block->collision != collision || block->elevation != elevation)) { + _floodFillCollisionElevation(x, y, collision, elevation); + commit(); + } +} + +QList Map::getAllEvents() { + QList all; + for (QList list : events.values()) { + all += list; + } + return all; +} + +void Map::removeEvent(Event *event) { + for (QString key : events.keys()) { + events[key].removeAll(event); + } +} + +void Map::addEvent(Event *event) { + events[event->get("event_group_type")].append(event); +} + +bool Map::hasUnsavedChanges() { + return !metatileHistory.isSaved() || !isPersistedToFile || layout->has_unsaved_changes; +} diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp new file mode 100644 index 00000000..b4fdb18d --- /dev/null +++ b/src/core/maplayout.cpp @@ -0,0 +1,2 @@ +#include "maplayout.h" + diff --git a/src/core/metatile.cpp b/src/core/metatile.cpp new file mode 100644 index 00000000..a44b4fa3 --- /dev/null +++ b/src/core/metatile.cpp @@ -0,0 +1,27 @@ +#include "metatile.h" +#include "tileset.h" +#include "project.h" + +Metatile::Metatile() +{ + tiles = new QList; +} + +Metatile* Metatile::copy() { + Metatile *copy = new Metatile; + copy->behavior = this->behavior; + copy->layerType = this->layerType; + copy->tiles = new QList; + for (Tile tile : *this->tiles) { + copy->tiles->append(tile); + } + return copy; +} + +int Metatile::getBlockIndex(int index) { + if (index < Project::getNumMetatilesPrimary()) { + return index; + } else { + return index - Project::getNumMetatilesPrimary(); + } +} diff --git a/parseutil.cpp b/src/core/parseutil.cpp old mode 100755 new mode 100644 similarity index 97% rename from parseutil.cpp rename to src/core/parseutil.cpp index 4ff232f6..a64ce33c --- a/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -1,217 +1,217 @@ -#include "parseutil.h" - -#include -#include -#include - -ParseUtil::ParseUtil() -{ -} - -void ParseUtil::strip_comment(QString *line) { - bool in_string = false; - for (int i = 0; i < line->length(); i++) { - if (line->at(i) == '"') { - in_string = !in_string; - } else if (line->at(i) == '@') { - if (!in_string) { - line->truncate(i); - break; - } - } - } -} - -QList* ParseUtil::parseAsm(QString text) { - QList *parsed = new QList; - QStringList lines = text.split('\n'); - for (QString line : lines) { - QString label; - //QString macro; - //QStringList *params; - strip_comment(&line); - if (line.trimmed().isEmpty()) { - } else if (line.contains(':')) { - label = line.left(line.indexOf(':')); - QStringList *list = new QStringList; - list->append(".label"); // This is not a real keyword. It's used only to make the output more regular. - list->append(label); - parsed->append(*list); - // There should not be anything else on the line. - // gas will raise a syntax error if there is. - } else { - line = line.trimmed(); - //parsed->append(line.split(QRegExp("\\s*,\\s*"))); - QString macro; - QStringList params; - int index = line.indexOf(QRegExp("\\s+")); - macro = line.left(index); - params = line.right(line.length() - index).trimmed().split(QRegExp("\\s*,\\s*")); - params.prepend(macro); - parsed->append(params); - } - //if (macro != NULL) { - // if (macros->contains(macro)) { - // void* function = macros->value(macro); - // if (function != NULL) { - // std::function function(params); - // } - // } - //} - } - return parsed; -} - -int ParseUtil::evaluateDefine(QString define, QMap* knownDefines) { - QList tokens = tokenizeExpression(define, knownDefines); - QList postfixExpression = generatePostfix(tokens); - return evaluatePostfix(postfixExpression); -} - -// arg here is the text in the file src/data/heal_locations.h -// returns a list of HealLocations (mapname, x, y) -QList* ParseUtil::parseHealLocs(QString text) { - QList *parsed = new QList; - QStringList lines = text.split('\n'); - - int i = 1; - for (auto line : lines){ - if (line.contains("MAP_GROUP")){ - QList li = line.replace(" ","").chopped(2).remove('{').split(','); - HealLocation hloc = HealLocation(li[1].remove("MAP_NUM(").remove(")"), i, li[2].toUShort(), li[3].toUShort()); - parsed->append(hloc); - i++; - } - } - return parsed; -} - -QList ParseUtil::tokenizeExpression(QString expression, QMap* knownIdentifiers) { - QList tokens; - - QStringList tokenTypes = (QStringList() << "hex" << "decimal" << "identifier" << "operator" << "leftparen" << "rightparen"); - QRegularExpression re("^(?0x[0-9a-fA-F]+)|(?[0-9]+)|(?[a-zA-Z_0-9]+)|(?[+\\-*\\/<>|^%]+)|(?\\()|(?\\))"); - - expression = expression.trimmed(); - while (!expression.isEmpty()) { - QRegularExpressionMatch match = re.match(expression); - if (!match.hasMatch()) { - qDebug() << "Failed to tokenize expression: " << expression; - break; - } - for (QString tokenType : tokenTypes) { - QString token = match.captured(tokenType); - if (!token.isEmpty()) { - if (tokenType == "identifier") { - if (knownIdentifiers->contains(token)) { - QString actualToken = QString("%1").arg(knownIdentifiers->value(token)); - expression = expression.replace(0, token.length(), actualToken); - token = actualToken; - tokenType = "decimal"; - } else { - qDebug() << "Unknown identifier found in expression: " << token; - } - } - - tokens.append(Token(token, tokenType)); - expression = expression.remove(0, token.length()).trimmed(); - break; - } - } - } - return tokens; -} - -QMap Token::precedenceMap = QMap( -{ - {"*", 3}, - {"/", 3}, - {"+", 4}, - {"-", 4}, - {"<<", 5}, - {">>", 5}, - {"&", 8}, - {"^", 9}, - {"|", 10} -}); - -// Shunting-yard algorithm for generating postfix notation. -// https://en.wikipedia.org/wiki/Shunting-yard_algorithm -QList ParseUtil::generatePostfix(QList tokens) { - QList output; - QStack operatorStack; - for (Token token : tokens) { - if (token.type == TokenType::Number) { - output.append(token); - } else if (token.value == "(") { - operatorStack.push(token); - } else if (token.value == ")") { - while (!operatorStack.empty() && operatorStack.top().value != "(") { - output.append(operatorStack.pop()); - } - if (!operatorStack.empty()) { - // pop the left parenthesis token - operatorStack.pop(); - } else { - qDebug() << "Mismatched parentheses detected in expression!"; - } - } else { - // token is an operator - while (!operatorStack.isEmpty() - && operatorStack.top().operatorPrecedence <= token.operatorPrecedence - && operatorStack.top().value != "(") { - output.append(operatorStack.pop()); - } - operatorStack.push(token); - } - } - - while (!operatorStack.isEmpty()) { - if (operatorStack.top().value == "(" || operatorStack.top().value == ")") { - qDebug() << "Mismatched parentheses detected in expression!"; - } else { - output.append(operatorStack.pop()); - } - } - - return output; -} - -// Evaluate postfix expression. -// https://en.wikipedia.org/wiki/Reverse_Polish_notation#Postfix_evaluation_algorithm -int ParseUtil::evaluatePostfix(QList postfix) { - QStack stack; - for (Token token : postfix) { - if (token.type == TokenType::Operator) { - int op2 = stack.pop().value.toInt(nullptr, 0); - int op1 = stack.pop().value.toInt(nullptr, 0); - int result = 0; - if (token.value == "*") { - result = op1 * op2; - } else if (token.value == "/") { - result = op1 / op2; - } else if (token.value == "+") { - result = op1 + op2; - } else if (token.value == "-") { - result = op1 - op2; - } else if (token.value == "<<") { - result = op1 << op2; - } else if (token.value == ">>") { - result = op1 >> op2; - } else if (token.value == "&") { - result = op1 & op2; - } else if (token.value == "^") { - result = op1 ^ op2; - } else if (token.value == "|") { - result = op1 | op2; - } else { - qDebug() << "Unsupported postfix operator: " << token.value; - } - stack.push(Token(QString("%1").arg(result), "decimal")); - } else { - stack.push(token); - } - } - - return stack.pop().value.toInt(nullptr, 0); -} +#include "parseutil.h" + +#include +#include +#include + +ParseUtil::ParseUtil() +{ +} + +void ParseUtil::strip_comment(QString *line) { + bool in_string = false; + for (int i = 0; i < line->length(); i++) { + if (line->at(i) == '"') { + in_string = !in_string; + } else if (line->at(i) == '@') { + if (!in_string) { + line->truncate(i); + break; + } + } + } +} + +QList* ParseUtil::parseAsm(QString text) { + QList *parsed = new QList; + QStringList lines = text.split('\n'); + for (QString line : lines) { + QString label; + //QString macro; + //QStringList *params; + strip_comment(&line); + if (line.trimmed().isEmpty()) { + } else if (line.contains(':')) { + label = line.left(line.indexOf(':')); + QStringList *list = new QStringList; + list->append(".label"); // This is not a real keyword. It's used only to make the output more regular. + list->append(label); + parsed->append(*list); + // There should not be anything else on the line. + // gas will raise a syntax error if there is. + } else { + line = line.trimmed(); + //parsed->append(line.split(QRegExp("\\s*,\\s*"))); + QString macro; + QStringList params; + int index = line.indexOf(QRegExp("\\s+")); + macro = line.left(index); + params = line.right(line.length() - index).trimmed().split(QRegExp("\\s*,\\s*")); + params.prepend(macro); + parsed->append(params); + } + //if (macro != NULL) { + // if (macros->contains(macro)) { + // void* function = macros->value(macro); + // if (function != NULL) { + // std::function function(params); + // } + // } + //} + } + return parsed; +} + +int ParseUtil::evaluateDefine(QString define, QMap* knownDefines) { + QList tokens = tokenizeExpression(define, knownDefines); + QList postfixExpression = generatePostfix(tokens); + return evaluatePostfix(postfixExpression); +} + +// arg here is the text in the file src/data/heal_locations.h +// returns a list of HealLocations (mapname, x, y) +QList* ParseUtil::parseHealLocs(QString text) { + QList *parsed = new QList; + QStringList lines = text.split('\n'); + + int i = 1; + for (auto line : lines){ + if (line.contains("MAP_GROUP")){ + QList li = line.replace(" ","").chopped(2).remove('{').split(','); + HealLocation hloc = HealLocation(li[1].remove("MAP_NUM(").remove(")"), i, li[2].toUShort(), li[3].toUShort()); + parsed->append(hloc); + i++; + } + } + return parsed; +} + +QList ParseUtil::tokenizeExpression(QString expression, QMap* knownIdentifiers) { + QList tokens; + + QStringList tokenTypes = (QStringList() << "hex" << "decimal" << "identifier" << "operator" << "leftparen" << "rightparen"); + QRegularExpression re("^(?0x[0-9a-fA-F]+)|(?[0-9]+)|(?[a-zA-Z_0-9]+)|(?[+\\-*\\/<>|^%]+)|(?\\()|(?\\))"); + + expression = expression.trimmed(); + while (!expression.isEmpty()) { + QRegularExpressionMatch match = re.match(expression); + if (!match.hasMatch()) { + qDebug() << "Failed to tokenize expression: " << expression; + break; + } + for (QString tokenType : tokenTypes) { + QString token = match.captured(tokenType); + if (!token.isEmpty()) { + if (tokenType == "identifier") { + if (knownIdentifiers->contains(token)) { + QString actualToken = QString("%1").arg(knownIdentifiers->value(token)); + expression = expression.replace(0, token.length(), actualToken); + token = actualToken; + tokenType = "decimal"; + } else { + qDebug() << "Unknown identifier found in expression: " << token; + } + } + + tokens.append(Token(token, tokenType)); + expression = expression.remove(0, token.length()).trimmed(); + break; + } + } + } + return tokens; +} + +QMap Token::precedenceMap = QMap( +{ + {"*", 3}, + {"/", 3}, + {"+", 4}, + {"-", 4}, + {"<<", 5}, + {">>", 5}, + {"&", 8}, + {"^", 9}, + {"|", 10} +}); + +// Shunting-yard algorithm for generating postfix notation. +// https://en.wikipedia.org/wiki/Shunting-yard_algorithm +QList ParseUtil::generatePostfix(QList tokens) { + QList output; + QStack operatorStack; + for (Token token : tokens) { + if (token.type == TokenType::Number) { + output.append(token); + } else if (token.value == "(") { + operatorStack.push(token); + } else if (token.value == ")") { + while (!operatorStack.empty() && operatorStack.top().value != "(") { + output.append(operatorStack.pop()); + } + if (!operatorStack.empty()) { + // pop the left parenthesis token + operatorStack.pop(); + } else { + qDebug() << "Mismatched parentheses detected in expression!"; + } + } else { + // token is an operator + while (!operatorStack.isEmpty() + && operatorStack.top().operatorPrecedence <= token.operatorPrecedence + && operatorStack.top().value != "(") { + output.append(operatorStack.pop()); + } + operatorStack.push(token); + } + } + + while (!operatorStack.isEmpty()) { + if (operatorStack.top().value == "(" || operatorStack.top().value == ")") { + qDebug() << "Mismatched parentheses detected in expression!"; + } else { + output.append(operatorStack.pop()); + } + } + + return output; +} + +// Evaluate postfix expression. +// https://en.wikipedia.org/wiki/Reverse_Polish_notation#Postfix_evaluation_algorithm +int ParseUtil::evaluatePostfix(QList postfix) { + QStack stack; + for (Token token : postfix) { + if (token.type == TokenType::Operator) { + int op2 = stack.pop().value.toInt(nullptr, 0); + int op1 = stack.pop().value.toInt(nullptr, 0); + int result = 0; + if (token.value == "*") { + result = op1 * op2; + } else if (token.value == "/") { + result = op1 / op2; + } else if (token.value == "+") { + result = op1 + op2; + } else if (token.value == "-") { + result = op1 - op2; + } else if (token.value == "<<") { + result = op1 << op2; + } else if (token.value == ">>") { + result = op1 >> op2; + } else if (token.value == "&") { + result = op1 & op2; + } else if (token.value == "^") { + result = op1 ^ op2; + } else if (token.value == "|") { + result = op1 | op2; + } else { + qDebug() << "Unsupported postfix operator: " << token.value; + } + stack.push(Token(QString("%1").arg(result), "decimal")); + } else { + stack.push(token); + } + } + + return stack.pop().value.toInt(nullptr, 0); +} diff --git a/tile.cpp b/src/core/tile.cpp old mode 100755 new mode 100644 similarity index 86% rename from tile.cpp rename to src/core/tile.cpp index fd0f42f3..93398fd5 --- a/tile.cpp +++ b/src/core/tile.cpp @@ -1,6 +1,6 @@ -#include "tile.h" - -Tile::Tile() -{ - -} +#include "tile.h" + +Tile::Tile() +{ + +} diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp new file mode 100644 index 00000000..d3acbf2b --- /dev/null +++ b/src/core/tileset.cpp @@ -0,0 +1,89 @@ +#include "tileset.h" +#include "metatile.h" +#include "project.h" + +#include +#include +#include + +Tileset::Tileset() +{ + +} + +Tileset* Tileset::copy() { + Tileset *copy = new Tileset; + copy->name = this->name; + copy->is_compressed = this->is_compressed; + copy->is_secondary = this->is_secondary; + copy->padding = this->padding; + copy->tiles_label = this->tiles_label; + copy->palettes_label = this->palettes_label; + copy->metatiles_label = this->metatiles_label; + copy->metatiles_path = this->metatiles_path; + copy->callback_label = this->callback_label; + copy->metatile_attrs_label = this->metatile_attrs_label; + copy->metatile_attrs_path = this->metatile_attrs_path; + copy->tilesImage = this->tilesImage.copy(); + copy->tilesImagePath = this->tilesImagePath; + for (int i = 0; i < this->palettePaths.length(); i++) { + copy->palettePaths.append(this->palettePaths.at(i)); + } + copy->tiles = new QList; + for (QImage tile : *this->tiles) { + copy->tiles->append(tile.copy()); + } + copy->metatiles = new QList; + for (Metatile *metatile : *this->metatiles) { + copy->metatiles->append(metatile->copy()); + } + copy->palettes = new QList>; + for (QList palette : *this->palettes) { + QList copyPalette; + for (QRgb color : palette) { + copyPalette.append(color); + } + copy->palettes->append(copyPalette); + } + return copy; +} + +Tileset* Tileset::getBlockTileset(int metatile_index, Tileset *primaryTileset, Tileset *secondaryTileset) { + if (metatile_index < Project::getNumMetatilesPrimary()) { + return primaryTileset; + } else { + return secondaryTileset; + } +} + +Metatile* Tileset::getMetatile(int index, Tileset *primaryTileset, Tileset *secondaryTileset) { + Tileset *tileset = Tileset::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; +} + +QList> Tileset::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; +} + +QList Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset) { + QList paletteTable; + Tileset *tileset = paletteId < Project::getNumPalettesPrimary() + ? primaryTileset + : secondaryTileset; + for (int i = 0; i < tileset->palettes->at(paletteId).length(); i++) { + paletteTable.append(tileset->palettes->at(paletteId).at(i)); + } + return paletteTable; +} diff --git a/editor.cpp b/src/editor.cpp old mode 100755 new mode 100644 similarity index 50% rename from editor.cpp rename to src/editor.cpp index cc550aaa..08f0c766 --- a/editor.cpp +++ b/src/editor.cpp @@ -1,1802 +1,1137 @@ -#include "editor.h" -#include "event.h" -#include -#include -#include -#include - -static bool selectingEvent = false; - -Editor::Editor(Ui::MainWindow* ui) -{ - this->ui = ui; - selected_events = new QList; -} - -void Editor::saveProject() { - if (project) { - project->saveAllMaps(); - project->saveAllDataStructures(); - } -} - -void Editor::save() { - if (project && map) { - project->saveMap(map); - project->saveAllDataStructures(); - } -} - -void Editor::undo() { - if (current_view) { - map->undo(); - map_item->draw(); - collision_item->draw(); - } -} - -void Editor::redo() { - if (current_view) { - map->redo(); - map_item->draw(); - collision_item->draw(); - } -} - -void Editor::setEditingMap() { - current_view = map_item; - if (map_item) { - displayMapConnections(); - map_item->draw(); - map_item->setVisible(true); - map_item->setEnabled(true); - setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); - } - if (collision_item) { - collision_item->setVisible(false); - } - if (events_group) { - events_group->setVisible(false); - } - setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); - setConnectionItemsVisible(false); -} - -void Editor::setEditingCollision() { - current_view = collision_item; - if (collision_item) { - displayMapConnections(); - collision_item->draw(); - collision_item->setVisible(true); - setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); - } - if (map_item) { - map_item->setVisible(false); - } - if (events_group) { - events_group->setVisible(false); - } - setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); - setConnectionItemsVisible(false); -} - -void Editor::setEditingObjects() { - current_view = map_item; - if (events_group) { - events_group->setVisible(true); - } - if (map_item) { - map_item->setVisible(true); - map_item->setEnabled(false); - setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); - } - if (collision_item) { - collision_item->setVisible(false); - } - setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); - setConnectionItemsVisible(false); -} - -void Editor::setEditingConnections() { - current_view = map_item; - if (map_item) { - map_item->draw(); - map_item->setVisible(true); - map_item->setEnabled(false); - populateConnectionMapPickers(); - ui->label_NumConnections->setText(QString::number(map->connections.length())); - setConnectionsVisibility(false); - setDiveEmergeControls(); - bool controlsEnabled = selected_connection_item != nullptr; - setConnectionEditControlsEnabled(controlsEnabled); - if (selected_connection_item) { - onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); - setConnectionMap(selected_connection_item->connection->map_name); - setCurrentConnectionDirection(selected_connection_item->connection->direction); - } - } - if (collision_item) { - collision_item->setVisible(false); - } - if (events_group) { - events_group->setVisible(false); - } - setBorderItemsVisible(true, 0.4); - setConnectionItemsVisible(true); -} - -void Editor::setDiveEmergeControls() { - ui->comboBox_DiveMap->blockSignals(true); - ui->comboBox_EmergeMap->blockSignals(true); - ui->comboBox_DiveMap->setCurrentText(""); - ui->comboBox_EmergeMap->setCurrentText(""); - for (Connection* connection : map->connections) { - if (connection->direction == "dive") { - ui->comboBox_DiveMap->setCurrentText(connection->map_name); - } else if (connection->direction == "emerge") { - ui->comboBox_EmergeMap->setCurrentText(connection->map_name); - } - } - ui->comboBox_DiveMap->blockSignals(false); - ui->comboBox_EmergeMap->blockSignals(false); -} - -void Editor::populateConnectionMapPickers() { - ui->comboBox_ConnectedMap->blockSignals(true); - ui->comboBox_DiveMap->blockSignals(true); - ui->comboBox_EmergeMap->blockSignals(true); - - ui->comboBox_ConnectedMap->clear(); - ui->comboBox_ConnectedMap->addItems(*project->mapNames); - ui->comboBox_DiveMap->clear(); - ui->comboBox_DiveMap->addItems(*project->mapNames); - ui->comboBox_EmergeMap->clear(); - ui->comboBox_EmergeMap->addItems(*project->mapNames); - - ui->comboBox_ConnectedMap->blockSignals(false); - ui->comboBox_DiveMap->blockSignals(true); - ui->comboBox_EmergeMap->blockSignals(true); -} - -void Editor::setConnectionItemsVisible(bool visible) { - for (ConnectionPixmapItem* item : connection_edit_items) { - item->setVisible(visible); - item->setEnabled(visible); - } -} - -void Editor::setBorderItemsVisible(bool visible, qreal opacity) { - for (QGraphicsPixmapItem* item : borderItems) { - item->setVisible(visible); - item->setOpacity(opacity); - } -} - -void Editor::setCurrentConnectionDirection(QString curDirection) { - if (!selected_connection_item) - return; - - selected_connection_item->connection->direction = curDirection; - - Map *connected_map = project->getMap(selected_connection_item->connection->map_name); - QPixmap pixmap = connected_map->renderConnection(*selected_connection_item->connection); - int offset = selected_connection_item->connection->offset.toInt(nullptr, 0); - selected_connection_item->initialOffset = offset; - int x = 0, y = 0; - if (selected_connection_item->connection->direction == "up") { - x = offset * 16; - y = -pixmap.height(); - } else if (selected_connection_item->connection->direction == "down") { - x = offset * 16; - y = map->getHeight() * 16; - } else if (selected_connection_item->connection->direction == "left") { - x = -pixmap.width(); - y = offset * 16; - } else if (selected_connection_item->connection->direction == "right") { - x = map->getWidth() * 16; - y = offset * 16; - } - - selected_connection_item->basePixmap = pixmap; - QPainter painter(&pixmap); - painter.setPen(QColor(255, 0, 255)); - painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); - painter.end(); - selected_connection_item->setPixmap(pixmap); - selected_connection_item->initialX = x; - selected_connection_item->initialY = y; - selected_connection_item->blockSignals(true); - selected_connection_item->setX(x); - selected_connection_item->setY(y); - selected_connection_item->setZValue(-1); - selected_connection_item->blockSignals(false); - - setConnectionEditControlValues(selected_connection_item->connection); -} - -void Editor::updateCurrentConnectionDirection(QString curDirection) { - if (!selected_connection_item) - return; - - QString originalDirection = selected_connection_item->connection->direction; - setCurrentConnectionDirection(curDirection); - updateMirroredConnectionDirection(selected_connection_item->connection, originalDirection); -} - -void Editor::onConnectionMoved(Connection* connection) { - updateMirroredConnectionOffset(connection); - onConnectionOffsetChanged(connection->offset.toInt()); -} - -void Editor::onConnectionOffsetChanged(int newOffset) { - ui->spinBox_ConnectionOffset->blockSignals(true); - ui->spinBox_ConnectionOffset->setValue(newOffset); - ui->spinBox_ConnectionOffset->blockSignals(false); - -} - -void Editor::setConnectionEditControlValues(Connection* connection) { - QString mapName = connection ? connection->map_name : ""; - QString direction = connection ? connection->direction : ""; - int offset = connection ? connection->offset.toInt() : 0; - - ui->comboBox_ConnectedMap->blockSignals(true); - ui->comboBox_ConnectionDirection->blockSignals(true); - ui->spinBox_ConnectionOffset->blockSignals(true); - - ui->comboBox_ConnectedMap->setCurrentText(mapName); - ui->comboBox_ConnectionDirection->setCurrentText(direction); - ui->spinBox_ConnectionOffset->setValue(offset); - - ui->comboBox_ConnectedMap->blockSignals(false); - ui->comboBox_ConnectionDirection->blockSignals(false); - ui->spinBox_ConnectionOffset->blockSignals(false); -} - -void Editor::setConnectionEditControlsEnabled(bool enabled) { - ui->comboBox_ConnectionDirection->setEnabled(enabled); - ui->comboBox_ConnectedMap->setEnabled(enabled); - ui->spinBox_ConnectionOffset->setEnabled(enabled); - - if (!enabled) { - setConnectionEditControlValues(nullptr); - } -} - -void Editor::onConnectionItemSelected(ConnectionPixmapItem* connectionItem) { - if (!connectionItem) - return; - - for (ConnectionPixmapItem* item : connection_edit_items) { - bool isSelectedItem = item == connectionItem; - int zValue = isSelectedItem ? 0 : -1; - qreal opacity = isSelectedItem ? 1 : 0.75; - item->setZValue(zValue); - item->render(opacity); - if (isSelectedItem) { - QPixmap pixmap = item->pixmap(); - QPainter painter(&pixmap); - painter.setPen(QColor(255, 0, 255)); - painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); - painter.end(); - item->setPixmap(pixmap); - } - } - selected_connection_item = connectionItem; - setConnectionEditControlsEnabled(true); - setConnectionEditControlValues(selected_connection_item->connection); - ui->spinBox_ConnectionOffset->setMaximum(selected_connection_item->getMaxOffset()); - ui->spinBox_ConnectionOffset->setMinimum(selected_connection_item->getMinOffset()); - onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); -} - -void Editor::setSelectedConnectionFromMap(QString mapName) { - // Search for the first connection that connects to the given map map. - for (ConnectionPixmapItem* item : connection_edit_items) { - if (item->connection->map_name == mapName) { - onConnectionItemSelected(item); - break; - } - } -} - -void Editor::onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem) { - emit loadMapRequested(connectionItem->connection->map_name, map->name); -} - -void Editor::onConnectionDirectionChanged(QString newDirection) { - ui->comboBox_ConnectionDirection->blockSignals(true); - ui->comboBox_ConnectionDirection->setCurrentText(newDirection); - ui->comboBox_ConnectionDirection->blockSignals(false); -} - -void Editor::onBorderMetatilesChanged() { - displayMapBorder(); -} - -void Editor::setConnectionsVisibility(bool visible) { - for (QGraphicsPixmapItem* item : connection_items) { - item->setVisible(visible); - item->setActive(visible); - } -} - -void Editor::setMap(QString map_name) { - if (map_name.isNull()) { - return; - } - if (project) { - map = project->loadMap(map_name); - connect(map, &Map::paintTileChanged, [=]() { - lastSelectedMetatilesFromMap = false; - redrawCurrentMetatilesSelection(); - }); - selected_events->clear(); - updateCurrentMetatilesSelection(); - displayMap(); - updateSelectedEvents(); - } -} - -void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item) { - if (map_edit_mode == "paint") { - if (event->buttons() & Qt::RightButton) { - item->updateMetatileSelection(event); - } else if (event->buttons() & Qt::MiddleButton) { - item->floodFill(event); - } else { - item->paint(event); - } - } else if (map_edit_mode == "select") { - item->select(event); - } else if (map_edit_mode == "fill") { - if (event->buttons() & Qt::RightButton) { - item->updateMetatileSelection(event); - } else { - item->floodFill(event); - } - } else if (map_edit_mode == "pick") { - - if (event->buttons() & Qt::RightButton) { - item->updateMetatileSelection(event); - } else { - item->pick(event); - } - } else if (map_edit_mode == "shift") { - item->shift(event); - } -} -void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item) { - if (map_edit_mode == "paint") { - if (event->buttons() & Qt::RightButton) { - item->updateMovementPermissionSelection(event); - } else if (event->buttons() & Qt::MiddleButton) { - item->floodFill(event); - } else { - item->paint(event); - } - } else if (map_edit_mode == "select") { - item->select(event); - } else if (map_edit_mode == "fill") { - if (event->buttons() & Qt::RightButton) { - item->pick(event); - } else { - item->floodFill(event); - } - } else if (map_edit_mode == "pick") { - item->pick(event); - } else if (map_edit_mode == "shift") { - item->shift(event); - } -} - -void Editor::updateCurrentMetatilesSelection() { - if (lastSelectedMetatilesFromMap) { - // Remember the copied metatiles from the previously-opened map - map->selected_metatiles_width = this->copiedMetatileSelectionWidth; - map->selected_metatiles_height = this->copiedMetatileSelectionHeight; - *map->selected_metatiles = *this->copiedMetatileSelection; - } -} - -void Editor::displayMap() { - if (!scene) - scene = new QGraphicsScene; - - if (map_item && scene) { - scene->removeItem(map_item); - delete map_item; - } - map_item = new MapPixmapItem(map, this); - connect(map_item, SIGNAL(mouseEvent(QGraphicsSceneMouseEvent*,MapPixmapItem*)), - this, SLOT(mouseEvent_map(QGraphicsSceneMouseEvent*,MapPixmapItem*))); - - map_item->draw(true); - scene->addItem(map_item); - - if (collision_item && scene) { - scene->removeItem(collision_item); - delete collision_item; - } - collision_item = new CollisionPixmapItem(map, this); - connect(collision_item, SIGNAL(mouseEvent(QGraphicsSceneMouseEvent*,CollisionPixmapItem*)), - this, SLOT(mouseEvent_collision(QGraphicsSceneMouseEvent*,CollisionPixmapItem*))); - - collision_item->draw(true); - scene->addItem(collision_item); - - int tw = 16; - int th = 16; - scene->setSceneRect( - -6 * tw, - -6 * th, - map_item->pixmap().width() + 12 * tw, - map_item->pixmap().height() + 12 * th - ); - - displayMetatiles(); - displayBorderMetatiles(); - displayCurrentMetatilesSelection(); - displayCollisionMetatiles(); - displayMapEvents(); - displayMapConnections(); - displayMapBorder(); - displayMapGrid(); - - if (map_item) { - map_item->setVisible(false); - } - if (collision_item) { - collision_item->setVisible(false); - } - if (events_group) { - events_group->setVisible(false); - } -} - -void Editor::displayMetatiles() { - if (metatiles_item && metatiles_item->scene()) { - metatiles_item->scene()->removeItem(metatiles_item); - delete metatiles_item; - } - - scene_metatiles = new QGraphicsScene; - metatiles_item = new MetatilesPixmapItem(map); - metatiles_item->draw(); - scene_metatiles->addItem(metatiles_item); -} - -void Editor::displayBorderMetatiles() { - if (selected_border_metatiles_item && selected_border_metatiles_item->scene()) { - selected_border_metatiles_item->scene()->removeItem(selected_border_metatiles_item); - delete selected_border_metatiles_item; - } - - scene_selected_border_metatiles = new QGraphicsScene; - selected_border_metatiles_item = new BorderMetatilesPixmapItem(map); - selected_border_metatiles_item->draw(); - scene_selected_border_metatiles->addItem(selected_border_metatiles_item); - - connect(selected_border_metatiles_item, SIGNAL(borderMetatilesChanged()), this, SLOT(onBorderMetatilesChanged())); -} - -void Editor::displayCurrentMetatilesSelection() { - if (scene_current_metatile_selection_item && scene_current_metatile_selection_item->scene()) { - scene_current_metatile_selection_item->scene()->removeItem(scene_current_metatile_selection_item); - delete scene_current_metatile_selection_item; - } - - scene_current_metatile_selection = new QGraphicsScene; - scene_current_metatile_selection_item = new CurrentSelectedMetatilesPixmapItem(map); - scene_current_metatile_selection_item->draw(); - scene_current_metatile_selection->addItem(scene_current_metatile_selection_item); -} - -void Editor::redrawCurrentMetatilesSelection() { - if (scene_current_metatile_selection_item) { - scene_current_metatile_selection_item->draw(); - emit currentMetatilesSelectionChanged(); - } -} - -void Editor::displayCollisionMetatiles() { - if (collision_metatiles_item && collision_metatiles_item->scene()) { - collision_metatiles_item->scene()->removeItem(collision_metatiles_item); - delete collision_metatiles_item; - } - - scene_collision_metatiles = new QGraphicsScene; - collision_metatiles_item = new MovementPermissionsPixmapItem(map); - collision_metatiles_item->draw(); - scene_collision_metatiles->addItem(collision_metatiles_item); -} - -void Editor::displayMapEvents() { - if (events_group) { - for (QGraphicsItem *child : events_group->childItems()) { - events_group->removeFromGroup(child); - delete child; - } - - if (events_group->scene()) { - events_group->scene()->removeItem(events_group); - } - - delete events_group; - } - - selected_events->clear(); - - events_group = new EventGroup; - scene->addItem(events_group); - - QList events = map->getAllEvents(); - project->loadEventPixmaps(events); - for (Event *event : events) { - addMapEvent(event); - } - //objects_group->setFiltersChildEvents(false); - events_group->setHandlesChildEvents(false); - - emit objectsChanged(); -} - -DraggablePixmapItem *Editor::addMapEvent(Event *event) { - DraggablePixmapItem *object = new DraggablePixmapItem(event, this); - events_group->addToGroup(object); - return object; -} - -void Editor::displayMapConnections() { - for (QGraphicsPixmapItem* item : connection_items) { - if (item->scene()) { - item->scene()->removeItem(item); - } - delete item; - } - connection_items.clear(); - - for (ConnectionPixmapItem* item : connection_edit_items) { - if (item->scene()) { - item->scene()->removeItem(item); - } - delete item; - } - selected_connection_item = nullptr; - connection_edit_items.clear(); - - for (Connection *connection : map->connections) { - if (connection->direction == "dive" || connection->direction == "emerge") { - continue; - } - createConnectionItem(connection, false); - } - - if (!connection_edit_items.empty()) { - onConnectionItemSelected(connection_edit_items.first()); - } -} - -void Editor::createConnectionItem(Connection* connection, bool hide) { - Map *connected_map = project->getMap(connection->map_name); - QPixmap pixmap = connected_map->renderConnection(*connection); - int offset = connection->offset.toInt(nullptr, 0); - int x = 0, y = 0; - if (connection->direction == "up") { - x = offset * 16; - y = -pixmap.height(); - } else if (connection->direction == "down") { - x = offset * 16; - y = map->getHeight() * 16; - } else if (connection->direction == "left") { - x = -pixmap.width(); - y = offset * 16; - } else if (connection->direction == "right") { - x = map->getWidth() * 16; - y = offset * 16; - } - - QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); - item->setZValue(-1); - item->setX(x); - item->setY(y); - scene->addItem(item); - connection_items.append(item); - item->setVisible(!hide); - - ConnectionPixmapItem *connection_edit_item = new ConnectionPixmapItem(pixmap, connection, x, y, map->getWidth(), map->getHeight()); - connection_edit_item->setX(x); - connection_edit_item->setY(y); - connection_edit_item->setZValue(-1); - scene->addItem(connection_edit_item); - connect(connection_edit_item, SIGNAL(connectionMoved(Connection*)), this, SLOT(onConnectionMoved(Connection*))); - connect(connection_edit_item, SIGNAL(connectionItemSelected(ConnectionPixmapItem*)), this, SLOT(onConnectionItemSelected(ConnectionPixmapItem*))); - connect(connection_edit_item, SIGNAL(connectionItemDoubleClicked(ConnectionPixmapItem*)), this, SLOT(onConnectionItemDoubleClicked(ConnectionPixmapItem*))); - connection_edit_items.append(connection_edit_item); -} - -void Editor::displayMapBorder() { - for (QGraphicsPixmapItem* item : borderItems) { - if (item->scene()) { - item->scene()->removeItem(item); - } - delete item; - } - borderItems.clear(); - - QPixmap pixmap = map->renderBorder(); - for (int y = -6; y < map->getHeight() + 6; y += 2) - for (int x = -6; x < map->getWidth() + 6; x += 2) { - QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); - item->setX(x * 16); - item->setY(y * 16); - item->setZValue(-2); - scene->addItem(item); - borderItems.append(item); - } -} - -void Editor::displayMapGrid() { - for (QGraphicsLineItem* item : gridLines) { - if (item && item->scene()) { - item->scene()->removeItem(item); - } - delete item; - } - gridLines.clear(); - ui->checkBox_ToggleGrid->disconnect(); - - int pixelWidth = map->getWidth() * 16; - int pixelHeight = map->getHeight() * 16; - for (int i = 0; i <= map->getWidth(); i++) { - int x = i * 16; - QGraphicsLineItem *line = scene->addLine(x, 0, x, pixelHeight); - line->setVisible(ui->checkBox_ToggleGrid->isChecked()); - gridLines.append(line); - connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); - } - for (int j = 0; j <= map->getHeight(); j++) { - int y = j * 16; - QGraphicsLineItem *line = scene->addLine(0, y, pixelWidth, y); - line->setVisible(ui->checkBox_ToggleGrid->isChecked()); - gridLines.append(line); - connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); - } -} - -void Editor::updateConnectionOffset(int offset) { - if (!selected_connection_item) - return; - - selected_connection_item->blockSignals(true); - offset = qMin(offset, selected_connection_item->getMaxOffset()); - offset = qMax(offset, selected_connection_item->getMinOffset()); - selected_connection_item->connection->offset = QString::number(offset); - if (selected_connection_item->connection->direction == "up" || selected_connection_item->connection->direction == "down") { - selected_connection_item->setX(selected_connection_item->initialX + (offset - selected_connection_item->initialOffset) * 16); - } else if (selected_connection_item->connection->direction == "left" || selected_connection_item->connection->direction == "right") { - selected_connection_item->setY(selected_connection_item->initialY + (offset - selected_connection_item->initialOffset) * 16); - } - selected_connection_item->blockSignals(false); - updateMirroredConnectionOffset(selected_connection_item->connection); -} - -void Editor::setConnectionMap(QString mapName) { - if (!mapName.isEmpty() && !project->mapNames->contains(mapName)) { - qDebug() << "Invalid map name " << mapName << " specified for connection."; - return; - } - if (!selected_connection_item) - return; - - if (mapName.isEmpty()) { - removeCurrentConnection(); - return; - } - - QString originalMapName = selected_connection_item->connection->map_name; - setConnectionEditControlsEnabled(true); - selected_connection_item->connection->map_name = mapName; - setCurrentConnectionDirection(selected_connection_item->connection->direction); - updateMirroredConnectionMap(selected_connection_item->connection, originalMapName); -} - -void Editor::addNewConnection() { - // Find direction with least number of connections. - QMap directionCounts = QMap({{"up", 0}, {"right", 0}, {"down", 0}, {"left", 0}}); - for (Connection* connection : map->connections) { - directionCounts[connection->direction]++; - } - QString minDirection = "up"; - int minCount = INT_MAX; - for (QString direction : directionCounts.keys()) { - if (directionCounts[direction] < minCount) { - minDirection = direction; - minCount = directionCounts[direction]; - } - } - - // Don't connect the map to itself. - QString defaultMapName = project->mapNames->first(); - if (defaultMapName == map->name) { - defaultMapName = project->mapNames->value(1); - } - - Connection* newConnection = new Connection; - newConnection->direction = minDirection; - newConnection->offset = "0"; - newConnection->map_name = defaultMapName; - map->connections.append(newConnection); - createConnectionItem(newConnection, true); - onConnectionItemSelected(connection_edit_items.last()); - ui->label_NumConnections->setText(QString::number(map->connections.length())); - - updateMirroredConnection(newConnection, newConnection->direction, newConnection->map_name); -} - -void Editor::updateMirroredConnectionOffset(Connection* connection) { - updateMirroredConnection(connection, connection->direction, connection->map_name); -} -void Editor::updateMirroredConnectionDirection(Connection* connection, QString originalDirection) { - updateMirroredConnection(connection, originalDirection, connection->map_name); -} -void Editor::updateMirroredConnectionMap(Connection* connection, QString originalMapName) { - updateMirroredConnection(connection, connection->direction, originalMapName); -} -void Editor::removeMirroredConnection(Connection* connection) { - updateMirroredConnection(connection, connection->direction, connection->map_name, true); -} -void Editor::updateMirroredConnection(Connection* connection, QString originalDirection, QString originalMapName, bool isDelete) { - if (!ui->checkBox_MirrorConnections->isChecked()) - return; - - static QMap oppositeDirections = QMap({ - {"up", "down"}, {"right", "left"}, - {"down", "up"}, {"left", "right"}, - {"dive", "emerge"},{"emerge", "dive"}}); - QString oppositeDirection = oppositeDirections.value(originalDirection); - - // Find the matching connection in the connected map. - Connection* mirrorConnection = nullptr; - Map* otherMap = project->getMap(originalMapName); - for (Connection* conn : otherMap->connections) { - if (conn->direction == oppositeDirection && conn->map_name == map->name) { - mirrorConnection = conn; - } - } - - if (isDelete) { - if (mirrorConnection) { - otherMap->connections.removeOne(mirrorConnection); - delete mirrorConnection; - } - return; - } - - if (connection->direction != originalDirection || connection->map_name != originalMapName) { - if (mirrorConnection) { - otherMap->connections.removeOne(mirrorConnection); - delete mirrorConnection; - mirrorConnection = nullptr; - otherMap = project->getMap(connection->map_name); - } - } - - // Create a new mirrored connection, if a matching one doesn't already exist. - if (!mirrorConnection) { - mirrorConnection = new Connection; - mirrorConnection->direction = oppositeDirections.value(connection->direction); - mirrorConnection->map_name = map->name; - otherMap->connections.append(mirrorConnection); - } - - mirrorConnection->offset = QString::number(-connection->offset.toInt()); -} - -void Editor::removeCurrentConnection() { - if (!selected_connection_item) - return; - - map->connections.removeOne(selected_connection_item->connection); - connection_edit_items.removeOne(selected_connection_item); - removeMirroredConnection(selected_connection_item->connection); - - if (selected_connection_item && selected_connection_item->scene()) { - selected_connection_item->scene()->removeItem(selected_connection_item); - delete selected_connection_item; - } - - selected_connection_item = nullptr; - setConnectionEditControlsEnabled(false); - ui->spinBox_ConnectionOffset->setValue(0); - ui->label_NumConnections->setText(QString::number(map->connections.length())); - - if (connection_edit_items.length() > 0) { - onConnectionItemSelected(connection_edit_items.last()); - } -} - -void Editor::updateDiveMap(QString mapName) { - updateDiveEmergeMap(mapName, "dive"); -} - -void Editor::updateEmergeMap(QString mapName) { - updateDiveEmergeMap(mapName, "emerge"); -} - -void Editor::updateDiveEmergeMap(QString mapName, QString direction) { - if (!mapName.isEmpty() && !project->mapNamesToMapConstants->contains(mapName)) { - qDebug() << "Invalid " << direction << " map connection: " << mapName; - return; - } - - Connection* connection = nullptr; - for (Connection* conn : map->connections) { - if (conn->direction == direction) { - connection = conn; - break; - } - } - - if (mapName.isEmpty()) { - // Remove dive/emerge connection - if (connection) { - map->connections.removeOne(connection); - removeMirroredConnection(connection); - } - } else { - if (!connection) { - connection = new Connection; - connection->direction = direction; - connection->offset = "0"; - connection->map_name = mapName; - map->connections.append(connection); - updateMirroredConnection(connection, connection->direction, connection->map_name); - } else { - QString originalMapName = connection->map_name; - connection->map_name = mapName; - updateMirroredConnectionMap(connection, originalMapName); - } - } - - ui->label_NumConnections->setText(QString::number(map->connections.length())); -} - -void Editor::updatePrimaryTileset(QString tilesetLabel) -{ - if (map->layout->tileset_primary_label != tilesetLabel) - { - map->layout->tileset_primary_label = tilesetLabel; - map->layout->tileset_primary = project->getTileset(tilesetLabel); - emit tilesetChanged(map->name); - } -} - -void Editor::updateSecondaryTileset(QString tilesetLabel) -{ - if (map->layout->tileset_secondary_label != tilesetLabel) - { - map->layout->tileset_secondary_label = tilesetLabel; - map->layout->tileset_secondary = project->getTileset(tilesetLabel); - emit tilesetChanged(map->name); - } -} - -void Editor::toggleBorderVisibility(bool visible) -{ - this->setBorderItemsVisible(visible); - this->setConnectionsVisibility(visible); -} - -void MetatilesPixmapItem::paintTileChanged() { - draw(); -} - -void MetatilesPixmapItem::draw() { - setPixmap(map->renderMetatiles()); -} - -void MetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) { - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - int width = pixmap().width() / 16; - int height = pixmap().height() / 16; - if (x < 0 || x >= width || y < 0 || y >= height) { - map->clearHoveredMetatile(); - } else { - int block = y * width + x; - map->hoveredMetatileChanged(block); - } -} - -void MetatilesPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { - updateCurHoveredMetatile(event->pos()); -} -void MetatilesPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { - map->clearHoveredMetatile(); -} -void MetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - map->paint_metatile_initial_x = x; - map->paint_metatile_initial_y = y; - updateSelection(event->pos()); -} -void MetatilesPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - updateCurHoveredMetatile(event->pos()); - updateSelection(event->pos()); -} -void MetatilesPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - updateSelection(event->pos()); -} -void MetatilesPixmapItem::updateSelection(QPointF pos) { - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - int width = pixmap().width() / 16; - int height = pixmap().height() / 16; - if ((x >= 0 && x < width) && (y >=0 && y < height)) { - int x1 = x < map->paint_metatile_initial_x ? x : map->paint_metatile_initial_x; - int y1 = y < map->paint_metatile_initial_y ? y : map->paint_metatile_initial_y; - map->paint_tile_index = y1 * 8 + x1; - map->paint_tile_width = abs(map->paint_metatile_initial_x - x) + 1; - map->paint_tile_height = abs(map->paint_metatile_initial_y - y) + 1; - map->setSelectedMetatilesFromTilePicker(); - - emit map->paintTileChanged(); - } -} - -void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - - for (int i = 0; i < map->selected_metatiles_width && (i + x) < 2; i++) { - for (int j = 0; j < map->selected_metatiles_height && (j + y) < 2; j++) { - int blockIndex = (j + y) * 2 + (i + x); - uint16_t tile = map->selected_metatiles->at(j * map->selected_metatiles_width + i); - (*map->layout->border->blocks)[blockIndex].tile = tile; - } - } - - draw(); - emit borderMetatilesChanged(); -} - -void BorderMetatilesPixmapItem::draw() { - QImage image(32, 32, QImage::Format_RGBA8888); - QPainter painter(&image); - QList *blocks = map->layout->border->blocks; - - for (int i = 0; i < 2; i++) - for (int j = 0; j < 2; j++) - { - int x = i * 16; - int y = j * 16; - int index = j * 2 + i; - QImage metatile_image = Metatile::getMetatileImage(blocks->value(index).tile, map->layout->tileset_primary, map->layout->tileset_secondary); - QPoint metatile_origin = QPoint(x, y); - painter.drawImage(metatile_origin, metatile_image); - } - - painter.end(); - setPixmap(QPixmap::fromImage(image)); -} - -void CurrentSelectedMetatilesPixmapItem::draw() { - int width = map->selected_metatiles_width * 16; - int height = map->selected_metatiles_height * 16; - QImage image(width, height, QImage::Format_RGBA8888); - QPainter painter(&image); - - for (int i = 0; i < map->selected_metatiles_width; i++) - for (int j = 0; j < map->selected_metatiles_height; j++) - { - int x = i * 16; - int y = j * 16; - int index = j * map->selected_metatiles_width + i; - QImage metatile_image = Metatile::getMetatileImage(map->selected_metatiles->at(index), map->layout->tileset_primary, map->layout->tileset_secondary); - QPoint metatile_origin = QPoint(x, y); - painter.drawImage(metatile_origin, metatile_image); - } - - painter.end(); - setPixmap(QPixmap::fromImage(image)); -} - -void MovementPermissionsPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent* event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 32; - int y = static_cast(pos.y()) / 32; - int width = pixmap().width() / 32; - int height = pixmap().height() / 32; - - // Snap to a valid position inside the selection area. - if (x < 0) x = 0; - if (x >= width) x = width - 1; - if (y < 0) y = 0; - if (y >= height) y = height - 1; - pick(static_cast(x), static_cast(y)); -} -void MovementPermissionsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { - updateCurHoveredMetatile(event->pos()); - mousePressEvent(event); -} -void MovementPermissionsPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { - mousePressEvent(event); -} - -void MovementPermissionsPixmapItem::updateCurHoveredMetatile(QPointF pos) { - int x = static_cast(pos.x()) / 32; - int y = static_cast(pos.y()) / 32; - int width = pixmap().width() / 32; - int height = pixmap().height() / 32; - - // Snap to a valid position inside the selection area. - if (x < 0) x = 0; - if (x >= width) x = width - 1; - if (y < 0) y = 0; - if (y >= height) y = height - 1; - map->hoveredMovementPermissionTileChanged(x, y); -} - -int ConnectionPixmapItem::getMinOffset() { - if (connection->direction == "up" || connection->direction == "down") - return 1 - (this->pixmap().width() / 16); - else - return 1 - (this->pixmap().height() / 16); -} -int ConnectionPixmapItem::getMaxOffset() { - if (connection->direction == "up" || connection->direction == "down") - return baseMapWidth - 1; - else - return baseMapHeight - 1; -} -QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) -{ - if (change == ItemPositionChange) { - QPointF newPos = value.toPointF(); - - qreal x, y; - int newOffset = initialOffset; - if (connection->direction == "up" || connection->direction == "down") { - x = round(newPos.x() / 16) * 16; - newOffset += (x - initialX) / 16; - newOffset = qMin(newOffset, this->getMaxOffset()); - newOffset = qMax(newOffset, this->getMinOffset()); - x = newOffset * 16; - } - else { - x = initialX; - } - - if (connection->direction == "right" || connection->direction == "left") { - y = round(newPos.y() / 16) * 16; - newOffset += (y - initialY) / 16; - newOffset = qMin(newOffset, this->getMaxOffset()); - newOffset = qMax(newOffset, this->getMinOffset()); - y = newOffset * 16; - } - else { - y = initialY; - } - - connection->offset = QString::number(newOffset); - emit connectionMoved(connection); - return QPointF(x, y); - } - else { - return QGraphicsItem::itemChange(change, value); - } -} -void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *) { - emit connectionItemSelected(this); -} -void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { - emit connectionItemDoubleClicked(this); -} - -void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { - if (map) { - if (event->type() == QEvent::GraphicsSceneMouseRelease) { - map->commit(); - } else { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - - // Paint onto the map. - bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier; - if ((map->smart_paths_enabled || smartPathsEnabled) && map->selected_metatiles_width == 3 && map->selected_metatiles_height == 3) { - paintSmartPath(x, y); - } else { - paintNormal(x, y); - } - } - - draw(); - } -} - -void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { - if (map) { - if (event->type() == QEvent::GraphicsSceneMouseRelease) { - map->commit(); - } else { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - - if (event->type() == QEvent::GraphicsSceneMousePress) { - selection_origin = QPoint(x, y); - selection.clear(); - } else if (event->type() == QEvent::GraphicsSceneMouseMove) { - if (x != selection_origin.x() || y != selection_origin.y()) { - int xDelta = x - selection_origin.x(); - int yDelta = y - selection_origin.y(); - Blockdata *backupBlockdata = map->layout->blockdata->copy(); - for (int i = 0; i < map->getWidth(); i++) - for (int j = 0; j < map->getHeight(); j++) { - int destX = i + xDelta; - int destY = j + yDelta; - if (destX < 0) - do { destX += map->getWidth(); } while (destX < 0); - if (destY < 0) - do { destY += map->getHeight(); } while (destY < 0); - destX %= map->getWidth(); - destY %= map->getHeight(); - - int blockIndex = j * map->getWidth() + i; - Block srcBlock = backupBlockdata->blocks->at(blockIndex); - map->_setBlock(destX, destY, srcBlock); - } - - delete backupBlockdata; - selection_origin = QPoint(x, y); - selection.clear(); - draw(); - } - } - } - } -} - -void MapPixmapItem::paintNormal(int x, int y) { - // Snap the selected position to the top-left of the block boundary. - // This allows painting via dragging the mouse to tile the painted region. - int xDiff = x - map->paint_tile_initial_x; - int yDiff = y - map->paint_tile_initial_y; - if (xDiff < 0 && xDiff % map->selected_metatiles_width != 0) xDiff -= map->selected_metatiles_width; - if (yDiff < 0 && yDiff % map->selected_metatiles_height != 0) yDiff -= map->selected_metatiles_height; - - x = map->paint_tile_initial_x + (xDiff / map->selected_metatiles_width) * map->selected_metatiles_width; - y = map->paint_tile_initial_y + (yDiff / map->selected_metatiles_height) * map->selected_metatiles_height; - for (int i = 0; i < map->selected_metatiles_width && i + x < map->getWidth(); i++) - for (int j = 0; j < map->selected_metatiles_height && j + y < map->getHeight(); j++) { - int actualX = i + x; - int actualY = j + y; - Block *block = map->getBlock(actualX, actualY); - if (block) { - block->tile = map->selected_metatiles->at(j * map->selected_metatiles_width + i); - map->_setBlock(actualX, actualY, *block); - } - } -} - -// These are tile offsets from the top-left tile in the 3x3 smart path selection. -// Each entry is for one possibility from the marching squares value for a tile. -// (Marching Squares: https://en.wikipedia.org/wiki/Marching_squares) -QList MapPixmapItem::smartPathTable = QList({ - 4, // 0000 - 4, // 0001 - 4, // 0010 - 6, // 0011 - 4, // 0100 - 4, // 0101 - 0, // 0110 - 3, // 0111 - 4, // 1000 - 8, // 1001 - 4, // 1010 - 7, // 1011 - 2, // 1100 - 5, // 1101 - 1, // 1110 - 4, // 1111 -}); - -#define IS_SMART_PATH_TILE(block) (map->selected_metatiles->contains(block->tile)) - -void MapPixmapItem::paintSmartPath(int x, int y) { - // Smart path should never be enabled without a 3x3 block selection. - if (map->selected_metatiles_width != 3 || map->selected_metatiles_height != 3) return; - - // Shift to the middle tile of the smart path selection. - uint16_t openTile = map->selected_metatiles->at(4); - - // Fill the region with the open tile. - for (int i = 0; i <= 1; i++) - for (int j = 0; j <= 1; j++) { - // Check if in map bounds. - if (!(i + x < map->getWidth() && i + x >= 0 && j + y < map->getHeight() && j + y >= 0)) - continue; - int actualX = i + x; - int actualY = j + y; - Block *block = map->getBlock(actualX, actualY); - if (block) { - block->tile = openTile; - map->_setBlock(actualX, actualY, *block); - } - } - - // Go back and resolve the edge tiles - for (int i = -1; i <= 2; i++) - for (int j = -1; j <= 2; j++) { - // Check if in map bounds. - if (!(i + x < map->getWidth() && i + x >= 0 && j + y < map->getHeight() && j + y >= 0)) - continue; - // Ignore the corners, which can't possible be affected by the smart path. - if ((i == -1 && j == -1) || (i == 2 && j == -1) || - (i == -1 && j == 2) || (i == 2 && j == 2)) - continue; - - // Ignore tiles that aren't part of the smart path set. - int actualX = i + x; - int actualY = j + y; - Block *block = map->getBlock(actualX, actualY); - if (!block || !IS_SMART_PATH_TILE(block)) { - continue; - } - - int id = 0; - Block *top = map->getBlock(actualX, actualY - 1); - Block *right = map->getBlock(actualX + 1, actualY); - Block *bottom = map->getBlock(actualX, actualY + 1); - Block *left = map->getBlock(actualX - 1, actualY); - - // Get marching squares value, to determine which tile to use. - if (top && IS_SMART_PATH_TILE(top)) - id += 1; - if (right && IS_SMART_PATH_TILE(right)) - id += 2; - if (bottom && IS_SMART_PATH_TILE(bottom)) - id += 4; - if (left && IS_SMART_PATH_TILE(left)) - id += 8; - - block->tile = map->selected_metatiles->at(smartPathTable[id]); - map->_setBlock(actualX, actualY, *block); - } -} - -void MapPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - - // Snap point to within map bounds. - if (x < 0) x = 0; - if (x >= map->getWidth()) x = map->getWidth() - 1; - if (y < 0) y = 0; - if (y >= map->getHeight()) y = map->getHeight() - 1; - - // Update/apply copied metatiles. - if (event->type() == QEvent::GraphicsSceneMousePress) { - selection_origin = QPoint(x, y); - selection.clear(); - selection.append(QPoint(x, y)); - editor->copiedMetatileSelectionWidth = 1; - editor->copiedMetatileSelectionHeight = 1; - } else if (event->type() == QEvent::GraphicsSceneMouseMove) { - QPoint pos = QPoint(x, y); - int x1 = selection_origin.x(); - int y1 = selection_origin.y(); - int x2 = pos.x(); - int y2 = pos.y(); - if (x1 > x2) SWAP(x1, x2); - if (y1 > y2) SWAP(y1, y2); - selection.clear(); - for (int y = y1; y <= y2; y++) { - for (int x = x1; x <= x2; x++) { - selection.append(QPoint(x, y)); - } - } - - editor->copiedMetatileSelectionWidth = x2 - x1 + 1; - editor->copiedMetatileSelectionHeight = y2 - y1 + 1; - } - - editor->copiedMetatileSelection->clear(); - for (QPoint point : selection) { - editor->copiedMetatileSelection->append(map->getBlock(point.x(), point.y())->tile); - } - - editor->lastSelectedMetatilesFromMap = true; - map->selected_metatiles_width = editor->copiedMetatileSelectionWidth; - map->selected_metatiles_height = editor->copiedMetatileSelectionHeight; - *map->selected_metatiles = *editor->copiedMetatileSelection; - - editor->redrawCurrentMetatilesSelection(); -} - -void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { - if (map) { - if (event->type() == QEvent::GraphicsSceneMouseRelease) { - map->commit(); - } else { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - Block *block = map->getBlock(x, y); - int tile = map->selected_metatiles->first(); - if (block && block->tile != tile) { - bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier; - if ((map->smart_paths_enabled || smartPathsEnabled) && map->selected_metatiles_width == 3 && map->selected_metatiles_height == 3) - this->_floodFillSmartPath(x, y); - else - this->_floodFill(x, y); - } - } - - draw(); - } -} - -void MapPixmapItem::_floodFill(int initialX, int initialY) { - QList todo; - todo.append(QPoint(initialX, initialY)); - while (todo.length()) { - QPoint point = todo.takeAt(0); - int x = point.x(); - int y = point.y(); - - Block *block = map->getBlock(x, y); - if (!block) { - continue; - } - - int xDiff = x - initialX; - int yDiff = y - initialY; - int i = xDiff % map->selected_metatiles_width; - int j = yDiff % map->selected_metatiles_height; - if (i < 0) i = map->selected_metatiles_width + i; - if (j < 0) j = map->selected_metatiles_height + j; - uint16_t tile = map->selected_metatiles->at(j * map->selected_metatiles_width + i); - uint16_t old_tile = block->tile; - if (old_tile == tile) { - continue; - } - - block->tile = tile; - map->_setBlock(x, y, *block); - if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) { - todo.append(QPoint(x + 1, y)); - } - if ((block = map->getBlock(x - 1, y)) && block->tile == old_tile) { - todo.append(QPoint(x - 1, y)); - } - if ((block = map->getBlock(x, y + 1)) && block->tile == old_tile) { - todo.append(QPoint(x, y + 1)); - } - if ((block = map->getBlock(x, y - 1)) && block->tile == old_tile) { - todo.append(QPoint(x, y - 1)); - } - } -} - -void MapPixmapItem::_floodFillSmartPath(int initialX, int initialY) { - // Smart path should never be enabled without a 3x3 block selection. - if (map->selected_metatiles_width != 3 || map->selected_metatiles_height != 3) return; - - // Shift to the middle tile of the smart path selection. - uint16_t openTile = map->selected_metatiles->at(4); - - // Flood fill the region with the open tile. - QList todo; - todo.append(QPoint(initialX, initialY)); - while (todo.length()) { - QPoint point = todo.takeAt(0); - int x = point.x(); - int y = point.y(); - - Block *block = map->getBlock(x, y); - if (!block) { - continue; - } - - uint16_t old_tile = block->tile; - if (old_tile == openTile) { - continue; - } - - block->tile = openTile; - map->_setBlock(x, y, *block); - if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) { - todo.append(QPoint(x + 1, y)); - } - if ((block = map->getBlock(x - 1, y)) && block->tile == old_tile) { - todo.append(QPoint(x - 1, y)); - } - if ((block = map->getBlock(x, y + 1)) && block->tile == old_tile) { - todo.append(QPoint(x, y + 1)); - } - if ((block = map->getBlock(x, y - 1)) && block->tile == old_tile) { - todo.append(QPoint(x, y - 1)); - } - } - - // Go back and resolve the flood-filled edge tiles. - // Mark tiles as visited while we go. - int numMetatiles = map->getWidth() * map->getHeight(); - bool *visited = new bool[numMetatiles]; - for (int i = 0; i < numMetatiles; i++) - visited[i] = false; - - todo.append(QPoint(initialX, initialY)); - while (todo.length()) { - QPoint point = todo.takeAt(0); - int x = point.x(); - int y = point.y(); - visited[x + y * map->getWidth()] = true; - - Block *block = map->getBlock(x, y); - if (!block) { - continue; - } - - int id = 0; - Block *top = map->getBlock(x, y - 1); - Block *right = map->getBlock(x + 1, y); - Block *bottom = map->getBlock(x, y + 1); - Block *left = map->getBlock(x - 1, y); - - // Get marching squares value, to determine which tile to use. - if (top && IS_SMART_PATH_TILE(top)) - id += 1; - if (right && IS_SMART_PATH_TILE(right)) - id += 2; - if (bottom && IS_SMART_PATH_TILE(bottom)) - id += 4; - if (left && IS_SMART_PATH_TILE(left)) - id += 8; - - block->tile = map->selected_metatiles->at(smartPathTable[id]); - map->_setBlock(x, y, *block); - - // Visit neighbors if they are smart-path tiles, and don't revisit any. - if (!visited[x + 1 + y * map->getWidth()] && (block = map->getBlock(x + 1, y)) && IS_SMART_PATH_TILE(block)) { - todo.append(QPoint(x + 1, y)); - visited[x + 1 + y * map->getWidth()] = true; - } - if (!visited[x - 1 + y * map->getWidth()] && (block = map->getBlock(x - 1, y)) && IS_SMART_PATH_TILE(block)) { - todo.append(QPoint(x - 1, y)); - visited[x - 1 + y * map->getWidth()] = true; - } - if (!visited[x + (y + 1) * map->getWidth()] && (block = map->getBlock(x, y + 1)) && IS_SMART_PATH_TILE(block)) { - todo.append(QPoint(x, y + 1)); - visited[x + (y + 1) * map->getWidth()] = true; - } - if (!visited[x + (y - 1) * map->getWidth()] && (block = map->getBlock(x, y - 1)) && IS_SMART_PATH_TILE(block)) { - todo.append(QPoint(x, y - 1)); - visited[x + (y - 1) * map->getWidth()] = true; - } - } - - delete[] visited; -} - -void MapPixmapItem::pick(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - Block *block = map->getBlock(x, y); - if (block) { - map->paint_tile_index = map->getDisplayedBlockIndex(block->tile); - map->paint_tile_width = 1; - map->paint_tile_height = 1; - map->setSelectedMetatilesFromTilePicker(); - - emit map->paintTileChanged(); - } -} - -void MapPixmapItem::select(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - if (event->type() == QEvent::GraphicsSceneMousePress) { - selection_origin = QPoint(x, y); - selection.clear(); - } else if (event->type() == QEvent::GraphicsSceneMouseMove) { - if (event->buttons() & Qt::LeftButton) { - selection.clear(); - selection.append(QPoint(x, y)); - } - } else if (event->type() == QEvent::GraphicsSceneMouseRelease) { - if (!selection.isEmpty()) { - QPoint pos = selection.last(); - int x1 = selection_origin.x(); - int y1 = selection_origin.y(); - int x2 = pos.x(); - int y2 = pos.y(); - if (x1 > x2) SWAP(x1, x2); - if (y1 > y2) SWAP(y1, y2); - selection.clear(); - for (int y = y1; y <= y2; y++) { - for (int x = x1; x <= x2; x++) { - selection.append(QPoint(x, y)); - } - } - qDebug() << QString("selected (%1, %2) -> (%3, %4)").arg(x1).arg(y1).arg(x2).arg(y2); - } - } -} - -void MapPixmapItem::draw(bool ignoreCache) { - if (map) { - setPixmap(map->render(ignoreCache)); - } -} - -void MapPixmapItem::updateCurHoveredTile(QPointF pos) { - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - int blockIndex = y * map->getWidth() + x; - if (x < 0 || x >= map->getWidth() || y < 0 || y >= map->getHeight()) { - map->clearHoveredTile(); - } else { - if (editor->current_view == editor->map_item) { - int tile = map->layout->blockdata->blocks->at(blockIndex).tile; - map->hoveredTileChanged(x, y, tile); - } else if (editor->current_view == editor->collision_item) { - int collision = map->layout->blockdata->blocks->at(blockIndex).collision; - int elevation = map->layout->blockdata->blocks->at(blockIndex).elevation; - map->hoveredMovementPermissionTileChanged(collision, elevation); - } - } -} - -void MapPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { - updateCurHoveredTile(event->pos()); - if (editor->ui->actionBetter_Cursors->isChecked()){ - setCursor(editor->cursor); - } -} -void MapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { - map->clearHoveredTile(); - if (editor->ui->actionBetter_Cursors->isChecked()){ - unsetCursor(); - } -} -void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - map->paint_tile_initial_x = x; - map->paint_tile_initial_y = y; - emit mouseEvent(event, this); -} -void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - updateCurHoveredTile(event->pos()); - emit mouseEvent(event, this); -} -void MapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - emit mouseEvent(event, this); -} - -void CollisionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - emit mouseEvent(event, this); -} -void CollisionPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - emit mouseEvent(event, this); -} -void CollisionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - emit mouseEvent(event, this); -} - -void CollisionPixmapItem::draw(bool ignoreCache) { - if (map) { - setPixmap(map->renderCollision(ignoreCache)); - } -} - -void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { - if (map) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - Block *block = map->getBlock(x, y); - if (block) { - block->collision = map->paint_collision; - block->elevation = map->paint_elevation; - map->_setBlock(x, y, *block); - } - if (event->type() == QEvent::GraphicsSceneMouseRelease) { - map->commit(); - } - draw(); - } -} - -void CollisionPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { - if (map) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - map->floodFillCollisionElevation(x, y, map->paint_collision, map->paint_elevation); - draw(); - } -} - -void CollisionPixmapItem::pick(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - Block *block = map->getBlock(x, y); - if (block) { - map->paint_collision = block->collision; - map->paint_elevation = block->elevation; - emit map->paintCollisionChanged(map); - } -} - -void CollisionPixmapItem::updateMovementPermissionSelection(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - - // Snap point to within map bounds. - if (x < 0) x = 0; - if (x >= map->getWidth()) x = map->getWidth() - 1; - if (y < 0) y = 0; - if (y >= map->getHeight()) y = map->getHeight() - 1; - - Block *block = map->getBlock(x, y); - map->paint_collision = block->collision; - map->paint_elevation = block->elevation; - editor->collision_metatiles_item->draw(); -} - -void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { - active = true; - last_x = static_cast(mouse->pos().x() + this->pos().x()) / 16; - last_y = static_cast(mouse->pos().y() + this->pos().y()) / 16; - this->editor->selectMapEvent(this, mouse->modifiers() & Qt::ControlModifier); - this->editor->updateSelectedEvents(); - selectingEvent = true; - //qDebug() << QString("(%1, %2)").arg(event->get("x")).arg(event->get("y")); -} - -void DraggablePixmapItem::move(int x, int y) { - event->setX(event->x() + x); - event->setY(event->y() + y); - updatePosition(); - emitPositionChanged(); -} - -void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { - if (active) { - int x = static_cast(mouse->pos().x() + this->pos().x()) / 16; - int y = static_cast(mouse->pos().y() + this->pos().y()) / 16; - if (x != last_x || y != last_y) { - if (editor->selected_events->contains(this)) { - for (DraggablePixmapItem *item : *editor->selected_events) { - item->move(x - last_x, y - last_y); - } - } else { - move(x - last_x, y - last_y); - } - last_x = x; - last_y = y; - //qDebug() << QString("(%1, %2)").arg(event->get("x")).arg(event->get("x")); - } - } -} - -void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *) { - active = false; -} - -void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { - if (this->event->get("event_type") == EventType::Warp) { - QString destMap = this->event->get("destination_map_name"); - if (destMap != NONE_MAP_NAME) { - emit editor->warpEventDoubleClicked(this->event->get("destination_map_name"), this->event->get("destination_warp")); - } - } -} - -QList *Editor::getObjects() { - QList *list = new QList; - for (Event *event : map->getAllEvents()) { - for (QGraphicsItem *child : events_group->childItems()) { - DraggablePixmapItem *item = static_cast(child); - if (item->event == event) { - list->append(item); - break; - } - } - } - return list; -} - -void Editor::redrawObject(DraggablePixmapItem *item) { - if (item) { - item->setPixmap(item->event->pixmap); - item->setShapeMode(QGraphicsPixmapItem::BoundingRectShape); - if (selected_events && selected_events->contains(item)) { - QImage image = item->pixmap().toImage(); - QPainter painter(&image); - painter.setPen(QColor(250, 0, 255)); - painter.drawRect(0, 0, image.width() - 1, image.height() - 1); - painter.end(); - item->setPixmap(QPixmap::fromImage(image)); - } - } -} - -void Editor::updateSelectedEvents() { - for (DraggablePixmapItem *item : *(getObjects())) { - redrawObject(item); - } - emit selectedObjectsChanged(); -} - -void Editor::selectMapEvent(DraggablePixmapItem *object) { - selectMapEvent(object, false); -} - -void Editor::selectMapEvent(DraggablePixmapItem *object, bool toggle) { - if (selected_events && object) { - if (selected_events->contains(object)) { - if (toggle) { - selected_events->removeOne(object); - } - } else { - if (!toggle) { - selected_events->clear(); - } - selected_events->append(object); - } - updateSelectedEvents(); - } -} - -DraggablePixmapItem* Editor::addNewEvent(QString event_type) { - if (project && map) { - Event *event = Event::createNewEvent(event_type, map->name); - event->put("map_name", map->name); - map->addEvent(event); - project->loadEventPixmaps(map->getAllEvents()); - DraggablePixmapItem *object = addMapEvent(event); - return object; - } - return nullptr; -} - -void Editor::deleteEvent(Event *event) { - Map *map = project->getMap(event->get("map_name")); - if (map) { - map->removeEvent(event); - } - //selected_events->removeAll(event); - //updateSelectedObjects(); -} - -// It doesn't seem to be possible to prevent the mousePress event -// from triggering both event's DraggablePixmapItem and the background mousePress. -// Since the DraggablePixmapItem's event fires first, we can set a temp -// variable "selectingEvent" so that we can detect whether or not the user -// is clicking on the background instead of an event. -void Editor::objectsView_onMousePress(QMouseEvent *event) { - bool multiSelect = event->modifiers() & Qt::ControlModifier; - if (!selectingEvent && !multiSelect && selected_events->length() > 1) { - DraggablePixmapItem *first = selected_events->first(); - selected_events->clear(); - selected_events->append(first); - updateSelectedEvents(); - } - - selectingEvent = false; -} +#include "editor.h" +#include "event.h" +#include "imageproviders.h" +#include "mapconnection.h" +#include "currentselectedmetatilespixmapitem.h" +#include "mapsceneeventfilter.h" +#include +#include +#include +#include + +static bool selectingEvent = false; + +Editor::Editor(Ui::MainWindow* ui) +{ + this->ui = ui; + this->selected_events = new QList; + this->settings = new Settings(); +} + +void Editor::saveProject() { + if (project) { + project->saveAllMaps(); + project->saveAllDataStructures(); + } +} + +void Editor::save() { + if (project && map) { + project->saveMap(map); + project->saveAllDataStructures(); + } +} + +void Editor::undo() { + if (current_view) { + map->undo(); + map_item->draw(); + collision_item->draw(); + } +} + +void Editor::redo() { + if (current_view) { + map->redo(); + map_item->draw(); + collision_item->draw(); + } +} + +void Editor::setEditingMap() { + current_view = map_item; + if (map_item) { + displayMapConnections(); + map_item->draw(); + map_item->setVisible(true); + map_item->setEnabled(true); + setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); + } + if (collision_item) { + collision_item->setVisible(false); + } + if (events_group) { + events_group->setVisible(false); + } + setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); + setConnectionItemsVisible(false); +} + +void Editor::setEditingCollision() { + current_view = collision_item; + if (collision_item) { + displayMapConnections(); + collision_item->draw(); + collision_item->setVisible(true); + setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); + } + if (map_item) { + map_item->setVisible(false); + } + if (events_group) { + events_group->setVisible(false); + } + setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); + setConnectionItemsVisible(false); +} + +void Editor::setEditingObjects() { + current_view = map_item; + if (events_group) { + events_group->setVisible(true); + } + if (map_item) { + map_item->setVisible(true); + map_item->setEnabled(false); + setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); + } + if (collision_item) { + collision_item->setVisible(false); + } + setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); + setConnectionItemsVisible(false); +} + +void Editor::setEditingConnections() { + current_view = map_item; + if (map_item) { + map_item->draw(); + map_item->setVisible(true); + map_item->setEnabled(false); + populateConnectionMapPickers(); + ui->label_NumConnections->setText(QString::number(map->connections.length())); + setConnectionsVisibility(false); + setDiveEmergeControls(); + bool controlsEnabled = selected_connection_item != nullptr; + setConnectionEditControlsEnabled(controlsEnabled); + if (selected_connection_item) { + onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); + setConnectionMap(selected_connection_item->connection->map_name); + setCurrentConnectionDirection(selected_connection_item->connection->direction); + } + } + if (collision_item) { + collision_item->setVisible(false); + } + if (events_group) { + events_group->setVisible(false); + } + setBorderItemsVisible(true, 0.4); + setConnectionItemsVisible(true); +} + +void Editor::setDiveEmergeControls() { + ui->comboBox_DiveMap->blockSignals(true); + ui->comboBox_EmergeMap->blockSignals(true); + ui->comboBox_DiveMap->setCurrentText(""); + ui->comboBox_EmergeMap->setCurrentText(""); + for (MapConnection* connection : map->connections) { + if (connection->direction == "dive") { + ui->comboBox_DiveMap->setCurrentText(connection->map_name); + } else if (connection->direction == "emerge") { + ui->comboBox_EmergeMap->setCurrentText(connection->map_name); + } + } + ui->comboBox_DiveMap->blockSignals(false); + ui->comboBox_EmergeMap->blockSignals(false); +} + +void Editor::populateConnectionMapPickers() { + ui->comboBox_ConnectedMap->blockSignals(true); + ui->comboBox_DiveMap->blockSignals(true); + ui->comboBox_EmergeMap->blockSignals(true); + + ui->comboBox_ConnectedMap->clear(); + ui->comboBox_ConnectedMap->addItems(*project->mapNames); + ui->comboBox_DiveMap->clear(); + ui->comboBox_DiveMap->addItems(*project->mapNames); + ui->comboBox_EmergeMap->clear(); + ui->comboBox_EmergeMap->addItems(*project->mapNames); + + ui->comboBox_ConnectedMap->blockSignals(false); + ui->comboBox_DiveMap->blockSignals(true); + ui->comboBox_EmergeMap->blockSignals(true); +} + +void Editor::setConnectionItemsVisible(bool visible) { + for (ConnectionPixmapItem* item : connection_edit_items) { + item->setVisible(visible); + item->setEnabled(visible); + } +} + +void Editor::setBorderItemsVisible(bool visible, qreal opacity) { + for (QGraphicsPixmapItem* item : borderItems) { + item->setVisible(visible); + item->setOpacity(opacity); + } +} + +void Editor::setCurrentConnectionDirection(QString curDirection) { + if (!selected_connection_item) + return; + + selected_connection_item->connection->direction = curDirection; + + Map *connected_map = project->getMap(selected_connection_item->connection->map_name); + QPixmap pixmap = connected_map->renderConnection(*selected_connection_item->connection); + int offset = selected_connection_item->connection->offset.toInt(nullptr, 0); + selected_connection_item->initialOffset = offset; + int x = 0, y = 0; + if (selected_connection_item->connection->direction == "up") { + x = offset * 16; + y = -pixmap.height(); + } else if (selected_connection_item->connection->direction == "down") { + x = offset * 16; + y = map->getHeight() * 16; + } else if (selected_connection_item->connection->direction == "left") { + x = -pixmap.width(); + y = offset * 16; + } else if (selected_connection_item->connection->direction == "right") { + x = map->getWidth() * 16; + y = offset * 16; + } + + selected_connection_item->basePixmap = pixmap; + QPainter painter(&pixmap); + painter.setPen(QColor(255, 0, 255)); + painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); + painter.end(); + selected_connection_item->setPixmap(pixmap); + selected_connection_item->initialX = x; + selected_connection_item->initialY = y; + selected_connection_item->blockSignals(true); + selected_connection_item->setX(x); + selected_connection_item->setY(y); + selected_connection_item->setZValue(-1); + selected_connection_item->blockSignals(false); + + setConnectionEditControlValues(selected_connection_item->connection); +} + +void Editor::updateCurrentConnectionDirection(QString curDirection) { + if (!selected_connection_item) + return; + + QString originalDirection = selected_connection_item->connection->direction; + setCurrentConnectionDirection(curDirection); + updateMirroredConnectionDirection(selected_connection_item->connection, originalDirection); +} + +void Editor::onConnectionMoved(MapConnection* connection) { + updateMirroredConnectionOffset(connection); + onConnectionOffsetChanged(connection->offset.toInt()); +} + +void Editor::onConnectionOffsetChanged(int newOffset) { + ui->spinBox_ConnectionOffset->blockSignals(true); + ui->spinBox_ConnectionOffset->setValue(newOffset); + ui->spinBox_ConnectionOffset->blockSignals(false); + +} + +void Editor::setConnectionEditControlValues(MapConnection* connection) { + QString mapName = connection ? connection->map_name : ""; + QString direction = connection ? connection->direction : ""; + int offset = connection ? connection->offset.toInt() : 0; + + ui->comboBox_ConnectedMap->blockSignals(true); + ui->comboBox_ConnectionDirection->blockSignals(true); + ui->spinBox_ConnectionOffset->blockSignals(true); + + ui->comboBox_ConnectedMap->setCurrentText(mapName); + ui->comboBox_ConnectionDirection->setCurrentText(direction); + ui->spinBox_ConnectionOffset->setValue(offset); + + ui->comboBox_ConnectedMap->blockSignals(false); + ui->comboBox_ConnectionDirection->blockSignals(false); + ui->spinBox_ConnectionOffset->blockSignals(false); +} + +void Editor::setConnectionEditControlsEnabled(bool enabled) { + ui->comboBox_ConnectionDirection->setEnabled(enabled); + ui->comboBox_ConnectedMap->setEnabled(enabled); + ui->spinBox_ConnectionOffset->setEnabled(enabled); + + if (!enabled) { + setConnectionEditControlValues(nullptr); + } +} + +void Editor::onConnectionItemSelected(ConnectionPixmapItem* connectionItem) { + if (!connectionItem) + return; + + for (ConnectionPixmapItem* item : connection_edit_items) { + bool isSelectedItem = item == connectionItem; + int zValue = isSelectedItem ? 0 : -1; + qreal opacity = isSelectedItem ? 1 : 0.75; + item->setZValue(zValue); + item->render(opacity); + if (isSelectedItem) { + QPixmap pixmap = item->pixmap(); + QPainter painter(&pixmap); + painter.setPen(QColor(255, 0, 255)); + painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); + painter.end(); + item->setPixmap(pixmap); + } + } + selected_connection_item = connectionItem; + setConnectionEditControlsEnabled(true); + setConnectionEditControlValues(selected_connection_item->connection); + ui->spinBox_ConnectionOffset->setMaximum(selected_connection_item->getMaxOffset()); + ui->spinBox_ConnectionOffset->setMinimum(selected_connection_item->getMinOffset()); + onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); +} + +void Editor::setSelectedConnectionFromMap(QString mapName) { + // Search for the first connection that connects to the given map map. + for (ConnectionPixmapItem* item : connection_edit_items) { + if (item->connection->map_name == mapName) { + onConnectionItemSelected(item); + break; + } + } +} + +void Editor::onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem) { + emit loadMapRequested(connectionItem->connection->map_name, map->name); +} + +void Editor::onConnectionDirectionChanged(QString newDirection) { + ui->comboBox_ConnectionDirection->blockSignals(true); + ui->comboBox_ConnectionDirection->setCurrentText(newDirection); + ui->comboBox_ConnectionDirection->blockSignals(false); +} + +void Editor::onBorderMetatilesChanged() { + displayMapBorder(); +} + +void Editor::onHoveredMovementPermissionChanged(uint16_t collision, uint16_t elevation) { + this->ui->statusBar->showMessage(this->getMovementPermissionText(collision, elevation)); +} + +void Editor::onHoveredMovementPermissionCleared() { + this->ui->statusBar->clearMessage(); +} + +void Editor::onHoveredMetatileSelectionChanged(uint16_t metatileId) { + QString message = QString("Metatile: 0x%1") + .arg(QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper()); + this->ui->statusBar->showMessage(message); +} + +void Editor::onHoveredMetatileSelectionCleared() { + this->ui->statusBar->clearMessage(); +} + +void Editor::onSelectedMetatilesChanged() { + this->redrawCurrentMetatilesSelection(); +} + +void Editor::onHoveredMapMetatileChanged(int x, int y) { + if (x >= 0 && x < map->getWidth() && y >= 0 && y < map->getHeight()) { + int blockIndex = y * map->getWidth() + x; + int tile = map->layout->blockdata->blocks->at(blockIndex).tile; + this->ui->statusBar->showMessage(QString("X: %1, Y: %2, Metatile: 0x%3, Scale = %4x") + .arg(x) + .arg(y) + .arg(QString("%1").arg(tile, 3, 16, QChar('0')).toUpper()) + .arg(QString::number(pow(scale_base, scale_exp), 'g', 2))); + } +} + +void Editor::onHoveredMapMetatileCleared() { + this->ui->statusBar->clearMessage(); +} + +void Editor::onHoveredMapMovementPermissionChanged(int x, int y) { + if (x >= 0 && x < map->getWidth() && y >= 0 && y < map->getHeight()) { + int blockIndex = y * map->getWidth() + x; + uint16_t collision = map->layout->blockdata->blocks->at(blockIndex).collision; + uint16_t elevation = map->layout->blockdata->blocks->at(blockIndex).elevation; + QString message = QString("X: %1, Y: %2, %3") + .arg(x) + .arg(y) + .arg(this->getMovementPermissionText(collision, elevation)); + this->ui->statusBar->showMessage(message); + } +} + +void Editor::onHoveredMapMovementPermissionCleared() { + this->ui->statusBar->clearMessage(); +} + +QString Editor::getMovementPermissionText(uint16_t collision, uint16_t elevation){ + QString message; + if (collision == 0 && elevation == 0) { + message = "Collision: Transition between elevations"; + } else if (collision == 0 && elevation == 15) { + message = "Collision: Multi-Level (Bridge)"; + } else if (collision == 0 && elevation == 1) { + message = "Collision: Surf"; + } else if (collision == 0) { + message = QString("Collision: Passable, Elevation: %1").arg(elevation); + } else { + message = QString("Collision: Impassable, Elevation: %1").arg(elevation); + } + return message; +} + +void Editor::setConnectionsVisibility(bool visible) { + for (QGraphicsPixmapItem* item : connection_items) { + item->setVisible(visible); + item->setActive(visible); + } +} + +void Editor::setMap(QString map_name) { + if (map_name.isNull()) { + return; + } + if (project) { + map = project->loadMap(map_name); + selected_events->clear(); + displayMap(); + updateSelectedEvents(); + } +} + +void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item) { + if (map_edit_mode == "paint") { + if (event->buttons() & Qt::RightButton) { + item->updateMetatileSelection(event); + } else if (event->buttons() & Qt::MiddleButton) { + item->floodFill(event); + } else { + item->paint(event); + } + } else if (map_edit_mode == "select") { + item->select(event); + } else if (map_edit_mode == "fill") { + if (event->buttons() & Qt::RightButton) { + item->updateMetatileSelection(event); + } else { + item->floodFill(event); + } + } else if (map_edit_mode == "pick") { + + if (event->buttons() & Qt::RightButton) { + item->updateMetatileSelection(event); + } else { + item->pick(event); + } + } else if (map_edit_mode == "shift") { + item->shift(event); + } +} +void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item) { + if (map_edit_mode == "paint") { + if (event->buttons() & Qt::RightButton) { + item->updateMovementPermissionSelection(event); + } else if (event->buttons() & Qt::MiddleButton) { + item->floodFill(event); + } else { + item->paint(event); + } + } else if (map_edit_mode == "select") { + item->select(event); + } else if (map_edit_mode == "fill") { + if (event->buttons() & Qt::RightButton) { + item->pick(event); + } else { + item->floodFill(event); + } + } else if (map_edit_mode == "pick") { + item->pick(event); + } else if (map_edit_mode == "shift") { + item->shift(event); + } +} + +void Editor::displayMap() { + 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); + delete map_item; + } + + displayMetatileSelector(); + displayMovementPermissionSelector(); + displayMapMetatiles(); + displayMapMovementPermissions(); + displayBorderMetatiles(); + displayCurrentMetatilesSelection(); + displayMapEvents(); + displayMapConnections(); + displayMapBorder(); + displayMapGrid(); + + if (map_item) { + map_item->setVisible(false); + } + if (collision_item) { + collision_item->setVisible(false); + } + if (events_group) { + events_group->setVisible(false); + } +} + +void Editor::displayMetatileSelector() { + if (metatile_selector_item && metatile_selector_item->scene()) { + metatile_selector_item->scene()->removeItem(metatile_selector_item); + delete scene_metatiles; + } + + scene_metatiles = new QGraphicsScene; + if (!metatile_selector_item) { + metatile_selector_item = new MetatileSelector(8, map->layout->tileset_primary, map->layout->tileset_secondary); + connect(metatile_selector_item, SIGNAL(hoveredMetatileSelectionChanged(uint16_t)), + this, SLOT(onHoveredMetatileSelectionChanged(uint16_t))); + connect(metatile_selector_item, SIGNAL(hoveredMetatileSelectionCleared()), + this, SLOT(onHoveredMetatileSelectionCleared())); + connect(metatile_selector_item, SIGNAL(selectedMetatilesChanged()), + this, SLOT(onSelectedMetatilesChanged())); + metatile_selector_item->select(0); + } else { + metatile_selector_item->setTilesets(map->layout->tileset_primary, map->layout->tileset_secondary); + } + + scene_metatiles->addItem(metatile_selector_item); +} + +void Editor::displayMapMetatiles() { + map_item = new MapPixmapItem(map, this->metatile_selector_item, this->settings); + connect(map_item, SIGNAL(mouseEvent(QGraphicsSceneMouseEvent*,MapPixmapItem*)), + this, SLOT(mouseEvent_map(QGraphicsSceneMouseEvent*,MapPixmapItem*))); + connect(map_item, SIGNAL(hoveredMapMetatileChanged(int, int)), + this, SLOT(onHoveredMapMetatileChanged(int, int))); + connect(map_item, SIGNAL(hoveredMapMetatileCleared()), + this, SLOT(onHoveredMapMetatileCleared())); + + map_item->draw(true); + scene->addItem(map_item); + + int tw = 16; + int th = 16; + scene->setSceneRect( + -6 * tw, + -6 * th, + map_item->pixmap().width() + 12 * tw, + map_item->pixmap().height() + 12 * th + ); +} + +void Editor::displayMapMovementPermissions() { + if (collision_item && scene) { + scene->removeItem(collision_item); + delete collision_item; + } + collision_item = new CollisionPixmapItem(map, this->movement_permissions_selector_item, this->metatile_selector_item, this->settings); + connect(collision_item, SIGNAL(mouseEvent(QGraphicsSceneMouseEvent*,CollisionPixmapItem*)), + this, SLOT(mouseEvent_collision(QGraphicsSceneMouseEvent*,CollisionPixmapItem*))); + connect(collision_item, SIGNAL(hoveredMapMovementPermissionChanged(int, int)), + this, SLOT(onHoveredMapMovementPermissionChanged(int, int))); + connect(collision_item, SIGNAL(hoveredMapMovementPermissionCleared()), + this, SLOT(onHoveredMapMovementPermissionCleared())); + + collision_item->draw(true); + scene->addItem(collision_item); +} + +void Editor::displayBorderMetatiles() { + if (selected_border_metatiles_item && selected_border_metatiles_item->scene()) { + selected_border_metatiles_item->scene()->removeItem(selected_border_metatiles_item); + delete selected_border_metatiles_item; + } + + scene_selected_border_metatiles = new QGraphicsScene; + selected_border_metatiles_item = new BorderMetatilesPixmapItem(map, this->metatile_selector_item); + selected_border_metatiles_item->draw(); + scene_selected_border_metatiles->addItem(selected_border_metatiles_item); + + connect(selected_border_metatiles_item, SIGNAL(borderMetatilesChanged()), this, SLOT(onBorderMetatilesChanged())); +} + +void Editor::displayCurrentMetatilesSelection() { + if (scene_current_metatile_selection_item && scene_current_metatile_selection_item->scene()) { + scene_current_metatile_selection_item->scene()->removeItem(scene_current_metatile_selection_item); + delete scene_current_metatile_selection_item; + } + + scene_current_metatile_selection = new QGraphicsScene; + scene_current_metatile_selection_item = new CurrentSelectedMetatilesPixmapItem(map, this->metatile_selector_item); + scene_current_metatile_selection_item->draw(); + scene_current_metatile_selection->addItem(scene_current_metatile_selection_item); +} + +void Editor::redrawCurrentMetatilesSelection() { + if (scene_current_metatile_selection_item) { + scene_current_metatile_selection_item->draw(); + emit currentMetatilesSelectionChanged(); + } +} + +void Editor::displayMovementPermissionSelector() { + if (movement_permissions_selector_item && movement_permissions_selector_item->scene()) { + movement_permissions_selector_item->scene()->removeItem(movement_permissions_selector_item); + delete scene_collision_metatiles; + } + + scene_collision_metatiles = new QGraphicsScene; + if (!movement_permissions_selector_item) { + movement_permissions_selector_item = new MovementPermissionsSelector(); + connect(movement_permissions_selector_item, SIGNAL(hoveredMovementPermissionChanged(uint16_t, uint16_t)), + this, SLOT(onHoveredMovementPermissionChanged(uint16_t, uint16_t))); + connect(movement_permissions_selector_item, SIGNAL(hoveredMovementPermissionCleared()), + this, SLOT(onHoveredMovementPermissionCleared())); + movement_permissions_selector_item->select(0, 3); + } + + scene_collision_metatiles->addItem(movement_permissions_selector_item); +} + +void Editor::displayMapEvents() { + if (events_group) { + for (QGraphicsItem *child : events_group->childItems()) { + events_group->removeFromGroup(child); + delete child; + } + + if (events_group->scene()) { + events_group->scene()->removeItem(events_group); + } + + delete events_group; + } + + selected_events->clear(); + + events_group = new QGraphicsItemGroup; + scene->addItem(events_group); + + QList events = map->getAllEvents(); + project->loadEventPixmaps(events); + for (Event *event : events) { + addMapEvent(event); + } + //objects_group->setFiltersChildEvents(false); + events_group->setHandlesChildEvents(false); + + emit objectsChanged(); +} + +DraggablePixmapItem *Editor::addMapEvent(Event *event) { + DraggablePixmapItem *object = new DraggablePixmapItem(event, this); + events_group->addToGroup(object); + return object; +} + +void Editor::displayMapConnections() { + for (QGraphicsPixmapItem* item : connection_items) { + if (item->scene()) { + item->scene()->removeItem(item); + } + delete item; + } + connection_items.clear(); + + for (ConnectionPixmapItem* item : connection_edit_items) { + if (item->scene()) { + item->scene()->removeItem(item); + } + delete item; + } + selected_connection_item = nullptr; + connection_edit_items.clear(); + + for (MapConnection *connection : map->connections) { + if (connection->direction == "dive" || connection->direction == "emerge") { + continue; + } + createConnectionItem(connection, false); + } + + if (!connection_edit_items.empty()) { + onConnectionItemSelected(connection_edit_items.first()); + } +} + +void Editor::createConnectionItem(MapConnection* connection, bool hide) { + Map *connected_map = project->getMap(connection->map_name); + QPixmap pixmap = connected_map->renderConnection(*connection); + int offset = connection->offset.toInt(nullptr, 0); + int x = 0, y = 0; + if (connection->direction == "up") { + x = offset * 16; + y = -pixmap.height(); + } else if (connection->direction == "down") { + x = offset * 16; + y = map->getHeight() * 16; + } else if (connection->direction == "left") { + x = -pixmap.width(); + y = offset * 16; + } else if (connection->direction == "right") { + x = map->getWidth() * 16; + y = offset * 16; + } + + QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); + item->setZValue(-1); + item->setX(x); + item->setY(y); + scene->addItem(item); + connection_items.append(item); + item->setVisible(!hide); + + ConnectionPixmapItem *connection_edit_item = new ConnectionPixmapItem(pixmap, connection, x, y, map->getWidth(), map->getHeight()); + connection_edit_item->setX(x); + connection_edit_item->setY(y); + connection_edit_item->setZValue(-1); + scene->addItem(connection_edit_item); + connect(connection_edit_item, SIGNAL(connectionMoved(MapConnection*)), this, SLOT(onConnectionMoved(MapConnection*))); + connect(connection_edit_item, SIGNAL(connectionItemSelected(ConnectionPixmapItem*)), this, SLOT(onConnectionItemSelected(ConnectionPixmapItem*))); + connect(connection_edit_item, SIGNAL(connectionItemDoubleClicked(ConnectionPixmapItem*)), this, SLOT(onConnectionItemDoubleClicked(ConnectionPixmapItem*))); + connection_edit_items.append(connection_edit_item); +} + +void Editor::displayMapBorder() { + for (QGraphicsPixmapItem* item : borderItems) { + if (item->scene()) { + item->scene()->removeItem(item); + } + delete item; + } + borderItems.clear(); + + QPixmap pixmap = map->renderBorder(); + for (int y = -6; y < map->getHeight() + 6; y += 2) + for (int x = -6; x < map->getWidth() + 6; x += 2) { + QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); + item->setX(x * 16); + item->setY(y * 16); + item->setZValue(-2); + scene->addItem(item); + borderItems.append(item); + } +} + +void Editor::displayMapGrid() { + for (QGraphicsLineItem* item : gridLines) { + if (item && item->scene()) { + item->scene()->removeItem(item); + } + delete item; + } + gridLines.clear(); + ui->checkBox_ToggleGrid->disconnect(); + + int pixelWidth = map->getWidth() * 16; + int pixelHeight = map->getHeight() * 16; + for (int i = 0; i <= map->getWidth(); i++) { + int x = i * 16; + QGraphicsLineItem *line = scene->addLine(x, 0, x, pixelHeight); + line->setVisible(ui->checkBox_ToggleGrid->isChecked()); + gridLines.append(line); + connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); + } + for (int j = 0; j <= map->getHeight(); j++) { + int y = j * 16; + QGraphicsLineItem *line = scene->addLine(0, y, pixelWidth, y); + line->setVisible(ui->checkBox_ToggleGrid->isChecked()); + gridLines.append(line); + connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); + } +} + +void Editor::updateConnectionOffset(int offset) { + if (!selected_connection_item) + return; + + selected_connection_item->blockSignals(true); + offset = qMin(offset, selected_connection_item->getMaxOffset()); + offset = qMax(offset, selected_connection_item->getMinOffset()); + selected_connection_item->connection->offset = QString::number(offset); + if (selected_connection_item->connection->direction == "up" || selected_connection_item->connection->direction == "down") { + selected_connection_item->setX(selected_connection_item->initialX + (offset - selected_connection_item->initialOffset) * 16); + } else if (selected_connection_item->connection->direction == "left" || selected_connection_item->connection->direction == "right") { + selected_connection_item->setY(selected_connection_item->initialY + (offset - selected_connection_item->initialOffset) * 16); + } + selected_connection_item->blockSignals(false); + updateMirroredConnectionOffset(selected_connection_item->connection); +} + +void Editor::setConnectionMap(QString mapName) { + if (!mapName.isEmpty() && !project->mapNames->contains(mapName)) { + qDebug() << "Invalid map name " << mapName << " specified for connection."; + return; + } + if (!selected_connection_item) + return; + + if (mapName.isEmpty()) { + removeCurrentConnection(); + return; + } + + QString originalMapName = selected_connection_item->connection->map_name; + setConnectionEditControlsEnabled(true); + selected_connection_item->connection->map_name = mapName; + setCurrentConnectionDirection(selected_connection_item->connection->direction); + updateMirroredConnectionMap(selected_connection_item->connection, originalMapName); +} + +void Editor::addNewConnection() { + // Find direction with least number of connections. + QMap directionCounts = QMap({{"up", 0}, {"right", 0}, {"down", 0}, {"left", 0}}); + for (MapConnection* connection : map->connections) { + directionCounts[connection->direction]++; + } + QString minDirection = "up"; + int minCount = INT_MAX; + for (QString direction : directionCounts.keys()) { + if (directionCounts[direction] < minCount) { + minDirection = direction; + minCount = directionCounts[direction]; + } + } + + // Don't connect the map to itself. + QString defaultMapName = project->mapNames->first(); + if (defaultMapName == map->name) { + defaultMapName = project->mapNames->value(1); + } + + MapConnection* newConnection = new MapConnection; + newConnection->direction = minDirection; + newConnection->offset = "0"; + newConnection->map_name = defaultMapName; + map->connections.append(newConnection); + createConnectionItem(newConnection, true); + onConnectionItemSelected(connection_edit_items.last()); + ui->label_NumConnections->setText(QString::number(map->connections.length())); + + updateMirroredConnection(newConnection, newConnection->direction, newConnection->map_name); +} + +void Editor::updateMirroredConnectionOffset(MapConnection* connection) { + updateMirroredConnection(connection, connection->direction, connection->map_name); +} +void Editor::updateMirroredConnectionDirection(MapConnection* connection, QString originalDirection) { + updateMirroredConnection(connection, originalDirection, connection->map_name); +} +void Editor::updateMirroredConnectionMap(MapConnection* connection, QString originalMapName) { + updateMirroredConnection(connection, connection->direction, originalMapName); +} +void Editor::removeMirroredConnection(MapConnection* connection) { + updateMirroredConnection(connection, connection->direction, connection->map_name, true); +} +void Editor::updateMirroredConnection(MapConnection* connection, QString originalDirection, QString originalMapName, bool isDelete) { + if (!ui->checkBox_MirrorConnections->isChecked()) + return; + + static QMap oppositeDirections = QMap({ + {"up", "down"}, {"right", "left"}, + {"down", "up"}, {"left", "right"}, + {"dive", "emerge"},{"emerge", "dive"}}); + QString oppositeDirection = oppositeDirections.value(originalDirection); + + // Find the matching connection in the connected map. + MapConnection* mirrorConnection = nullptr; + Map* otherMap = project->getMap(originalMapName); + for (MapConnection* conn : otherMap->connections) { + if (conn->direction == oppositeDirection && conn->map_name == map->name) { + mirrorConnection = conn; + } + } + + if (isDelete) { + if (mirrorConnection) { + otherMap->connections.removeOne(mirrorConnection); + delete mirrorConnection; + } + return; + } + + if (connection->direction != originalDirection || connection->map_name != originalMapName) { + if (mirrorConnection) { + otherMap->connections.removeOne(mirrorConnection); + delete mirrorConnection; + mirrorConnection = nullptr; + otherMap = project->getMap(connection->map_name); + } + } + + // Create a new mirrored connection, if a matching one doesn't already exist. + if (!mirrorConnection) { + mirrorConnection = new MapConnection; + mirrorConnection->direction = oppositeDirections.value(connection->direction); + mirrorConnection->map_name = map->name; + otherMap->connections.append(mirrorConnection); + } + + mirrorConnection->offset = QString::number(-connection->offset.toInt()); +} + +void Editor::removeCurrentConnection() { + if (!selected_connection_item) + return; + + map->connections.removeOne(selected_connection_item->connection); + connection_edit_items.removeOne(selected_connection_item); + removeMirroredConnection(selected_connection_item->connection); + + if (selected_connection_item && selected_connection_item->scene()) { + selected_connection_item->scene()->removeItem(selected_connection_item); + delete selected_connection_item; + } + + selected_connection_item = nullptr; + setConnectionEditControlsEnabled(false); + ui->spinBox_ConnectionOffset->setValue(0); + ui->label_NumConnections->setText(QString::number(map->connections.length())); + + if (connection_edit_items.length() > 0) { + onConnectionItemSelected(connection_edit_items.last()); + } +} + +void Editor::updateDiveMap(QString mapName) { + updateDiveEmergeMap(mapName, "dive"); +} + +void Editor::updateEmergeMap(QString mapName) { + updateDiveEmergeMap(mapName, "emerge"); +} + +void Editor::updateDiveEmergeMap(QString mapName, QString direction) { + if (!mapName.isEmpty() && !project->mapNamesToMapConstants->contains(mapName)) { + qDebug() << "Invalid " << direction << " map connection: " << mapName; + return; + } + + MapConnection* connection = nullptr; + for (MapConnection* conn : map->connections) { + if (conn->direction == direction) { + connection = conn; + break; + } + } + + if (mapName.isEmpty()) { + // Remove dive/emerge connection + if (connection) { + map->connections.removeOne(connection); + removeMirroredConnection(connection); + } + } else { + if (!connection) { + connection = new MapConnection; + connection->direction = direction; + connection->offset = "0"; + connection->map_name = mapName; + map->connections.append(connection); + updateMirroredConnection(connection, connection->direction, connection->map_name); + } else { + QString originalMapName = connection->map_name; + connection->map_name = mapName; + updateMirroredConnectionMap(connection, originalMapName); + } + } + + ui->label_NumConnections->setText(QString::number(map->connections.length())); +} + +void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad) +{ + if (map->layout->tileset_primary_label != tilesetLabel || forceLoad) + { + map->layout->tileset_primary_label = tilesetLabel; + map->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad); + emit tilesetChanged(map->name); + } +} + +void Editor::updateSecondaryTileset(QString tilesetLabel, bool forceLoad) +{ + if (map->layout->tileset_secondary_label != tilesetLabel || forceLoad) + { + map->layout->tileset_secondary_label = tilesetLabel; + map->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad); + emit tilesetChanged(map->name); + } +} + +void Editor::toggleBorderVisibility(bool visible) +{ + this->setBorderItemsVisible(visible); + this->setConnectionsVisibility(visible); +} + +Tileset* Editor::getCurrentMapPrimaryTileset() +{ + QString tilesetLabel = map->layout->tileset_primary_label; + return project->getTileset(tilesetLabel); +} + +void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { + active = true; + last_x = static_cast(mouse->pos().x() + this->pos().x()) / 16; + last_y = static_cast(mouse->pos().y() + this->pos().y()) / 16; + this->editor->selectMapEvent(this, mouse->modifiers() & Qt::ControlModifier); + this->editor->updateSelectedEvents(); + selectingEvent = true; + //qDebug() << QString("(%1, %2)").arg(event->get("x")).arg(event->get("y")); +} + +void DraggablePixmapItem::move(int x, int y) { + event->setX(event->x() + x); + event->setY(event->y() + y); + updatePosition(); + emitPositionChanged(); +} + +void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { + if (active) { + int x = static_cast(mouse->pos().x() + this->pos().x()) / 16; + int y = static_cast(mouse->pos().y() + this->pos().y()) / 16; + if (x != last_x || y != last_y) { + if (editor->selected_events->contains(this)) { + for (DraggablePixmapItem *item : *editor->selected_events) { + item->move(x - last_x, y - last_y); + } + } else { + move(x - last_x, y - last_y); + } + last_x = x; + last_y = y; + //qDebug() << QString("(%1, %2)").arg(event->get("x")).arg(event->get("x")); + } + } +} + +void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *) { + active = false; +} + +void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { + if (this->event->get("event_type") == EventType::Warp) { + QString destMap = this->event->get("destination_map_name"); + if (destMap != NONE_MAP_NAME) { + emit editor->warpEventDoubleClicked(this->event->get("destination_map_name"), this->event->get("destination_warp")); + } + } +} + +QList *Editor::getObjects() { + QList *list = new QList; + for (Event *event : map->getAllEvents()) { + for (QGraphicsItem *child : events_group->childItems()) { + DraggablePixmapItem *item = static_cast(child); + if (item->event == event) { + list->append(item); + break; + } + } + } + return list; +} + +void Editor::redrawObject(DraggablePixmapItem *item) { + if (item) { + item->setPixmap(item->event->pixmap); + item->setShapeMode(QGraphicsPixmapItem::BoundingRectShape); + if (selected_events && selected_events->contains(item)) { + QImage image = item->pixmap().toImage(); + QPainter painter(&image); + painter.setPen(QColor(250, 0, 255)); + painter.drawRect(0, 0, image.width() - 1, image.height() - 1); + painter.end(); + item->setPixmap(QPixmap::fromImage(image)); + } + } +} + +void Editor::updateSelectedEvents() { + for (DraggablePixmapItem *item : *(getObjects())) { + redrawObject(item); + } + emit selectedObjectsChanged(); +} + +void Editor::selectMapEvent(DraggablePixmapItem *object) { + selectMapEvent(object, false); +} + +void Editor::selectMapEvent(DraggablePixmapItem *object, bool toggle) { + if (selected_events && object) { + if (selected_events->contains(object)) { + if (toggle) { + selected_events->removeOne(object); + } + } else { + if (!toggle) { + selected_events->clear(); + } + selected_events->append(object); + } + updateSelectedEvents(); + } +} + +DraggablePixmapItem* Editor::addNewEvent(QString event_type) { + if (project && map) { + Event *event = Event::createNewEvent(event_type, map->name); + event->put("map_name", map->name); + map->addEvent(event); + project->loadEventPixmaps(map->getAllEvents()); + DraggablePixmapItem *object = addMapEvent(event); + return object; + } + return nullptr; +} + +void Editor::deleteEvent(Event *event) { + Map *map = project->getMap(event->get("map_name")); + if (map) { + map->removeEvent(event); + } + //selected_events->removeAll(event); + //updateSelectedObjects(); +} + +// It doesn't seem to be possible to prevent the mousePress event +// from triggering both event's DraggablePixmapItem and the background mousePress. +// Since the DraggablePixmapItem's event fires first, we can set a temp +// variable "selectingEvent" so that we can detect whether or not the user +// is clicking on the background instead of an event. +void Editor::objectsView_onMousePress(QMouseEvent *event) { + bool multiSelect = event->modifiers() & Qt::ControlModifier; + if (!selectingEvent && !multiSelect && selected_events->length() > 1) { + DraggablePixmapItem *first = selected_events->first(); + selected_events->clear(); + selected_events->append(first); + updateSelectedEvents(); + } + + selectingEvent = false; +} diff --git a/main.cpp b/src/main.cpp old mode 100755 new mode 100644 similarity index 94% rename from main.cpp rename to src/main.cpp index 94e1c1b8..ab638672 --- a/main.cpp +++ b/src/main.cpp @@ -1,12 +1,12 @@ -#include "mainwindow.h" -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - a.setStyle("fusion"); - MainWindow w; - w.show(); - - return a.exec(); -} +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.setStyle("fusion"); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/mainwindow.cpp b/src/mainwindow.cpp old mode 100755 new mode 100644 similarity index 86% rename from mainwindow.cpp rename to src/mainwindow.cpp index c5c90f65..4adf9562 --- a/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,1183 +1,1266 @@ -#include "mainwindow.h" -#include "ui_mainwindow.h" -#include "project.h" -#include "editor.h" -#include "objectpropertiesframe.h" -#include "ui_objectpropertiesframe.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) -{ - QCoreApplication::setOrganizationName("pret"); - QCoreApplication::setApplicationName("porymap"); - QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-1.ico")); - - ui->setupUi(this); - - ui->newEventToolButton->initButton(); - connect(ui->newEventToolButton, SIGNAL(newEventAdded(QString)), this, SLOT(addNewEvent(QString))); - - new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo())); - - editor = new Editor(ui); - connect(editor, SIGNAL(objectsChanged()), this, SLOT(updateSelectedObjects())); - connect(editor, SIGNAL(selectedObjectsChanged()), this, SLOT(updateSelectedObjects())); - connect(editor, SIGNAL(loadMapRequested(QString, QString)), this, SLOT(onLoadMapRequested(QString, QString))); - connect(editor, SIGNAL(tilesetChanged(QString)), this, SLOT(onTilesetChanged(QString))); - connect(editor, SIGNAL(warpEventDoubleClicked(QString,QString)), this, SLOT(openWarpMap(QString,QString))); - connect(editor, SIGNAL(currentMetatilesSelectionChanged()), this, SLOT(currentMetatilesSelectionChanged())); - - on_toolButton_Paint_clicked(); - - QSettings settings; - QString key = "recent_projects"; - if (settings.contains(key)) { - QString default_dir = settings.value(key).toStringList().last(); - if (!default_dir.isNull()) { - qDebug() << QString("default_dir: '%1'").arg(default_dir); - openProject(default_dir); - } - } - - if (settings.contains("cursor_mode") && settings.value("cursor_mode") == "0") { - ui->actionBetter_Cursors->setChecked(false); - } -} - -MainWindow::~MainWindow() -{ - delete ui; -} - -void MainWindow::setStatusBarMessage(QString message, int timeout/* = 0*/) { - statusBar()->showMessage(message, timeout); -} - -void MainWindow::openProject(QString dir) { - if (dir.isNull()) { - return; - } - - setStatusBarMessage(QString("Opening project %1").arg(dir)); - - bool already_open = ( - (editor && editor != nullptr) - && (editor->project && editor->project != nullptr) - && (editor->project->root == dir) - ); - if (!already_open) { - editor->project = new Project; - editor->project->root = dir; - setWindowTitle(editor->project->getProjectTitle() + " - porymap"); - loadDataStructures(); - populateMapList(); - setMap(getDefaultMap()); - } else { - setWindowTitle(editor->project->getProjectTitle() + " - porymap"); - loadDataStructures(); - populateMapList(); - } - - setStatusBarMessage(QString("Opened project %1").arg(dir)); -} - -QString MainWindow::getDefaultMap() { - if (editor && editor->project) { - QList names = editor->project->groupedMapNames; - if (!names.isEmpty()) { - QSettings settings; - QString key = "project:" + editor->project->root; - if (settings.contains(key)) { - QMap qmap = settings.value(key).toMap(); - if (qmap.contains("recent_map")) { - QString map_name = qmap.value("recent_map").toString(); - for (int i = 0; i < names.length(); i++) { - if (names.value(i).contains(map_name)) { - return map_name; - } - } - } - } - // Failing that, just get the first map in the list. - for (int i = 0; i < names.length(); i++) { - QStringList list = names.value(i); - if (list.length()) { - return list.value(0); - } - } - } - } - return QString(); -} - -QString MainWindow::getExistingDirectory(QString dir) { - return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); -} - -void MainWindow::on_action_Open_Project_triggered() -{ - QSettings settings; - QString key = "recent_projects"; - QString recent = "."; - if (settings.contains(key)) { - recent = settings.value(key).toStringList().last(); - } - QString dir = getExistingDirectory(recent); - if (!dir.isEmpty()) { - QStringList recents; - if (settings.contains(key)) { - recents = settings.value(key).toStringList(); - } - recents.removeAll(dir); - recents.append(dir); - settings.setValue(key, recents); - - openProject(dir); - } -} - -void MainWindow::setMap(QString map_name) { - qDebug() << QString("setMap(%1)").arg(map_name); - if (map_name.isNull()) { - return; - } - editor->setMap(map_name); - redrawMapScene(); - displayMapProperties(); - - setWindowTitle(map_name + " - " + editor->project->getProjectTitle() + " - porymap"); - - connect(editor->map, SIGNAL(mapChanged(Map*)), this, SLOT(onMapChanged(Map *))); - connect(editor->map, SIGNAL(mapNeedsRedrawing()), this, SLOT(onMapNeedsRedrawing())); - connect(editor->map, SIGNAL(statusBarMessage(QString)), this, SLOT(setStatusBarMessage(QString))); - - setRecentMap(map_name); - updateMapList(); -} - -void MainWindow::redrawMapScene() -{ - editor->displayMap(); - on_tabWidget_currentChanged(ui->tabWidget->currentIndex()); - - 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_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->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_Metatiles->setScene(editor->scene_metatiles); - //ui->graphicsView_Metatiles->setSceneRect(editor->scene_metatiles->sceneRect()); - ui->graphicsView_Metatiles->setFixedSize(editor->metatiles_item->pixmap().width() + 2, editor->metatiles_item->pixmap().height() + 2); - - ui->graphicsView_BorderMetatile->setScene(editor->scene_selected_border_metatiles); - ui->graphicsView_BorderMetatile->setFixedSize(editor->selected_border_metatiles_item->pixmap().width() + 2, editor->selected_border_metatiles_item->pixmap().height() + 2); - - ui->graphicsView_currentMetatileSelection->setScene(editor->scene_current_metatile_selection); - ui->graphicsView_currentMetatileSelection->setFixedSize(editor->scene_current_metatile_selection_item->pixmap().width() + 2, editor->scene_current_metatile_selection_item->pixmap().height() + 2); - - ui->graphicsView_Collision->setScene(editor->scene_collision_metatiles); - //ui->graphicsView_Collision->setSceneRect(editor->scene_collision_metatiles->sceneRect()); - ui->graphicsView_Collision->setFixedSize(editor->collision_metatiles_item->pixmap().width() + 2, editor->collision_metatiles_item->pixmap().height() + 2); -} - -void MainWindow::openWarpMap(QString map_name, QString warp_num) { - // Ensure valid destination map name. - if (!editor->project->mapNames->contains(map_name)) { - qDebug() << QString("Invalid warp destination map name '%1'").arg(map_name); - return; - } - - // Ensure valid destination warp number. - bool ok; - int warpNum = warp_num.toInt(&ok, 0); - if (!ok) { - qDebug() << QString("Invalid warp number '%1' for destination map '%2'").arg(warp_num).arg(map_name); - return; - } - - // Open the destination map, and select the target warp event. - setMap(map_name); - QList warp_events = editor->map->events["warp_event_group"]; - if (warp_events.length() > warpNum) { - Event *warp_event = warp_events.at(warpNum); - QList *all_events = editor->getObjects(); - for (DraggablePixmapItem *item : *all_events) { - if (item->event == warp_event) { - editor->selected_events->clear(); - editor->selected_events->append(item); - editor->updateSelectedEvents(); - } - } - - delete all_events; - } -} - -void MainWindow::setRecentMap(QString map_name) { - QSettings settings; - QString key = "project:" + editor->project->root; - QMap qmap; - if (settings.contains(key)) { - qmap = settings.value(key).toMap(); - } - qmap.insert("recent_map", map_name); - settings.setValue(key, qmap); -} - -void MainWindow::displayMapProperties() { - ui->comboBox_Song->clear(); - ui->comboBox_Location->clear(); - ui->checkBox_Visibility->setChecked(false); - ui->comboBox_Weather->clear(); - ui->comboBox_Type->clear(); - ui->comboBox_BattleScene->clear(); - ui->comboBox_PrimaryTileset->clear(); - ui->comboBox_SecondaryTileset->clear(); - ui->checkBox_ShowLocation->setChecked(false); - if (!editor || !editor->map || !editor->project) { - ui->frame_3->setEnabled(false); - return; - } - ui->frame_3->setEnabled(true); - Map *map = editor->map; - Project *project = editor->project; - - QStringList songs = project->getSongNames(); - ui->comboBox_Song->addItems(songs); - ui->comboBox_Song->setCurrentText(map->song); - - ui->comboBox_Location->addItems(*project->regionMapSections); - ui->comboBox_Location->setCurrentText(map->location); - - QMap tilesets = project->getTilesets(); - ui->comboBox_PrimaryTileset->addItems(tilesets.value("primary")); - ui->comboBox_PrimaryTileset->setCurrentText(map->layout->tileset_primary_label); - ui->comboBox_SecondaryTileset->addItems(tilesets.value("secondary")); - ui->comboBox_SecondaryTileset->setCurrentText(map->layout->tileset_secondary_label); - - ui->checkBox_Visibility->setChecked(map->requiresFlash.toInt() > 0 || map->requiresFlash == "TRUE"); - - ui->comboBox_Weather->addItems(*project->weatherNames); - ui->comboBox_Weather->setCurrentText(map->weather); - - ui->comboBox_Type->addItems(*project->mapTypes); - ui->comboBox_Type->setCurrentText(map->type); - - ui->comboBox_BattleScene->addItems(*project->mapBattleScenes); - ui->comboBox_BattleScene->setCurrentText(map->battle_scene); - - ui->checkBox_ShowLocation->setChecked(map->show_location.toInt() > 0 || map->show_location == "TRUE"); -} - -void MainWindow::on_comboBox_Song_activated(const QString &song) -{ - if (editor && editor->map) { - editor->map->song = song; - } -} - -void MainWindow::on_comboBox_Location_activated(const QString &location) -{ - if (editor && editor->map) { - editor->map->location = location; - } -} - -void MainWindow::on_comboBox_Visibility_activated(const QString &requiresFlash) -{ - if (editor && editor->map) { - editor->map->requiresFlash = requiresFlash; - } -} - -void MainWindow::on_comboBox_Weather_activated(const QString &weather) -{ - if (editor && editor->map) { - editor->map->weather = weather; - } -} - -void MainWindow::on_comboBox_Type_activated(const QString &type) -{ - if (editor && editor->map) { - editor->map->type = type; - } -} - -void MainWindow::on_comboBox_BattleScene_activated(const QString &battle_scene) -{ - if (editor && editor->map) { - editor->map->battle_scene = battle_scene; - } -} - -void MainWindow::on_checkBox_Visibility_clicked(bool checked) -{ - if (editor && editor->map) { - if (checked) { - editor->map->requiresFlash = "TRUE"; - } else { - editor->map->requiresFlash = "FALSE"; - } - } -} - -void MainWindow::on_checkBox_ShowLocation_clicked(bool checked) -{ - if (editor && editor->map) { - if (checked) { - editor->map->show_location = "TRUE"; - } else { - editor->map->show_location = "FALSE"; - } - } -} - -void MainWindow::loadDataStructures() { - Project *project = editor->project; - project->readMapLayoutsTable(); - project->readAllMapLayouts(); - project->readRegionMapSections(); - project->readItemNames(); - project->readFlagNames(); - project->readVarNames(); - project->readMovementTypes(); - project->readMapTypes(); - project->readMapBattleScenes(); - project->readWeatherNames(); - project->readCoordEventWeatherNames(); - project->readSecretBaseIds(); - project->readBgEventFacingDirections(); - project->readMapsWithConnections(); - project->readTilesetProperties(); -} - -void MainWindow::populateMapList() { - Project *project = editor->project; - - QIcon mapFolderIcon; - mapFolderIcon.addFile(QStringLiteral(":/icons/folder_closed_map.ico"), QSize(), QIcon::Normal, QIcon::Off); - mapFolderIcon.addFile(QStringLiteral(":/icons/folder_map.ico"), QSize(), QIcon::Normal, QIcon::On); - - QIcon folderIcon; - folderIcon.addFile(QStringLiteral(":/icons/folder_closed.ico"), QSize(), QIcon::Normal, QIcon::Off); - - mapIcon = new QIcon; - mapIcon->addFile(QStringLiteral(":/icons/map.ico"), QSize(), QIcon::Normal, QIcon::Off); - mapIcon->addFile(QStringLiteral(":/icons/image.ico"), QSize(), QIcon::Normal, QIcon::On); - - mapListModel = new QStandardItemModel; - mapGroupsModel = new QList; - - QStandardItem *entry = new QStandardItem; - entry->setText(project->getProjectTitle()); - entry->setIcon(folderIcon); - entry->setEditable(false); - mapListModel->appendRow(entry); - - QStandardItem *maps = new QStandardItem; - maps->setText("maps"); - maps->setIcon(folderIcon); - maps->setEditable(false); - entry->appendRow(maps); - - project->readMapGroups(); - for (int i = 0; i < project->groupNames->length(); i++) { - QString group_name = project->groupNames->value(i); - QStandardItem *group = new QStandardItem; - group->setText(group_name); - group->setIcon(mapFolderIcon); - group->setEditable(false); - group->setData(group_name, Qt::UserRole); - group->setData("map_group", MapListUserRoles::TypeRole); - group->setData(i, MapListUserRoles::GroupRole); - maps->appendRow(group); - mapGroupsModel->append(group); - QStringList names = project->groupedMapNames.value(i); - for (int j = 0; j < names.length(); j++) { - QString map_name = names.value(j); - QStandardItem *map = createMapItem(map_name, i, j); - group->appendRow(map); - } - } - - // Right-clicking on items in the map list tree view brings up a context menu. - ui->mapList->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->mapList, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(onOpenMapListContextMenu(const QPoint &))); - - ui->mapList->setModel(mapListModel); - ui->mapList->setUpdatesEnabled(true); - ui->mapList->expandToDepth(2); - ui->mapList->repaint(); -} - -QStandardItem* MainWindow::createMapItem(QString mapName, int groupNum, int inGroupNum) { - QStandardItem *map = new QStandardItem; - map->setText(QString("[%1.%2] ").arg(groupNum).arg(inGroupNum, 2, 10, QLatin1Char('0')) + mapName); - map->setIcon(*mapIcon); - map->setEditable(false); - map->setData(mapName, Qt::UserRole); - map->setData("map_name", MapListUserRoles::TypeRole); - return map; -} - -void MainWindow::onOpenMapListContextMenu(const QPoint &point) -{ - QModelIndex index = ui->mapList->indexAt(point); - if (!index.isValid()) { - return; - } - - QStandardItem *selectedItem = mapListModel->itemFromIndex(index); - QVariant itemType = selectedItem->data(MapListUserRoles::TypeRole); - if (!itemType.isValid()) { - return; - } - - // Build custom context menu depending on which type of item was selected (map group, map name, etc.) - if (itemType == "map_group") { - QString groupName = selectedItem->data(Qt::UserRole).toString(); - int groupNum = selectedItem->data(MapListUserRoles::GroupRole).toInt(); - QMenu* menu = new QMenu(); - QActionGroup* actions = new QActionGroup(menu); - actions->addAction(menu->addAction("Add New Map to Group"))->setData(groupNum); - connect(actions, SIGNAL(triggered(QAction*)), this, SLOT(onAddNewMapToGroupClick(QAction*))); - menu->exec(QCursor::pos()); - } -} - -void MainWindow::onAddNewMapToGroupClick(QAction* triggeredAction) -{ - int groupNum = triggeredAction->data().toInt(); - QStandardItem* groupItem = mapGroupsModel->at(groupNum); - - QString newMapName = editor->project->getNewMapName(); - Map* newMap = editor->project->addNewMapToGroup(newMapName, groupNum); - editor->project->saveMap(newMap); - editor->project->saveAllDataStructures(); - - int numMapsInGroup = groupItem->rowCount(); - QStandardItem *newMapItem = createMapItem(newMapName, groupNum, numMapsInGroup); - groupItem->appendRow(newMapItem); - - setMap(newMapName); -} - -void MainWindow::onTilesetChanged(QString mapName) -{ - setMap(mapName); -} - -void MainWindow::currentMetatilesSelectionChanged() -{ - ui->graphicsView_currentMetatileSelection->setFixedSize(editor->scene_current_metatile_selection_item->pixmap().width() + 2, editor->scene_current_metatile_selection_item->pixmap().height() + 2); - ui->graphicsView_currentMetatileSelection->setSceneRect(0, 0, editor->scene_current_metatile_selection_item->pixmap().width(), editor->scene_current_metatile_selection_item->pixmap().height()); -} - -void MainWindow::on_mapList_activated(const QModelIndex &index) -{ - QVariant data = index.data(Qt::UserRole); - if (!data.isNull()) { - setMap(data.toString()); - } -} - -void MainWindow::markAllEdited(QAbstractItemModel *model) { - QList list; - list.append(QModelIndex()); - while (list.length()) { - QModelIndex parent = list.takeFirst(); - for (int i = 0; i < model->rowCount(parent); i++) { - QModelIndex index = model->index(i, 0, parent); - if (model->hasChildren(index)) { - list.append(index); - } - markEdited(index); - } - } -} - -void MainWindow::markEdited(QModelIndex index) { - QVariant data = index.data(Qt::UserRole); - if (!data.isNull()) { - QString map_name = data.toString(); - if (editor->project) { - if (editor->project->map_cache->contains(map_name)) { - // Just mark anything that's been opened for now. - // TODO if (project->getMap()->saved) - //ui->mapList->setExpanded(index, true); - ui->mapList->setExpanded(index, editor->project->map_cache->value(map_name)->hasUnsavedChanges()); - } - } - } -} - -void MainWindow::updateMapList() { - QAbstractItemModel *model = ui->mapList->model(); - markAllEdited(model); -} - -void MainWindow::on_action_Save_Project_triggered() -{ - editor->saveProject(); - updateMapList(); -} - -void MainWindow::undo() { - editor->undo(); -} - -void MainWindow::redo() { - editor->redo(); -} - -// Open current map scripts in system default editor for .inc files -void MainWindow::openInTextEditor() { - QString path = QDir::cleanPath("file://" + editor->project->root + QDir::separator() + "data/maps/" + editor->map->name + "/scripts.inc"); - QDesktopServices::openUrl(QUrl(path)); -} - -void MainWindow::on_action_Save_triggered() { - editor->save(); - updateMapList(); -} - -void MainWindow::on_tabWidget_2_currentChanged(int index) -{ - if (index == 0) { - editor->setEditingMap(); - } else if (index == 1) { - editor->setEditingCollision(); - } -} - -void MainWindow::on_action_Exit_triggered() -{ - QApplication::quit(); -} - -void MainWindow::on_tabWidget_currentChanged(int index) -{ - if (index == 0) { - on_tabWidget_2_currentChanged(ui->tabWidget_2->currentIndex()); - } else if (index == 1) { - editor->setEditingObjects(); - } else if (index == 3) { - editor->setEditingConnections(); - } -} - -void MainWindow::on_actionUndo_triggered() -{ - undo(); -} - -void MainWindow::on_actionRedo_triggered() -{ - redo(); -} - -void MainWindow::on_actionZoom_In_triggered() { - scaleMapView(1); -} - -void MainWindow::on_actionZoom_Out_triggered() { - scaleMapView(-1); -} - -void MainWindow::on_actionBetter_Cursors_triggered() { - QSettings settings; - settings.setValue("cursor_mode", QString::number(ui->actionBetter_Cursors->isChecked())); -} - -void MainWindow::on_actionPencil_triggered() -{ - on_toolButton_Paint_clicked(); -} - -void MainWindow::on_actionPointer_triggered() -{ - on_toolButton_Select_clicked(); -} - -void MainWindow::on_actionFlood_Fill_triggered() -{ - on_toolButton_Fill_clicked(); -} - -void MainWindow::on_actionEyedropper_triggered() -{ - on_toolButton_Dropper_clicked(); -} - -void MainWindow::on_actionMove_triggered() -{ - on_toolButton_Move_clicked(); -} - -void MainWindow::on_actionMap_Shift_triggered() -{ - on_toolButton_Shift_clicked(); -} - -void MainWindow::scaleMapView(int s) { - editor->map->scale_exp += s; - - double base = editor->map->scale_base; - double exp = editor->map->scale_exp; - double sfactor = pow(base,s); - - ui->graphicsView_Map->scale(sfactor,sfactor); - ui->graphicsView_Objects_Map->scale(sfactor,sfactor); - - 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); -} - -void MainWindow::addNewEvent(QString event_type) -{ - if (editor) { - DraggablePixmapItem *object = editor->addNewEvent(event_type); - if (object) { - editor->selectMapEvent(object, false); - } - updateSelectedObjects(); - } -} - -// Should probably just pass layout and let the editor work it out -void MainWindow::updateSelectedObjects() { - QList *all_events = editor->getObjects(); - QList *events = nullptr; - - if (editor->selected_events && editor->selected_events->length()) { - events = editor->selected_events; - } else { - events = new QList; - if (all_events && all_events->length()) { - DraggablePixmapItem *selectedEvent = all_events->first(); - editor->selected_events->append(selectedEvent); - editor->redrawObject(selectedEvent); - events->append(selectedEvent); - } - } - - QMap event_obj_gfx_constants = editor->project->getEventObjGfxConstants(); - - QList frames; - - for (DraggablePixmapItem *item : *events) { - ObjectPropertiesFrame *frame = new ObjectPropertiesFrame; -// frame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - QSpinBox *x = frame->ui->spinBox_x; - QSpinBox *y = frame->ui->spinBox_y; - QSpinBox *z = frame->ui->spinBox_z; - - x->setValue(item->event->x()); - connect(x, SIGNAL(valueChanged(QString)), item, SLOT(set_x(QString))); - connect(item, SIGNAL(xChanged(int)), x, SLOT(setValue(int))); - - y->setValue(item->event->y()); - connect(y, SIGNAL(valueChanged(QString)), item, SLOT(set_y(QString))); - connect(item, SIGNAL(yChanged(int)), y, SLOT(setValue(int))); - - z->setValue(item->event->elevation()); - connect(z, SIGNAL(valueChanged(QString)), item, SLOT(set_elevation(QString))); - connect(item, SIGNAL(elevationChanged(int)), z, SLOT(setValue(int))); - - QFont font; - font.setCapitalization(QFont::Capitalize); - frame->ui->label_name->setFont(font); - QString event_type = item->event->get("event_type"); - QString event_group_type = item->event->get("event_group_type"); - QString map_name = item->event->get("map_name"); - int event_offs; - if (event_type == "event_warp") { event_offs = 0; } - else { event_offs = 1; } - frame->ui->label_name->setText( - QString("%1: %2 %3") - .arg(editor->project->getMap(map_name)->events.value(event_group_type).indexOf(item->event) + event_offs) - .arg(map_name) - .arg(event_type) - ); - - frame->ui->label_spritePixmap->setPixmap(item->event->pixmap); - connect(item, SIGNAL(spriteChanged(QPixmap)), frame->ui->label_spritePixmap, SLOT(setPixmap(QPixmap))); - - frame->ui->sprite->setVisible(false); - - QMap field_labels; - field_labels["script_label"] = "Script"; - field_labels["event_flag"] = "Event Flag"; - field_labels["movement_type"] = "Movement"; - field_labels["radius_x"] = "Movement Radius X"; - field_labels["radius_y"] = "Movement Radius Y"; - field_labels["is_trainer"] = "Trainer"; - field_labels["sight_radius_tree_id"] = "Sight Radius / Berry Tree ID"; - field_labels["destination_warp"] = "Destination Warp"; - field_labels["destination_map_name"] = "Destination Map"; - field_labels["script_var"] = "Var"; - field_labels["script_var_value"] = "Var Value"; - field_labels["player_facing_direction"] = "Player Facing Direction"; - field_labels["item"] = "Item"; - field_labels["item_unknown5"] = "Unknown 5"; - field_labels["item_unknown6"] = "Unknown 6"; - field_labels["weather"] = "Weather"; - field_labels["flag"] = "Flag"; - field_labels["secret_base_id"] = "Secret Base Id"; - - QStringList fields; - - if (event_type == EventType::Object) { - - frame->ui->sprite->setVisible(true); - frame->ui->comboBox_sprite->addItems(event_obj_gfx_constants.keys()); - frame->ui->comboBox_sprite->setCurrentText(item->event->get("sprite")); - connect(frame->ui->comboBox_sprite, SIGNAL(activated(QString)), item, SLOT(set_sprite(QString))); - - /* - frame->ui->script->setVisible(true); - frame->ui->comboBox_script->addItem(item->event->get("script_label")); - frame->ui->comboBox_script->setCurrentText(item->event->get("script_label")); - //item->bind(frame->ui->comboBox_script, "script_label"); - connect(frame->ui->comboBox_script, SIGNAL(activated(QString)), item, SLOT(set_script(QString))); - //connect(frame->ui->comboBox_script, static_cast(&QComboBox::activated), item, [item](QString script_label){ item->event->put("script_label", script_label); }); - //connect(item, SIGNAL(scriptChanged(QString)), frame->ui->comboBox_script, SLOT(setValue(QString))); - */ - - fields << "movement_type"; - fields << "radius_x"; - fields << "radius_y"; - fields << "script_label"; - fields << "event_flag"; - fields << "is_trainer"; - fields << "sight_radius_tree_id"; - } - else if (event_type == EventType::Warp) { - fields << "destination_map_name"; - fields << "destination_warp"; - } - else if (event_type == EventType::CoordScript) { - fields << "script_label"; - fields << "script_var"; - fields << "script_var_value"; - } - else if (event_type == EventType::CoordWeather) { - fields << "weather"; - } - else if (event_type == EventType::Sign) { - fields << "player_facing_direction"; - fields << "script_label"; - } - else if (event_type == EventType::HiddenItem) { - fields << "item"; - fields << "flag"; - } - else if (event_type == EventType::SecretBase) { - fields << "secret_base_id"; - } - - for (QString key : fields) { - QString value = item->event->get(key); - QWidget *widget = new QWidget(frame); - QFormLayout *fl = new QFormLayout(widget); - fl->setContentsMargins(9, 0, 9, 0); - - // is_trainer is the only non-combobox item. - if (key == "is_trainer") { - QCheckBox *checkbox = new QCheckBox(widget); - checkbox->setEnabled(true); - checkbox->setChecked(value.toInt() != 0 && value != "FALSE"); - checkbox->setToolTip("Whether or not this object is trainer."); - fl->addRow(new QLabel(field_labels[key], widget), checkbox); - widget->setLayout(fl); - frame->layout()->addWidget(widget); - connect(checkbox, &QCheckBox::stateChanged, [=](int state) { - QString isTrainer = state == Qt::Checked ? "TRUE" : "FALSE"; - item->event->put("is_trainer", isTrainer); - }); - continue; - } - - NoScrollComboBox *combo = new NoScrollComboBox(widget); - combo->setEditable(true); - - if (key == "destination_map_name") { - if (!editor->project->mapNames->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->mapNames); - combo->setToolTip("The destination map name of the warp."); - } else if (key == "destination_warp") { - combo->setToolTip("The warp id on the destination map."); - } else if (key == "item") { - if (!editor->project->itemNames->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->itemNames); - } else if (key == "flag" || key == "event_flag") { - if (!editor->project->flagNames->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->flagNames); - if (key == "flag") - combo->setToolTip("The flag which is set when the hidden item is picked up."); - else if (key == "event_flag") - combo->setToolTip("The flag which hides the object when set."); - } else if (key == "script_var") { - if (!editor->project->varNames->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->varNames); - combo->setToolTip("The variable by which the script is triggered. The script is triggered when this variable's value matches 'Var Value'."); - } else if (key == "script_var_value") { - combo->setToolTip("The variable's value which triggers the script."); - } else if (key == "movement_type") { - if (!editor->project->movementTypes->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->movementTypes); - combo->setToolTip("The object's natural movement behavior when the player is not interacting with it."); - } else if (key == "weather") { - if (!editor->project->coordEventWeatherNames->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->coordEventWeatherNames); - combo->setToolTip("The weather that starts when the player steps on this spot."); - } else if (key == "secret_base_id") { - if (!editor->project->secretBaseIds->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->secretBaseIds); - combo->setToolTip("The secret base id which is inside this secret base entrance. Secret base ids are meant to be unique to each and every secret base entrance."); - } else if (key == "player_facing_direction") { - if (!editor->project->bgEventFacingDirections->contains(value)) { - combo->addItem(value); - } - combo->addItems(*editor->project->bgEventFacingDirections); - combo->setToolTip("The direction which the player must be facing to be able to interact with this event."); - } else if (key == "radius_x") { - combo->setToolTip("The maximum number of metatiles this object is allowed to move left or right during its normal movement behavior actions."); - } else if (key == "radius_y") { - combo->setToolTip("The maximum number of metatiles this object is allowed to move up or down during its normal movement behavior actions."); - } else if (key == "script_label") { - combo->setToolTip("The script which is executed with this event."); - } else if (key == "sight_radius_tree_id") { - combo->setToolTip("The maximum sight range of a trainer, OR the unique id of the berry tree."); - } else { - combo->addItem(value); - } - combo->setCurrentText(value); - - fl->addRow(new QLabel(field_labels[key], widget), combo); - widget->setLayout(fl); - frame->layout()->addWidget(widget); - - item->bind(combo, key); - } - - frames.append(frame); - - } - - //int scroll = ui->scrollArea_4->verticalScrollBar()->value(); - - QWidget *target = ui->scrollAreaWidgetContents; - - if (target->children().length()) { - qDeleteAll(target->children()); - } - - QVBoxLayout *layout = new QVBoxLayout(target); - target->setLayout(layout); - ui->scrollArea_4->setWidgetResizable(true); - ui->scrollArea_4->setWidget(target); - - for (ObjectPropertiesFrame *frame : frames) { - layout->addWidget(frame); - } - - layout->addStretch(1); - - // doesn't work - //QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - //ui->scrollArea_4->ensureVisible(0, scroll); -} - -void MainWindow::on_toolButton_deleteObject_clicked() -{ - if (editor && editor->selected_events) { - if (editor->selected_events->length()) { - for (DraggablePixmapItem *item : *editor->selected_events) { - if (item->event->get("event_type") != EventType::HealLocation) { - editor->deleteEvent(item->event); - if (editor->scene->items().contains(item)) { - editor->scene->removeItem(item); - } - editor->selected_events->removeOne(item); - } - else { // don't allow deletion of heal locations - qDebug() << "Cannot delete event of type " << item->event->get("event_type"); - } - } - updateSelectedObjects(); - } - } -} - -void MainWindow::on_toolButton_Open_Scripts_clicked() -{ - openInTextEditor(); -} - -void MainWindow::on_toolButton_Paint_clicked() -{ - editor->map_edit_mode = "paint"; - editor->cursor = QCursor(QPixmap(":/icons/pencil_cursor.ico"), 10, 10); - - ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - QScroller::ungrabGesture(ui->scrollArea); - - checkToolButtons(); -} - -void MainWindow::on_toolButton_Select_clicked() -{ - editor->map_edit_mode = "select"; - editor->cursor = QCursor(QPixmap(":/icons/cursor.ico"), 0, 0); - - ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - QScroller::ungrabGesture(ui->scrollArea); - - checkToolButtons(); -} - -void MainWindow::on_toolButton_Fill_clicked() -{ - editor->map_edit_mode = "fill"; - editor->cursor = QCursor(QPixmap(":/icons/fill_color_cursor.ico"), 10, 10); - - ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - QScroller::ungrabGesture(ui->scrollArea); - - checkToolButtons(); -} - -void MainWindow::on_toolButton_Dropper_clicked() -{ - editor->map_edit_mode = "pick"; - editor->cursor = QCursor(QPixmap(":/icons/pipette_cursor.ico"), 10, 10); - - ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - QScroller::ungrabGesture(ui->scrollArea); - - checkToolButtons(); -} - -void MainWindow::on_toolButton_Move_clicked() -{ - editor->map_edit_mode = "move"; - editor->cursor = QCursor(QPixmap(":/icons/move.ico"), 7, 7); - - ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - QScroller::grabGesture(ui->scrollArea, QScroller::LeftMouseButtonGesture); - - checkToolButtons(); -} - -void MainWindow::on_toolButton_Shift_clicked() -{ - editor->map_edit_mode = "shift"; - editor->cursor = QCursor(QPixmap(":/icons/shift_cursor.ico"), 10, 10); - - ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - QScroller::ungrabGesture(ui->scrollArea); - - checkToolButtons(); -} - -void MainWindow::checkToolButtons() { - ui->toolButton_Paint->setChecked(editor->map_edit_mode == "paint"); - ui->toolButton_Select->setChecked(editor->map_edit_mode == "select"); - ui->toolButton_Fill->setChecked(editor->map_edit_mode == "fill"); - ui->toolButton_Dropper->setChecked(editor->map_edit_mode == "pick"); - ui->toolButton_Move->setChecked(editor->map_edit_mode == "move"); - ui->toolButton_Shift->setChecked(editor->map_edit_mode == "shift"); -} - -void MainWindow::onLoadMapRequested(QString mapName, QString fromMapName) { - setMap(mapName); - editor->setSelectedConnectionFromMap(fromMapName); -} - -void MainWindow::onMapChanged(Map *map) { - map->layout->has_unsaved_changes = true; - updateMapList(); -} - -void MainWindow::onMapNeedsRedrawing() { - redrawMapScene(); -} - -void MainWindow::on_action_Export_Map_Image_triggered() -{ - QString defaultFilepath = QString("%1/%2.png").arg(editor->project->root).arg(editor->map->name); - QString filepath = QFileDialog::getSaveFileName(this, "Export Map Image", defaultFilepath, "Image Files (*.png *.jpg *.bmp)"); - if (!filepath.isEmpty()) { - editor->map_item->pixmap().save(filepath); - } -} - -void MainWindow::on_comboBox_ConnectionDirection_currentIndexChanged(const QString &direction) -{ - editor->updateCurrentConnectionDirection(direction); -} - -void MainWindow::on_spinBox_ConnectionOffset_valueChanged(int offset) -{ - editor->updateConnectionOffset(offset); -} - -void MainWindow::on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName) -{ - editor->setConnectionMap(mapName); -} - -void MainWindow::on_pushButton_AddConnection_clicked() -{ - editor->addNewConnection(); -} - -void MainWindow::on_pushButton_RemoveConnection_clicked() -{ - editor->removeCurrentConnection(); -} - -void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName) -{ - editor->updateDiveMap(mapName); -} - -void MainWindow::on_comboBox_EmergeMap_currentTextChanged(const QString &mapName) -{ - editor->updateEmergeMap(mapName); -} - -void MainWindow::on_comboBox_PrimaryTileset_activated(const QString &tilesetLabel) -{ - editor->updatePrimaryTileset(tilesetLabel); -} - -void MainWindow::on_comboBox_SecondaryTileset_activated(const QString &tilesetLabel) -{ - editor->updateSecondaryTileset(tilesetLabel); -} - -void MainWindow::on_pushButton_clicked() -{ - QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - dialog.setWindowTitle("Change Map Dimensions"); - dialog.setWindowModality(Qt::NonModal); - - QFormLayout form(&dialog); - - QSpinBox *widthSpinBox = new QSpinBox(); - QSpinBox *heightSpinBox = new QSpinBox(); - widthSpinBox->setMinimum(1); - heightSpinBox->setMinimum(1); - // See below for explanation of maximum map dimensions - widthSpinBox->setMaximum(0x1E7); - heightSpinBox->setMaximum(0x1D1); - widthSpinBox->setValue(editor->map->getWidth()); - heightSpinBox->setValue(editor->map->getHeight()); - form.addRow(new QLabel("Width"), widthSpinBox); - form.addRow(new QLabel("Height"), heightSpinBox); - - QLabel *errorLabel = new QLabel(); - QPalette errorPalette; - errorPalette.setColor(QPalette::WindowText, Qt::red); - errorLabel->setPalette(errorPalette); - errorLabel->setVisible(false); - - QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); - form.addRow(&buttonBox); - connect(&buttonBox, &QDialogButtonBox::accepted, [&dialog, &widthSpinBox, &heightSpinBox, &errorLabel](){ - // Ensure width and height are an acceptable size. - // The maximum number of metatiles in a map is the following: - // max = (width + 15) * (height + 14) - // This limit can be found in fieldmap.c in pokeruby/pokeemerald. - int realWidth = widthSpinBox->value() + 15; - int realHeight = heightSpinBox->value() + 14; - int numMetatiles = realWidth * realHeight; - if (numMetatiles <= 0x2800) { - dialog.accept(); - } else { - QString errorText = QString("Error: The specified width and height are too large.\n" - "The maximum width and height is the following: (width + 15) * (height + 14) <= 10240\n" - "The specified width and height was: (%1 + 15) * (%2 + 14) = %3") - .arg(widthSpinBox->value()) - .arg(heightSpinBox->value()) - .arg(numMetatiles); - errorLabel->setText(errorText); - errorLabel->setVisible(true); - } - }); - connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); - - form.addRow(errorLabel); - - if (dialog.exec() == QDialog::Accepted) { - editor->map->setDimensions(widthSpinBox->value(), heightSpinBox->value()); - editor->map->commit(); - onMapNeedsRedrawing(); - } -} - -void MainWindow::on_checkBox_smartPaths_stateChanged(int selected) -{ - editor->map->smart_paths_enabled = selected == Qt::Checked; -} - -void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected) -{ - bool visible = selected != 0; - editor->toggleBorderVisibility(visible); -} +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "project.h" +#include "editor.h" +#include "eventpropertiesframe.h" +#include "ui_eventpropertiesframe.h" +#include "bordermetatilespixmapitem.h" +#include "currentselectedmetatilespixmapitem.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + QCoreApplication::setOrganizationName("pret"); + QCoreApplication::setApplicationName("porymap"); + QApplication::setApplicationDisplayName("porymap"); + QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-1.ico")); + + ui->setupUi(this); + this->initExtraSignals(); + this->initExtraShortcuts(); + this->initEditor(); + this->openRecentProject(); + + on_toolButton_Paint_clicked(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +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() { + connect(ui->newEventToolButton, SIGNAL(newEventAdded(QString)), this, SLOT(addNewEvent(QString))); +} + +void MainWindow::initEditor() { + this->editor = new Editor(ui); + connect(this->editor, SIGNAL(objectsChanged()), this, SLOT(updateSelectedObjects())); + connect(this->editor, SIGNAL(selectedObjectsChanged()), this, SLOT(updateSelectedObjects())); + connect(this->editor, SIGNAL(loadMapRequested(QString, QString)), this, SLOT(onLoadMapRequested(QString, QString))); + 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(); +} + +void MainWindow::loadUserSettings() { + QSettings settings; + + bool betterCursors = settings.contains("cursor_mode") && settings.value("cursor_mode") != "0"; + ui->actionBetter_Cursors->setChecked(betterCursors); + this->editor->settings->betterCursors = betterCursors; +} + +void MainWindow::openRecentProject() { + QSettings settings; + QString key = "recent_projects"; + if (settings.contains(key)) { + QString default_dir = settings.value(key).toStringList().last(); + if (!default_dir.isNull()) { + qDebug() << QString("default_dir: '%1'").arg(default_dir); + openProject(default_dir); + } + } +} + +void MainWindow::openProject(QString dir) { + if (dir.isNull()) { + return; + } + + this->statusBar()->showMessage(QString("Opening project %1").arg(dir)); + + bool already_open = ( + (editor && editor != nullptr) + && (editor->project && editor->project != nullptr) + && (editor->project->root == dir) + ); + if (!already_open) { + editor->project = new Project; + editor->project->root = dir; + setWindowTitle(editor->project->getProjectTitle()); + loadDataStructures(); + populateMapList(); + setMap(getDefaultMap(), true); + } else { + setWindowTitle(editor->project->getProjectTitle()); + loadDataStructures(); + populateMapList(); + } + + this->statusBar()->showMessage(QString("Opened project %1").arg(dir)); +} + +QString MainWindow::getDefaultMap() { + if (editor && editor->project) { + QList names = editor->project->groupedMapNames; + if (!names.isEmpty()) { + QSettings settings; + QString key = "project:" + editor->project->root; + if (settings.contains(key)) { + QMap qmap = settings.value(key).toMap(); + if (qmap.contains("recent_map")) { + QString map_name = qmap.value("recent_map").toString(); + for (int i = 0; i < names.length(); i++) { + if (names.value(i).contains(map_name)) { + return map_name; + } + } + } + } + // Failing that, just get the first map in the list. + for (int i = 0; i < names.length(); i++) { + QStringList list = names.value(i); + if (list.length()) { + return list.value(0); + } + } + } + } + return QString(); +} + +QString MainWindow::getExistingDirectory(QString dir) { + return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); +} + +void MainWindow::on_action_Open_Project_triggered() +{ + QSettings settings; + QString key = "recent_projects"; + QString recent = "."; + if (settings.contains(key)) { + recent = settings.value(key).toStringList().last(); + } + QString dir = getExistingDirectory(recent); + if (!dir.isEmpty()) { + QStringList recents; + if (settings.contains(key)) { + recents = settings.value(key).toStringList(); + } + recents.removeAll(dir); + recents.append(dir); + settings.setValue(key, recents); + + openProject(dir); + } +} + +void MainWindow::setMap(QString map_name, bool scrollTreeView) { + qDebug() << QString("setMap(%1)").arg(map_name); + if (map_name.isNull()) { + return; + } + editor->setMap(map_name); + redrawMapScene(); + displayMapProperties(); + + if (scrollTreeView) { + ui->mapList->setCurrentIndex(mapListIndexes.value(map_name)); + ui->mapList->scrollTo(ui->mapList->currentIndex(), QAbstractItemView::PositionAtCenter); + } + + setWindowTitle(map_name + " - " + editor->project->getProjectTitle()); + + connect(editor->map, SIGNAL(mapChanged(Map*)), this, SLOT(onMapChanged(Map *))); + connect(editor->map, SIGNAL(mapNeedsRedrawing()), this, SLOT(onMapNeedsRedrawing())); + + setRecentMap(map_name); + updateMapList(); + updateTilesetEditor(); +} + +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(width, height); + + ui->graphicsView_Objects_Map->setScene(editor->scene); + ui->graphicsView_Objects_Map->setSceneRect(editor->scene->sceneRect()); + 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(width, height); + + ui->graphicsView_Metatiles->setScene(editor->scene_metatiles); + //ui->graphicsView_Metatiles->setSceneRect(editor->scene_metatiles->sceneRect()); + ui->graphicsView_Metatiles->setFixedSize(editor->metatile_selector_item->pixmap().width() + 2, editor->metatile_selector_item->pixmap().height() + 2); + + ui->graphicsView_BorderMetatile->setScene(editor->scene_selected_border_metatiles); + ui->graphicsView_BorderMetatile->setFixedSize(editor->selected_border_metatiles_item->pixmap().width() + 2, editor->selected_border_metatiles_item->pixmap().height() + 2); + + ui->graphicsView_currentMetatileSelection->setScene(editor->scene_current_metatile_selection); + ui->graphicsView_currentMetatileSelection->setFixedSize(editor->scene_current_metatile_selection_item->pixmap().width() + 2, editor->scene_current_metatile_selection_item->pixmap().height() + 2); + + ui->graphicsView_Collision->setScene(editor->scene_collision_metatiles); + //ui->graphicsView_Collision->setSceneRect(editor->scene_collision_metatiles->sceneRect()); + ui->graphicsView_Collision->setFixedSize(editor->movement_permissions_selector_item->pixmap().width() + 2, editor->movement_permissions_selector_item->pixmap().height() + 2); +} + +void MainWindow::openWarpMap(QString map_name, QString warp_num) { + // Ensure valid destination map name. + if (!editor->project->mapNames->contains(map_name)) { + qDebug() << QString("Invalid warp destination map name '%1'").arg(map_name); + return; + } + + // Ensure valid destination warp number. + bool ok; + int warpNum = warp_num.toInt(&ok, 0); + if (!ok) { + qDebug() << QString("Invalid warp number '%1' for destination map '%2'").arg(warp_num).arg(map_name); + return; + } + + // Open the destination map, and select the target warp event. + setMap(map_name, true); + QList warp_events = editor->map->events["warp_event_group"]; + if (warp_events.length() > warpNum) { + Event *warp_event = warp_events.at(warpNum); + QList *all_events = editor->getObjects(); + for (DraggablePixmapItem *item : *all_events) { + if (item->event == warp_event) { + editor->selected_events->clear(); + editor->selected_events->append(item); + editor->updateSelectedEvents(); + } + } + + delete all_events; + } +} + +void MainWindow::setRecentMap(QString map_name) { + QSettings settings; + QString key = "project:" + editor->project->root; + QMap qmap; + if (settings.contains(key)) { + qmap = settings.value(key).toMap(); + } + qmap.insert("recent_map", map_name); + settings.setValue(key, qmap); +} + +void MainWindow::displayMapProperties() { + ui->comboBox_Song->clear(); + ui->comboBox_Location->clear(); + ui->checkBox_Visibility->setChecked(false); + ui->comboBox_Weather->clear(); + ui->comboBox_Type->clear(); + ui->comboBox_BattleScene->clear(); + ui->comboBox_PrimaryTileset->clear(); + ui->comboBox_SecondaryTileset->clear(); + ui->checkBox_ShowLocation->setChecked(false); + if (!editor || !editor->map || !editor->project) { + ui->frame_3->setEnabled(false); + return; + } + ui->frame_3->setEnabled(true); + Map *map = editor->map; + Project *project = editor->project; + + QStringList songs = project->getSongNames(); + ui->comboBox_Song->addItems(songs); + ui->comboBox_Song->setCurrentText(map->song); + + ui->comboBox_Location->addItems(*project->regionMapSections); + ui->comboBox_Location->setCurrentText(map->location); + + QMap tilesets = project->getTilesets(); + ui->comboBox_PrimaryTileset->addItems(tilesets.value("primary")); + ui->comboBox_PrimaryTileset->setCurrentText(map->layout->tileset_primary_label); + ui->comboBox_SecondaryTileset->addItems(tilesets.value("secondary")); + ui->comboBox_SecondaryTileset->setCurrentText(map->layout->tileset_secondary_label); + + ui->checkBox_Visibility->setChecked(map->requiresFlash.toInt() > 0 || map->requiresFlash == "TRUE"); + + ui->comboBox_Weather->addItems(*project->weatherNames); + ui->comboBox_Weather->setCurrentText(map->weather); + + ui->comboBox_Type->addItems(*project->mapTypes); + ui->comboBox_Type->setCurrentText(map->type); + + ui->comboBox_BattleScene->addItems(*project->mapBattleScenes); + ui->comboBox_BattleScene->setCurrentText(map->battle_scene); + + ui->checkBox_ShowLocation->setChecked(map->show_location.toInt() > 0 || map->show_location == "TRUE"); +} + +void MainWindow::on_comboBox_Song_activated(const QString &song) +{ + if (editor && editor->map) { + editor->map->song = song; + } +} + +void MainWindow::on_comboBox_Location_activated(const QString &location) +{ + if (editor && editor->map) { + editor->map->location = location; + } +} + +void MainWindow::on_comboBox_Visibility_activated(const QString &requiresFlash) +{ + if (editor && editor->map) { + editor->map->requiresFlash = requiresFlash; + } +} + +void MainWindow::on_comboBox_Weather_activated(const QString &weather) +{ + if (editor && editor->map) { + editor->map->weather = weather; + } +} + +void MainWindow::on_comboBox_Type_activated(const QString &type) +{ + if (editor && editor->map) { + editor->map->type = type; + } +} + +void MainWindow::on_comboBox_BattleScene_activated(const QString &battle_scene) +{ + if (editor && editor->map) { + editor->map->battle_scene = battle_scene; + } +} + +void MainWindow::on_checkBox_Visibility_clicked(bool checked) +{ + if (editor && editor->map) { + if (checked) { + editor->map->requiresFlash = "TRUE"; + } else { + editor->map->requiresFlash = "FALSE"; + } + } +} + +void MainWindow::on_checkBox_ShowLocation_clicked(bool checked) +{ + if (editor && editor->map) { + if (checked) { + editor->map->show_location = "TRUE"; + } else { + editor->map->show_location = "FALSE"; + } + } +} + +void MainWindow::loadDataStructures() { + Project *project = editor->project; + project->readMapLayoutsTable(); + project->readAllMapLayouts(); + project->readRegionMapSections(); + project->readItemNames(); + project->readFlagNames(); + project->readVarNames(); + project->readMovementTypes(); + project->readMapTypes(); + project->readMapBattleScenes(); + project->readWeatherNames(); + project->readCoordEventWeatherNames(); + project->readSecretBaseIds(); + project->readBgEventFacingDirections(); + project->readMapsWithConnections(); + project->readMetatileBehaviors(); + project->readTilesetProperties(); +} + +void MainWindow::populateMapList() { + Project *project = editor->project; + + QIcon mapFolderIcon; + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_closed_map.ico"), QSize(), QIcon::Normal, QIcon::Off); + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_map.ico"), QSize(), QIcon::Normal, QIcon::On); + + QIcon folderIcon; + folderIcon.addFile(QStringLiteral(":/icons/folder_closed.ico"), QSize(), QIcon::Normal, QIcon::Off); + + mapIcon = new QIcon; + mapIcon->addFile(QStringLiteral(":/icons/map.ico"), QSize(), QIcon::Normal, QIcon::Off); + mapIcon->addFile(QStringLiteral(":/icons/image.ico"), QSize(), QIcon::Normal, QIcon::On); + + mapListModel = new QStandardItemModel; + mapGroupsModel = new QList; + + QStandardItem *entry = new QStandardItem; + entry->setText(project->getProjectTitle()); + entry->setIcon(folderIcon); + entry->setEditable(false); + mapListModel->appendRow(entry); + + QStandardItem *maps = new QStandardItem; + maps->setText("maps"); + maps->setIcon(folderIcon); + maps->setEditable(false); + entry->appendRow(maps); + + project->readMapGroups(); + for (int i = 0; i < project->groupNames->length(); i++) { + QString group_name = project->groupNames->value(i); + QStandardItem *group = new QStandardItem; + group->setText(group_name); + group->setIcon(mapFolderIcon); + group->setEditable(false); + group->setData(group_name, Qt::UserRole); + group->setData("map_group", MapListUserRoles::TypeRole); + group->setData(i, MapListUserRoles::GroupRole); + maps->appendRow(group); + mapGroupsModel->append(group); + QStringList names = project->groupedMapNames.value(i); + for (int j = 0; j < names.length(); j++) { + QString map_name = names.value(j); + QStandardItem *map = createMapItem(map_name, i, j); + group->appendRow(map); + mapListIndexes.insert(map_name, map->index()); + } + } + + // Right-clicking on items in the map list tree view brings up a context menu. + ui->mapList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->mapList, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(onOpenMapListContextMenu(const QPoint &))); + + ui->mapList->setModel(mapListModel); + ui->mapList->setUpdatesEnabled(true); + ui->mapList->expandToDepth(2); + ui->mapList->repaint(); +} + +QStandardItem* MainWindow::createMapItem(QString mapName, int groupNum, int inGroupNum) { + QStandardItem *map = new QStandardItem; + map->setText(QString("[%1.%2] ").arg(groupNum).arg(inGroupNum, 2, 10, QLatin1Char('0')) + mapName); + map->setIcon(*mapIcon); + map->setEditable(false); + map->setData(mapName, Qt::UserRole); + map->setData("map_name", MapListUserRoles::TypeRole); + return map; +} + +void MainWindow::onOpenMapListContextMenu(const QPoint &point) +{ + QModelIndex index = ui->mapList->indexAt(point); + if (!index.isValid()) { + return; + } + + QStandardItem *selectedItem = mapListModel->itemFromIndex(index); + QVariant itemType = selectedItem->data(MapListUserRoles::TypeRole); + if (!itemType.isValid()) { + return; + } + + // Build custom context menu depending on which type of item was selected (map group, map name, etc.) + if (itemType == "map_group") { + QString groupName = selectedItem->data(Qt::UserRole).toString(); + int groupNum = selectedItem->data(MapListUserRoles::GroupRole).toInt(); + QMenu* menu = new QMenu(); + QActionGroup* actions = new QActionGroup(menu); + actions->addAction(menu->addAction("Add New Map to Group"))->setData(groupNum); + connect(actions, SIGNAL(triggered(QAction*)), this, SLOT(onAddNewMapToGroupClick(QAction*))); + menu->exec(QCursor::pos()); + } +} + +void MainWindow::onAddNewMapToGroupClick(QAction* triggeredAction) +{ + int groupNum = triggeredAction->data().toInt(); + QStandardItem* groupItem = mapGroupsModel->at(groupNum); + + QString newMapName = editor->project->getNewMapName(); + Map* newMap = editor->project->addNewMapToGroup(newMapName, groupNum); + editor->project->saveMap(newMap); + editor->project->saveAllDataStructures(); + + int numMapsInGroup = groupItem->rowCount(); + QStandardItem *newMapItem = createMapItem(newMapName, groupNum, numMapsInGroup); + groupItem->appendRow(newMapItem); + mapListIndexes.insert(newMapName, newMapItem->index()); + + setMap(newMapName); +} + +void MainWindow::onTilesetChanged(QString mapName) +{ + setMap(mapName); +} + +void MainWindow::updateTilesetEditor() { + if (this->tilesetEditor) { + this->tilesetEditor->setTilesets(editor->ui->comboBox_PrimaryTileset->currentText(), editor->ui->comboBox_SecondaryTileset->currentText()); + } +} + +void MainWindow::currentMetatilesSelectionChanged() +{ + ui->graphicsView_currentMetatileSelection->setFixedSize(editor->scene_current_metatile_selection_item->pixmap().width() + 2, editor->scene_current_metatile_selection_item->pixmap().height() + 2); + ui->graphicsView_currentMetatileSelection->setSceneRect(0, 0, editor->scene_current_metatile_selection_item->pixmap().width(), editor->scene_current_metatile_selection_item->pixmap().height()); +} + +void MainWindow::on_mapList_activated(const QModelIndex &index) +{ + QVariant data = index.data(Qt::UserRole); + if (index.data(MapListUserRoles::TypeRole) == "map_name" && !data.isNull()) { + setMap(data.toString()); + } +} + +void MainWindow::markAllEdited(QAbstractItemModel *model) { + QList list; + list.append(QModelIndex()); + while (list.length()) { + QModelIndex parent = list.takeFirst(); + for (int i = 0; i < model->rowCount(parent); i++) { + QModelIndex index = model->index(i, 0, parent); + if (model->hasChildren(index)) { + list.append(index); + } + markEdited(index); + } + } +} + +void MainWindow::markEdited(QModelIndex index) { + QVariant data = index.data(Qt::UserRole); + if (!data.isNull()) { + QString map_name = data.toString(); + if (editor->project) { + if (editor->project->map_cache->contains(map_name)) { + // Just mark anything that's been opened for now. + // TODO if (project->getMap()->saved) + //ui->mapList->setExpanded(index, true); + ui->mapList->setExpanded(index, editor->project->map_cache->value(map_name)->hasUnsavedChanges()); + } + } + } +} + +void MainWindow::updateMapList() { + QAbstractItemModel *model = ui->mapList->model(); + markAllEdited(model); +} + +void MainWindow::on_action_Save_Project_triggered() +{ + editor->saveProject(); + updateMapList(); +} + +void MainWindow::undo() { + editor->undo(); +} + +void MainWindow::redo() { + editor->redo(); +} + +// Open current map scripts in system default editor for .inc files +void MainWindow::openInTextEditor() { + QString path = QDir::cleanPath("file://" + editor->project->root + QDir::separator() + "data/maps/" + editor->map->name + "/scripts.inc"); + QDesktopServices::openUrl(QUrl(path)); +} + +void MainWindow::on_action_Save_triggered() { + editor->save(); + updateMapList(); +} + +void MainWindow::on_tabWidget_2_currentChanged(int index) +{ + if (index == 0) { + editor->setEditingMap(); + } else if (index == 1) { + editor->setEditingCollision(); + } +} + +void MainWindow::on_action_Exit_triggered() +{ + QApplication::quit(); +} + +void MainWindow::on_tabWidget_currentChanged(int index) +{ + if (index == 0) { + on_tabWidget_2_currentChanged(ui->tabWidget_2->currentIndex()); + } else if (index == 1) { + editor->setEditingObjects(); + } else if (index == 3) { + editor->setEditingConnections(); + } +} + +void MainWindow::on_actionUndo_triggered() +{ + undo(); +} + +void MainWindow::on_actionRedo_triggered() +{ + redo(); +} + +void MainWindow::on_actionZoom_In_triggered() { + scaleMapView(1); +} + +void MainWindow::on_actionZoom_Out_triggered() { + scaleMapView(-1); +} + +void MainWindow::on_actionBetter_Cursors_triggered() { + QSettings settings; + settings.setValue("cursor_mode", QString::number(ui->actionBetter_Cursors->isChecked())); + this->editor->settings->betterCursors = ui->actionBetter_Cursors->isChecked(); +} + +void MainWindow::on_actionPencil_triggered() +{ + on_toolButton_Paint_clicked(); +} + +void MainWindow::on_actionPointer_triggered() +{ + on_toolButton_Select_clicked(); +} + +void MainWindow::on_actionFlood_Fill_triggered() +{ + on_toolButton_Fill_clicked(); +} + +void MainWindow::on_actionEyedropper_triggered() +{ + on_toolButton_Dropper_clicked(); +} + +void MainWindow::on_actionMove_triggered() +{ + on_toolButton_Move_clicked(); +} + +void MainWindow::on_actionMap_Shift_triggered() +{ + on_toolButton_Shift_clicked(); +} + +void MainWindow::scaleMapView(int s) { + if ((editor->scale_exp + s) <= 5 && (editor->scale_exp + s) >= -2) // sane limits + { + if (s == 0) + { + s = -editor->scale_exp; + } + + editor->scale_exp += s; + + double base = editor->scale_base; + double exp = editor->scale_exp; + double sfactor = pow(base,s); + + 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) +{ + if (editor) { + DraggablePixmapItem *object = editor->addNewEvent(event_type); + if (object) { + editor->selectMapEvent(object, false); + } + updateSelectedObjects(); + } +} + +// Should probably just pass layout and let the editor work it out +void MainWindow::updateSelectedObjects() { + QList *all_events = editor->getObjects(); + QList *events = nullptr; + + if (editor->selected_events && editor->selected_events->length()) { + events = editor->selected_events; + } else { + events = new QList; + if (all_events && all_events->length()) { + DraggablePixmapItem *selectedEvent = all_events->first(); + editor->selected_events->append(selectedEvent); + editor->redrawObject(selectedEvent); + events->append(selectedEvent); + } + } + + QMap event_obj_gfx_constants = editor->project->getEventObjGfxConstants(); + + QList frames; + + for (DraggablePixmapItem *item : *events) { + EventPropertiesFrame *frame = new EventPropertiesFrame; +// frame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + QSpinBox *x = frame->ui->spinBox_x; + QSpinBox *y = frame->ui->spinBox_y; + QSpinBox *z = frame->ui->spinBox_z; + + x->setValue(item->event->x()); + connect(x, SIGNAL(valueChanged(QString)), item, SLOT(set_x(QString))); + connect(item, SIGNAL(xChanged(int)), x, SLOT(setValue(int))); + + y->setValue(item->event->y()); + connect(y, SIGNAL(valueChanged(QString)), item, SLOT(set_y(QString))); + connect(item, SIGNAL(yChanged(int)), y, SLOT(setValue(int))); + + z->setValue(item->event->elevation()); + connect(z, SIGNAL(valueChanged(QString)), item, SLOT(set_elevation(QString))); + connect(item, SIGNAL(elevationChanged(int)), z, SLOT(setValue(int))); + + QFont font; + font.setCapitalization(QFont::Capitalize); + frame->ui->label_name->setFont(font); + QString event_type = item->event->get("event_type"); + QString event_group_type = item->event->get("event_group_type"); + QString map_name = item->event->get("map_name"); + int event_offs; + if (event_type == "event_warp") { event_offs = 0; } + else { event_offs = 1; } + frame->ui->label_name->setText( + QString("%1: %2 %3") + .arg(editor->project->getMap(map_name)->events.value(event_group_type).indexOf(item->event) + event_offs) + .arg(map_name) + .arg(event_type) + ); + + frame->ui->label_spritePixmap->setPixmap(item->event->pixmap); + connect(item, SIGNAL(spriteChanged(QPixmap)), frame->ui->label_spritePixmap, SLOT(setPixmap(QPixmap))); + + frame->ui->sprite->setVisible(false); + + QMap field_labels; + field_labels["script_label"] = "Script"; + field_labels["event_flag"] = "Event Flag"; + field_labels["movement_type"] = "Movement"; + field_labels["radius_x"] = "Movement Radius X"; + field_labels["radius_y"] = "Movement Radius Y"; + field_labels["is_trainer"] = "Trainer"; + field_labels["sight_radius_tree_id"] = "Sight Radius / Berry Tree ID"; + field_labels["destination_warp"] = "Destination Warp"; + field_labels["destination_map_name"] = "Destination Map"; + field_labels["script_var"] = "Var"; + field_labels["script_var_value"] = "Var Value"; + field_labels["player_facing_direction"] = "Player Facing Direction"; + field_labels["item"] = "Item"; + field_labels["item_unknown5"] = "Unknown 5"; + field_labels["item_unknown6"] = "Unknown 6"; + field_labels["weather"] = "Weather"; + field_labels["flag"] = "Flag"; + field_labels["secret_base_id"] = "Secret Base Id"; + + QStringList fields; + + if (event_type == EventType::Object) { + + frame->ui->sprite->setVisible(true); + frame->ui->comboBox_sprite->addItems(event_obj_gfx_constants.keys()); + frame->ui->comboBox_sprite->setCurrentText(item->event->get("sprite")); + connect(frame->ui->comboBox_sprite, SIGNAL(activated(QString)), item, SLOT(set_sprite(QString))); + + /* + frame->ui->script->setVisible(true); + frame->ui->comboBox_script->addItem(item->event->get("script_label")); + frame->ui->comboBox_script->setCurrentText(item->event->get("script_label")); + //item->bind(frame->ui->comboBox_script, "script_label"); + connect(frame->ui->comboBox_script, SIGNAL(activated(QString)), item, SLOT(set_script(QString))); + //connect(frame->ui->comboBox_script, static_cast(&QComboBox::activated), item, [item](QString script_label){ item->event->put("script_label", script_label); }); + //connect(item, SIGNAL(scriptChanged(QString)), frame->ui->comboBox_script, SLOT(setValue(QString))); + */ + + fields << "movement_type"; + fields << "radius_x"; + fields << "radius_y"; + fields << "script_label"; + fields << "event_flag"; + fields << "is_trainer"; + fields << "sight_radius_tree_id"; + } + else if (event_type == EventType::Warp) { + fields << "destination_map_name"; + fields << "destination_warp"; + } + else if (event_type == EventType::CoordScript) { + fields << "script_label"; + fields << "script_var"; + fields << "script_var_value"; + } + else if (event_type == EventType::CoordWeather) { + fields << "weather"; + } + else if (event_type == EventType::Sign) { + fields << "player_facing_direction"; + fields << "script_label"; + } + else if (event_type == EventType::HiddenItem) { + fields << "item"; + fields << "flag"; + } + else if (event_type == EventType::SecretBase) { + fields << "secret_base_id"; + } + + for (QString key : fields) { + QString value = item->event->get(key); + QWidget *widget = new QWidget(frame); + QFormLayout *fl = new QFormLayout(widget); + fl->setContentsMargins(9, 0, 9, 0); + + // is_trainer is the only non-combobox item. + if (key == "is_trainer") { + QCheckBox *checkbox = new QCheckBox(widget); + checkbox->setEnabled(true); + checkbox->setChecked(value.toInt() != 0 && value != "FALSE"); + checkbox->setToolTip("Whether or not this object is trainer."); + fl->addRow(new QLabel(field_labels[key], widget), checkbox); + widget->setLayout(fl); + frame->layout()->addWidget(widget); + connect(checkbox, &QCheckBox::stateChanged, [=](int state) { + QString isTrainer = state == Qt::Checked ? "TRUE" : "FALSE"; + item->event->put("is_trainer", isTrainer); + }); + continue; + } + + NoScrollComboBox *combo = new NoScrollComboBox(widget); + combo->setEditable(true); + + if (key == "destination_map_name") { + if (!editor->project->mapNames->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->mapNames); + combo->setToolTip("The destination map name of the warp."); + } else if (key == "destination_warp") { + combo->setToolTip("The warp id on the destination map."); + } else if (key == "item") { + if (!editor->project->itemNames->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->itemNames); + } else if (key == "flag" || key == "event_flag") { + if (!editor->project->flagNames->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->flagNames); + if (key == "flag") + combo->setToolTip("The flag which is set when the hidden item is picked up."); + else if (key == "event_flag") + combo->setToolTip("The flag which hides the object when set."); + } else if (key == "script_var") { + if (!editor->project->varNames->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->varNames); + combo->setToolTip("The variable by which the script is triggered. The script is triggered when this variable's value matches 'Var Value'."); + } else if (key == "script_var_value") { + combo->setToolTip("The variable's value which triggers the script."); + } else if (key == "movement_type") { + if (!editor->project->movementTypes->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->movementTypes); + combo->setToolTip("The object's natural movement behavior when the player is not interacting with it."); + } else if (key == "weather") { + if (!editor->project->coordEventWeatherNames->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->coordEventWeatherNames); + combo->setToolTip("The weather that starts when the player steps on this spot."); + } else if (key == "secret_base_id") { + if (!editor->project->secretBaseIds->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->secretBaseIds); + combo->setToolTip("The secret base id which is inside this secret base entrance. Secret base ids are meant to be unique to each and every secret base entrance."); + } else if (key == "player_facing_direction") { + if (!editor->project->bgEventFacingDirections->contains(value)) { + combo->addItem(value); + } + combo->addItems(*editor->project->bgEventFacingDirections); + combo->setToolTip("The direction which the player must be facing to be able to interact with this event."); + } else if (key == "radius_x") { + combo->setToolTip("The maximum number of metatiles this object is allowed to move left or right during its normal movement behavior actions."); + } else if (key == "radius_y") { + combo->setToolTip("The maximum number of metatiles this object is allowed to move up or down during its normal movement behavior actions."); + } else if (key == "script_label") { + combo->setToolTip("The script which is executed with this event."); + } else if (key == "sight_radius_tree_id") { + combo->setToolTip("The maximum sight range of a trainer, OR the unique id of the berry tree."); + } else { + combo->addItem(value); + } + combo->setCurrentText(value); + + fl->addRow(new QLabel(field_labels[key], widget), combo); + widget->setLayout(fl); + frame->layout()->addWidget(widget); + + item->bind(combo, key); + } + + frames.append(frame); + + } + + //int scroll = ui->scrollArea_4->verticalScrollBar()->value(); + + QWidget *target = ui->scrollAreaWidgetContents; + + if (target->children().length()) { + qDeleteAll(target->children()); + } + + QVBoxLayout *layout = new QVBoxLayout(target); + target->setLayout(layout); + ui->scrollArea_4->setWidgetResizable(true); + ui->scrollArea_4->setWidget(target); + + for (EventPropertiesFrame *frame : frames) { + layout->addWidget(frame); + } + + layout->addStretch(1); + + // doesn't work + //QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + //ui->scrollArea_4->ensureVisible(0, scroll); +} + +void MainWindow::on_toolButton_deleteObject_clicked() +{ + if (editor && editor->selected_events) { + if (editor->selected_events->length()) { + for (DraggablePixmapItem *item : *editor->selected_events) { + if (item->event->get("event_type") != EventType::HealLocation) { + editor->deleteEvent(item->event); + if (editor->scene->items().contains(item)) { + editor->scene->removeItem(item); + } + editor->selected_events->removeOne(item); + } + else { // don't allow deletion of heal locations + qDebug() << "Cannot delete event of type " << item->event->get("event_type"); + } + } + updateSelectedObjects(); + } + } +} + +void MainWindow::on_toolButton_Open_Scripts_clicked() +{ + openInTextEditor(); +} + +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); + + checkToolButtons(); +} + +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); + + checkToolButtons(); +} + +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); + + checkToolButtons(); +} + +void MainWindow::on_toolButton_Dropper_clicked() +{ + editor->map_edit_mode = "pick"; + editor->settings->mapCursor = QCursor(QPixmap(":/icons/pipette_cursor.ico"), 10, 10); + + ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + QScroller::ungrabGesture(ui->scrollArea); + + checkToolButtons(); +} + +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(); +} + +void MainWindow::on_toolButton_Shift_clicked() +{ + editor->map_edit_mode = "shift"; + editor->settings->mapCursor = QCursor(QPixmap(":/icons/shift_cursor.ico"), 10, 10); + + ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + QScroller::ungrabGesture(ui->scrollArea); + + checkToolButtons(); +} + +void MainWindow::checkToolButtons() { + ui->toolButton_Paint->setChecked(editor->map_edit_mode == "paint"); + ui->toolButton_Select->setChecked(editor->map_edit_mode == "select"); + ui->toolButton_Fill->setChecked(editor->map_edit_mode == "fill"); + ui->toolButton_Dropper->setChecked(editor->map_edit_mode == "pick"); + ui->toolButton_Move->setChecked(editor->map_edit_mode == "move"); + ui->toolButton_Shift->setChecked(editor->map_edit_mode == "shift"); +} + +void MainWindow::onLoadMapRequested(QString mapName, QString fromMapName) { + setMap(mapName, true); + editor->setSelectedConnectionFromMap(fromMapName); +} + +void MainWindow::onMapChanged(Map *map) { + map->layout->has_unsaved_changes = true; + updateMapList(); +} + +void MainWindow::onMapNeedsRedrawing() { + redrawMapScene(); +} + +void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryTilesetLabel) { + this->editor->updatePrimaryTileset(primaryTilesetLabel, true); + this->editor->updateSecondaryTileset(secondaryTilesetLabel, true); +} + +void MainWindow::on_action_Export_Map_Image_triggered() +{ + QString defaultFilepath = QString("%1/%2.png").arg(editor->project->root).arg(editor->map->name); + QString filepath = QFileDialog::getSaveFileName(this, "Export Map Image", defaultFilepath, "Image Files (*.png *.jpg *.bmp)"); + if (!filepath.isEmpty()) { + editor->map_item->pixmap().save(filepath); + } +} + +void MainWindow::on_comboBox_ConnectionDirection_currentIndexChanged(const QString &direction) +{ + editor->updateCurrentConnectionDirection(direction); +} + +void MainWindow::on_spinBox_ConnectionOffset_valueChanged(int offset) +{ + editor->updateConnectionOffset(offset); +} + +void MainWindow::on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName) +{ + editor->setConnectionMap(mapName); +} + +void MainWindow::on_pushButton_AddConnection_clicked() +{ + editor->addNewConnection(); +} + +void MainWindow::on_pushButton_RemoveConnection_clicked() +{ + editor->removeCurrentConnection(); +} + +void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName) +{ + editor->updateDiveMap(mapName); +} + +void MainWindow::on_comboBox_EmergeMap_currentTextChanged(const QString &mapName) +{ + editor->updateEmergeMap(mapName); +} + +void MainWindow::on_comboBox_PrimaryTileset_activated(const QString &tilesetLabel) +{ + editor->updatePrimaryTileset(tilesetLabel); +} + +void MainWindow::on_comboBox_SecondaryTileset_activated(const QString &tilesetLabel) +{ + editor->updateSecondaryTileset(tilesetLabel); +} + +void MainWindow::on_pushButton_clicked() +{ + QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + dialog.setWindowTitle("Change Map Dimensions"); + dialog.setWindowModality(Qt::NonModal); + + QFormLayout form(&dialog); + + QSpinBox *widthSpinBox = new QSpinBox(); + QSpinBox *heightSpinBox = new QSpinBox(); + widthSpinBox->setMinimum(1); + heightSpinBox->setMinimum(1); + // See below for explanation of maximum map dimensions + widthSpinBox->setMaximum(0x1E7); + heightSpinBox->setMaximum(0x1D1); + widthSpinBox->setValue(editor->map->getWidth()); + heightSpinBox->setValue(editor->map->getHeight()); + form.addRow(new QLabel("Width"), widthSpinBox); + form.addRow(new QLabel("Height"), heightSpinBox); + + QLabel *errorLabel = new QLabel(); + QPalette errorPalette; + errorPalette.setColor(QPalette::WindowText, Qt::red); + errorLabel->setPalette(errorPalette); + errorLabel->setVisible(false); + + QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); + form.addRow(&buttonBox); + connect(&buttonBox, &QDialogButtonBox::accepted, [&dialog, &widthSpinBox, &heightSpinBox, &errorLabel](){ + // Ensure width and height are an acceptable size. + // The maximum number of metatiles in a map is the following: + // max = (width + 15) * (height + 14) + // This limit can be found in fieldmap.c in pokeruby/pokeemerald. + int realWidth = widthSpinBox->value() + 15; + int realHeight = heightSpinBox->value() + 14; + int numMetatiles = realWidth * realHeight; + if (numMetatiles <= 0x2800) { + dialog.accept(); + } else { + QString errorText = QString("Error: The specified width and height are too large.\n" + "The maximum width and height is the following: (width + 15) * (height + 14) <= 10240\n" + "The specified width and height was: (%1 + 15) * (%2 + 14) = %3") + .arg(widthSpinBox->value()) + .arg(heightSpinBox->value()) + .arg(numMetatiles); + errorLabel->setText(errorText); + errorLabel->setVisible(true); + } + }); + connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); + + form.addRow(errorLabel); + + if (dialog.exec() == QDialog::Accepted) { + editor->map->setDimensions(widthSpinBox->value(), heightSpinBox->value()); + editor->map->commit(); + onMapNeedsRedrawing(); + } +} + +void MainWindow::on_checkBox_smartPaths_stateChanged(int selected) +{ + editor->settings->smartPathsEnabled = selected == Qt::Checked; +} + +void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected) +{ + bool visible = selected != 0; + editor->toggleBorderVisibility(visible); +} + +void MainWindow::on_actionTileset_Editor_triggered() +{ + if (!this->tilesetEditor) { + this->tilesetEditor = new TilesetEditor(this->editor->project, this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label, this); + connect(this->tilesetEditor, SIGNAL(tilesetsSaved(QString, QString)), this, SLOT(onTilesetsSaved(QString, QString))); + connect(this->tilesetEditor, SIGNAL(closed()), this, SLOT(onTilesetEditorClosed())); + } + + if (!this->tilesetEditor->isVisible()) { + this->tilesetEditor->show(); + } else if (this->tilesetEditor->isMinimized()) { + this->tilesetEditor->showNormal(); + } else { + this->tilesetEditor->activateWindow(); + } +} + +void MainWindow::onTilesetEditorClosed() { + if (this->tilesetEditor) { + delete this->tilesetEditor; + this->tilesetEditor = nullptr; + } +} diff --git a/project.cpp b/src/project.cpp old mode 100755 new mode 100644 similarity index 88% rename from project.cpp rename to src/project.cpp index 9cce7bbb..f1168123 --- a/project.cpp +++ b/src/project.cpp @@ -1,1763 +1,1873 @@ -#include "parseutil.h" -#include "project.h" -#include "tile.h" -#include "tileset.h" -#include "event.h" - -#include -#include -#include -#include -#include -#include -#include - -int Project::num_tiles_primary = 512; -int Project::num_tiles_total = 1024; -int Project::num_metatiles_primary = 512; -int Project::num_metatiles_total = 1024; -int Project::num_pals_primary = 6; -int Project::num_pals_total = 13; - -Project::Project() -{ - groupNames = new QStringList; - map_groups = new QMap; - mapNames = new QStringList; - regionMapSections = new QStringList; - itemNames = new QStringList; - flagNames = new QStringList; - varNames = new QStringList; - movementTypes = new QStringList; - mapTypes = new QStringList; - mapBattleScenes = new QStringList; - weatherNames = new QStringList; - coordEventWeatherNames = new QStringList; - secretBaseIds = new QStringList; - bgEventFacingDirections = new QStringList; - map_cache = new QMap; - mapConstantsToMapNames = new QMap; - mapNamesToMapConstants = new QMap; - tileset_cache = new QMap; -} - -QString Project::getProjectTitle() { - if (!root.isNull()) { - return root.section('/', -1); - } else { - return QString(); - } -} - -Map* Project::loadMap(QString map_name) { - Map *map; - if (map_cache->contains(map_name)) { - map = map_cache->value(map_name); - // TODO: uncomment when undo/redo history is fully implemented for all actions. - if (true/*map->hasUnsavedChanges()*/) { - return map; - } - } else { - map = new Map; - map->setName(map_name); - } - - readMapHeader(map); - readMapLayout(map); - readMapEvents(map); - loadMapConnections(map); - map->commit(); - map->history.save(); - - map_cache->insert(map_name, map); - return map; -} - -void Project::loadMapConnections(Map *map) { - if (!map->isPersistedToFile) { - return; - } - - map->connections.clear(); - if (!map->connections_label.isNull()) { - QString path = root + QString("/data/maps/%1/connections.inc").arg(map->name); - QString text = readTextFile(path); - if (!text.isNull()) { - QList *commands = parseAsm(text); - QStringList *list = getLabelValues(commands, map->connections_label); - - //// Avoid using this value. It ought to be generated instead. - //int num_connections = list->value(0).toInt(nullptr, 0); - - QString connections_list_label = list->value(1); - QList *connections = getLabelMacros(commands, connections_list_label); - for (QStringList command : *connections) { - QString macro = command.value(0); - if (macro == "connection") { - Connection *connection = new Connection; - connection->direction = command.value(1); - connection->offset = command.value(2); - QString mapConstant = command.value(3); - if (mapConstantsToMapNames->contains(mapConstant)) { - connection->map_name = mapConstantsToMapNames->value(mapConstant); - map->connections.append(connection); - } else { - qDebug() << QString("Failed to find connected map for map constant '%1'").arg(mapConstant); - } - } - } - } - } -} - -void Project::setNewMapConnections(Map *map) { - map->connections.clear(); -} - -QList* Project::getLabelMacros(QList *list, QString label) { - bool in_label = false; - QList *new_list = new QList; - for (int i = 0; i < list->length(); i++) { - QStringList params = list->value(i); - QString macro = params.value(0); - if (macro == ".label") { - if (params.value(1) == label) { - in_label = true; - } else if (in_label) { - // If nothing has been read yet, assume the label - // we're looking for is in a stack of labels. - if (new_list->length() > 0) { - break; - } - } - } else if (in_label) { - new_list->append(params); - } - } - return new_list; -} - -// For if you don't care about filtering by macro, -// and just want all values associated with some label. -QStringList* Project::getLabelValues(QList *list, QString label) { - list = getLabelMacros(list, label); - QStringList *values = new QStringList; - for (int i = 0; i < list->length(); i++) { - QStringList params = list->value(i); - QString macro = params.value(0); - // Ignore .align - if (macro == ".align") - continue; - if (macro == ".ifdef") - continue; - if (macro == ".ifndef") - continue; - for (int j = 1; j < params.length(); j++) { - values->append(params.value(j)); - } - } - return values; -} - -void Project::readMapHeader(Map* map) { - if (!map->isPersistedToFile) { - return; - } - - QString label = map->name; - ParseUtil *parser = new ParseUtil; - - QString header_text = readTextFile(root + "/data/maps/" + label + "/header.inc"); - if (header_text.isNull()) { - return; - } - QStringList *header = getLabelValues(parser->parseAsm(header_text), label); - map->layout_label = header->value(0); - map->events_label = header->value(1); - map->scripts_label = header->value(2); - map->connections_label = header->value(3); - map->song = header->value(4); - map->layout_id = header->value(5); - map->location = header->value(6); - map->requiresFlash = header->value(7); - map->weather = header->value(8); - map->type = header->value(9); - map->unknown = header->value(10); - map->show_location = header->value(11); - map->battle_scene = header->value(12); -} - -void Project::setNewMapHeader(Map* map, int mapIndex) { - map->layout_label = QString("%1_Layout").arg(map->name); - map->events_label = QString("%1_MapEvents").arg(map->name);; - map->scripts_label = QString("%1_MapScripts").arg(map->name);; - map->connections_label = "0x0"; - map->song = "MUS_DAN02"; - map->layout_id = QString("%1").arg(mapIndex); - map->location = "MAPSEC_LITTLEROOT_TOWN"; - map->requiresFlash = "FALSE"; - map->weather = "WEATHER_SUNNY"; - map->type = "MAP_TYPE_TOWN"; - map->unknown = "0"; - map->show_location = "TRUE"; - map->battle_scene = "MAP_BATTLE_SCENE_NORMAL"; -} - -void Project::saveMapHeader(Map *map) { - QString label = map->name; - QString header_path = root + "/data/maps/" + label + "/header.inc"; - QString text = ""; - text += QString("%1::\n").arg(label); - text += QString("\t.4byte %1\n").arg(map->layout_label); - text += QString("\t.4byte %1\n").arg(map->events_label); - text += QString("\t.4byte %1\n").arg(map->scripts_label); - - if (map->connections.length() == 0) { - map->connections_label = "0x0"; - } else { - map->connections_label = QString("%1_MapConnections").arg(map->name); - } - text += QString("\t.4byte %1\n").arg(map->connections_label); - - text += QString("\t.2byte %1\n").arg(map->song); - text += QString("\t.2byte %1\n").arg(map->layout_id); - text += QString("\t.byte %1\n").arg(map->location); - text += QString("\t.byte %1\n").arg(map->requiresFlash); - text += QString("\t.byte %1\n").arg(map->weather); - text += QString("\t.byte %1\n").arg(map->type); - text += QString("\t.2byte %1\n").arg(map->unknown); - text += QString("\t.byte %1\n").arg(map->show_location); - text += QString("\t.byte %1\n").arg(map->battle_scene); - saveTextFile(header_path, text); -} - -void Project::saveMapConnections(Map *map) { - QString path = root + "/data/maps/" + map->name + "/connections.inc"; - if (map->connections.length() > 0) { - QString text = ""; - QString connectionsListLabel = QString("%1_MapConnectionsList").arg(map->name); - int numValidConnections = 0; - text += QString("%1::\n").arg(connectionsListLabel); - for (Connection* connection : map->connections) { - if (mapNamesToMapConstants->contains(connection->map_name)) { - text += QString("\tconnection %1, %2, %3\n") - .arg(connection->direction) - .arg(connection->offset) - .arg(mapNamesToMapConstants->value(connection->map_name)); - numValidConnections++; - } else { - qDebug() << QString("Failed to write map connection. %1 not a valid map name").arg(connection->map_name); - } - } - text += QString("\n"); - text += QString("%1::\n").arg(map->connections_label); - text += QString("\t.4byte %1\n").arg(numValidConnections); - text += QString("\t.4byte %1\n").arg(connectionsListLabel); - saveTextFile(path, text); - } else { - deleteFile(path); - } - - updateMapsWithConnections(map); -} - -void Project::updateMapsWithConnections(Map *map) { - if (map->connections.length() > 0) { - if (!mapsWithConnections.contains(map->name)) { - mapsWithConnections.append(map->name); - } - } else { - if (mapsWithConnections.contains(map->name)) { - mapsWithConnections.removeOne(map->name); - } - } -} - -void Project::readMapLayoutsTable() { - QString layoutsText = readTextFile(getMapLayoutsTableFilepath()); - QList* values = parseAsm(layoutsText); - bool inLayoutPointers = false; - for (int i = 0; i < values->length(); i++) { - QStringList params = values->value(i); - QString macro = params.value(0); - if (macro == ".label") { - if (inLayoutPointers) { - break; - } - if (params.value(1) == "gMapLayouts") { - inLayoutPointers = true; - } - } else if (macro == ".4byte" && inLayoutPointers) { - QString layoutName = params.value(1); - mapLayoutsTable.append(layoutName); - } - } - - // Deep copy - mapLayoutsTableMaster = mapLayoutsTable; - mapLayoutsTableMaster.detach(); -} - -void Project::saveMapLayoutsTable() { - QString text = ""; - text += QString("\t.align 2\n"); - text += QString("gMapLayouts::\n"); - for (QString layoutName : mapLayoutsTableMaster) { - text += QString("\t.4byte %1\n").arg(layoutName); - } - saveTextFile(getMapLayoutsTableFilepath(), text); -} - -QString Project::getMapLayoutsTableFilepath() { - return QString("%1/data/layouts_table.inc").arg(root); -} - -QStringList* Project::readLayoutValues(QString layoutLabel) { - ParseUtil *parser = new ParseUtil; - - QString layoutText = readTextFile(getMapLayoutFilepath(layoutLabel)); - if (layoutText.isNull()) { - return nullptr; - } - - QStringList *layoutValues = getLabelValues(parser->parseAsm(layoutText), layoutLabel); - QString borderLabel = layoutValues->value(2); - QString blockdataLabel = layoutValues->value(3); - QStringList *borderValues = getLabelValues(parser->parseAsm(layoutText), borderLabel); - QString borderPath = borderValues->value(0).replace("\"", ""); - layoutValues->append(borderPath); - QStringList *blockdataValues = getLabelValues(parser->parseAsm(layoutText), blockdataLabel); - QString blockdataPath = blockdataValues->value(0).replace("\"", ""); - layoutValues->append(blockdataPath); - - if (layoutValues->size() != 8) { - qDebug() << "Error: Unexpected number of properties in layout '" << layoutLabel << "'"; - return nullptr; - } - - return layoutValues; -} - -void Project::readMapLayout(Map* map) { - if (!map->isPersistedToFile) { - return; - } - - MapLayout *layout; - if (!mapLayouts.contains(map->layout_label)) { - QStringList *layoutValues = readLayoutValues(map->layout->label); - if (!layoutValues) { - return; - } - - layout = new MapLayout(); - mapLayouts.insert(map->layout_label, layout); - layout->name = MapLayout::getNameFromLabel(map->layout_label); - layout->label = map->layout_label; - layout->width = layoutValues->value(0); - layout->height = layoutValues->value(1); - layout->border_label = layoutValues->value(2); - layout->blockdata_label = layoutValues->value(3); - layout->tileset_primary_label = layoutValues->value(4); - layout->tileset_secondary_label = layoutValues->value(5); - layout->border_path = layoutValues->value(6); - layout->blockdata_path = layoutValues->value(7); - map->layout = layout; - } else { - map->layout = mapLayouts[map->layout_label]; - } - - loadMapTilesets(map); - loadBlockdata(map); - loadMapBorder(map); -} - -void Project::readAllMapLayouts() { - mapLayouts.clear(); - - for (int i = 0; i < mapLayoutsTable.size(); i++) { - QString layoutLabel = mapLayoutsTable[i]; - QStringList *layoutValues = readLayoutValues(layoutLabel); - if (!layoutValues) { - return; - } - - MapLayout *layout = new MapLayout(); - layout->name = MapLayout::getNameFromLabel(layoutLabel); - layout->label = layoutLabel; - layout->index = i; - layout->width = layoutValues->value(0); - layout->height = layoutValues->value(1); - layout->border_label = layoutValues->value(2); - layout->blockdata_label = layoutValues->value(3); - layout->tileset_primary_label = layoutValues->value(4); - layout->tileset_secondary_label = layoutValues->value(5); - layout->border_path = layoutValues->value(6); - layout->blockdata_path = layoutValues->value(7); - mapLayouts.insert(layoutLabel, layout); - } - - // Deep copy - mapLayoutsMaster = mapLayouts; - mapLayoutsMaster.detach(); -} - -void Project::saveAllMapLayouts() { - for (QString layoutName : mapLayoutsTableMaster) { - MapLayout *layout = mapLayouts.value(layoutName); - QString text = QString("%1::\n").arg(layout->border_label); - text += QString("\t.incbin \"%1\"\n").arg(layout->border_path); - text += QString("\n"); - text += QString("%1::\n").arg(layout->blockdata_label); - text += QString("\t.incbin \"%1\"\n").arg(layout->blockdata_path); - text += QString("\n"); - text += QString("\t.align 2\n"); - text += QString("%1::\n").arg(layoutName); - text += QString("\t.4byte %1\n").arg(layout->width); - text += QString("\t.4byte %1\n").arg(layout->height); - text += QString("\t.4byte %1\n").arg(layout->border_label); - text += QString("\t.4byte %1\n").arg(layout->blockdata_label); - text += QString("\t.4byte %1\n").arg(layout->tileset_primary_label); - text += QString("\t.4byte %1\n").arg(layout->tileset_secondary_label); - text += QString("\n"); - saveTextFile(getMapLayoutFilepath(layout->label), text); - } -} - -QString Project::getMapLayoutFilepath(QString layoutLabel) { - return QString("%1/data/layouts/%2/layout.inc").arg(root).arg(MapLayout::getNameFromLabel(layoutLabel)); -} - -void Project::setNewMapLayout(Map* map) { - MapLayout *layout = new MapLayout(); - layout->label = QString("%1_Layout").arg(map->name); - layout->name = MapLayout::getNameFromLabel(layout->label); - layout->width = "20"; - layout->height = "20"; - layout->border_label = QString("%1_MapBorder").arg(map->name); - layout->border_path = QString("data/layouts/%1/border.bin").arg(map->name); - layout->blockdata_label = QString("%1_MapBlockdata").arg(map->name); - layout->blockdata_path = QString("data/layouts/%1/map.bin").arg(map->name); - layout->tileset_primary_label = "gTileset_General"; - layout->tileset_secondary_label = "gTileset_Petalburg"; - map->layout = layout; - map->layout_label = layout->label; - - // Insert new entry into the global map layouts. - mapLayouts.insert(layout->label, layout); - mapLayoutsTable.append(layout->label); -} - -void Project::saveMapGroupsTable() { - QString text = ""; - int groupNum = 0; - for (QStringList mapNames : groupedMapNames) { - text += QString("\t.align 2\n"); - text += QString("gMapGroup%1::\n").arg(groupNum); - for (QString mapName : mapNames) { - text += QString("\t.4byte %1\n").arg(mapName); - } - text += QString("\n"); - groupNum++; - } - - text += QString("\t.align 2\n"); - text += QString("gMapGroups::\n"); - for (int i = 0; i < groupNum; i++) { - text += QString("\t.4byte gMapGroup%1\n").arg(i); - } - - saveTextFile(root + "/data/maps/groups.inc", text); -} - -void Project::saveMapConstantsHeader() { - QString text = QString("#ifndef GUARD_CONSTANTS_MAPS_H\n"); - text += QString("#define GUARD_CONSTANTS_MAPS_H\n"); - text += QString("\n"); - - int groupNum = 0; - for (QStringList mapNames : groupedMapNames) { - text += QString("// Map Group %1\n").arg(groupNum); - int maxLength = 0; - for (QString mapName : mapNames) { - QString mapConstantName = mapNamesToMapConstants->value(mapName); - if (mapConstantName.length() > maxLength) - maxLength = mapConstantName.length(); - } - int groupIndex = 0; - for (QString mapName : mapNames) { - QString mapConstantName = mapNamesToMapConstants->value(mapName); - text += QString("#define %1%2(%3 | (%4 << 8))\n") - .arg(mapConstantName) - .arg(QString(" ").repeated(maxLength - mapConstantName.length() + 1)) - .arg(groupIndex) - .arg(groupNum); - groupIndex++; - } - text += QString("\n"); - groupNum++; - } - - text += QString("\n"); - text += QString("#define MAP_NONE (0x7F | (0x7F << 8))\n"); - text += QString("#define MAP_UNDEFINED (0xFF | (0xFF << 8))\n\n\n"); - text += QString("#define MAP_GROUP(map) (MAP_##map >> 8)\n"); - text += QString("#define MAP_NUM(map) (MAP_##map & 0xFF)\n\n"); - text += QString("#endif // GUARD_CONSTANTS_MAPS_H\n"); - saveTextFile(root + "/include/constants/maps.h", text); -} - -// saves heal location coords in root + /src/data/heal_locations.h -// and indexes as defines in root + /include/constants/heal_locations.h -void Project::saveHealLocationStruct(Map *map) { - QString tab = QString(" "); - - QString data_text = QString("static const struct HealLocation sHealLocations[] =\n{\n"); - - QString constants_text = QString("#ifndef GUARD_CONSTANTS_HEAL_LOCATIONS_H\n"); - constants_text += QString("#define GUARD_CONSTANTS_HEAL_LOCATIONS_H\n\n"); - - QMap flyableMapsDupes; - QSet flyableMapsUnique; - - // set flyableMapsDupes and flyableMapsUnique - for (auto it = flyableMaps.begin(); it != flyableMaps.end(); it++) { - HealLocation loc = *it; - QString xname = loc.name; - if (flyableMapsUnique.contains(xname)) { - flyableMapsDupes[xname] = 1; - } - flyableMapsUnique.insert(xname); - } - - // set new location in flyableMapsList - if (map->events["heal_event_group"].length() > 0) { - for (Event *heal : map->events["heal_event_group"]) { - HealLocation hl = heal->buildHealLocation(); - flyableMaps[hl.index - 1] = hl; - } - } - - int i = 1; - - for (auto map_in : flyableMaps) { - data_text += QString(" {MAP_GROUP(%1), MAP_NUM(%1), %2, %3},\n") - .arg(map_in.name) - .arg(map_in.x) - .arg(map_in.y); - - QString ending = QString(""); - - // must add _1 / _2 for maps that have duplicates - if (flyableMapsDupes.keys().contains(map_in.name)) { - // map contains multiple heal locations - ending += QString("_%1").arg(flyableMapsDupes[map_in.name]); - flyableMapsDupes[map_in.name]++; - } - if (map_in.index != 0) { - constants_text += QString("#define HEAL_LOCATION_%1 %2\n") - .arg(map_in.name + ending) - .arg(map_in.index); - } - else { - constants_text += QString("#define HEAL_LOCATION_%1 %2\n") - .arg(map_in.name + ending) - .arg(i); - } - i++; - } - - data_text += QString("};\n"); - constants_text += QString("\n#endif // GUARD_CONSTANTS_HEAL_LOCATIONS_H\n"); - - saveTextFile(root + "/src/data/heal_locations.h", data_text); - saveTextFile(root + "/include/constants/heal_locations.h", constants_text); -} - -void Project::loadMapTilesets(Map* map) { - if (map->layout->has_unsaved_changes) { - return; - } - - map->layout->tileset_primary = getTileset(map->layout->tileset_primary_label); - map->layout->tileset_secondary = getTileset(map->layout->tileset_secondary_label); -} - -Tileset* Project::loadTileset(QString label) { - ParseUtil *parser = new ParseUtil; - - QString headers_text = readTextFile(root + "/data/tilesets/headers.inc"); - QStringList *values = getLabelValues(parser->parseAsm(headers_text), label); - Tileset *tileset = new Tileset; - tileset->name = label; - tileset->is_compressed = values->value(0); - tileset->is_secondary = values->value(1); - tileset->padding = values->value(2); - tileset->tiles_label = values->value(3); - tileset->palettes_label = values->value(4); - tileset->metatiles_label = values->value(5); - tileset->metatile_attrs_label = values->value(6); - tileset->callback_label = values->value(7); - - loadTilesetAssets(tileset); - - tileset_cache->insert(label, tileset); - return tileset; -} - -void Project::loadBlockdata(Map* map) { - if (!map->isPersistedToFile || map->layout->has_unsaved_changes) { - return; - } - - QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path); - map->layout->blockdata = readBlockdata(path); -} - -void Project::setNewMapBlockdata(Map* map) { - Blockdata *blockdata = new Blockdata; - for (int i = 0; i < map->getWidth() * map->getHeight(); i++) { - blockdata->addBlock(qint16(0x3001)); - } - map->layout->blockdata = blockdata; -} - -void Project::loadMapBorder(Map *map) { - if (!map->isPersistedToFile || map->layout->has_unsaved_changes) { - return; - } - - QString path = QString("%1/%2").arg(root).arg(map->layout->border_path); - map->layout->border = readBlockdata(path); -} - -void Project::setNewMapBorder(Map *map) { - Blockdata *blockdata = new Blockdata; - blockdata->addBlock(qint16(0x01D4)); - blockdata->addBlock(qint16(0x01D5)); - blockdata->addBlock(qint16(0x01DC)); - blockdata->addBlock(qint16(0x01DD)); - map->layout->border = blockdata; -} - -void Project::saveMapBorder(Map *map) { - QString path = QString("%1/%2").arg(root).arg(map->layout->border_path); - writeBlockdata(path, map->layout->border); -} - -void Project::saveBlockdata(Map* map) { - QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path); - writeBlockdata(path, map->layout->blockdata); - map->history.save(); -} - -void Project::writeBlockdata(QString path, Blockdata *blockdata) { - QFile file(path); - if (file.open(QIODevice::WriteOnly)) { - QByteArray data = blockdata->serialize(); - file.write(data); - } else { - qDebug() << "Failed to open blockdata file for writing: '" << path << "'"; - } -} - -void Project::saveAllMaps() { - QList keys = map_cache->keys(); - for (int i = 0; i < keys.length(); i++) { - QString key = keys.value(i); - Map* map = map_cache->value(key); - saveMap(map); - } -} - -void Project::saveMap(Map *map) { - // Create/Modify a few collateral files for brand new maps. - if (!map->isPersistedToFile) { - QString newMapDataDir = QString(root + "/data/maps/%1").arg(map->name); - if (!QDir::root().mkdir(newMapDataDir)) { - qDebug() << "Error: failed to create directory for new map. " << newMapDataDir; - } - - QString newLayoutDir = QString(root + "/data/layouts/%1").arg(map->name); - if (!QDir::root().mkdir(newLayoutDir)) { - qDebug() << "Error: failed to create directory for new layout. " << newLayoutDir; - } - - // TODO: In the future, these files needs more structure to allow for proper parsing/saving. - // Create file data/maps//scripts.inc - QString text = QString("%1_MapScripts::\n\t.byte 0\n").arg(map->name); - saveTextFile(root + "/data/maps/" + map->name + "/scripts.inc", text); - - // Create file data/maps//text.inc - saveTextFile(root + "/data/maps/" + map->name + "/text.inc", "\n"); - - // Simply append to data/event_scripts.s. - text = QString("\n\t.include \"data/maps/%1/scripts.inc\"\n").arg(map->name); - text += QString("\t.include \"data/maps/%1/text.inc\"\n").arg(map->name); - appendTextFile(root + "/data/event_scripts.s", text); - - // Simply append to data/map_events.s. - text = QString("\n\t.include \"data/maps/%1/events.inc\"\n").arg(map->name); - appendTextFile(root + "/data/map_events.s", text); - - // Simply append to data/maps/headers.inc. - text = QString("\t.include \"data/maps/%1/header.inc\"\n").arg(map->name); - appendTextFile(root + "/data/maps/headers.inc", text); - - // Simply append to data/layouts.inc. - text = QString("\t.include \"data/layouts/%1/layout.inc\"\n").arg(map->layout->name); - appendTextFile(root + "/data/layouts.inc", text); - } - - saveMapBorder(map); - saveMapHeader(map); - saveMapConnections(map); - saveBlockdata(map); - saveMapEvents(map); - - // Update global data structures with current map data. - updateMapLayout(map); - - map->isPersistedToFile = true; - map->layout->has_unsaved_changes = false; -} - -void Project::updateMapLayout(Map* map) { - if (!mapLayoutsTableMaster.contains(map->layout_label)) { - mapLayoutsTableMaster.append(map->layout_label); - } - - // Deep copy - MapLayout *layout = mapLayouts.value(map->layout_label); - MapLayout *newLayout = new MapLayout(); - *newLayout = *layout; - mapLayoutsMaster.insert(map->layout_label, newLayout); -} - -void Project::saveAllDataStructures() { - saveMapLayoutsTable(); - saveAllMapLayouts(); - saveMapGroupsTable(); - saveMapConstantsHeader(); - saveMapsWithConnections(); -} - -void Project::loadTilesetAssets(Tileset* tileset) { - ParseUtil* parser = new ParseUtil; - QString category = (tileset->is_secondary == "TRUE") ? "secondary" : "primary"; - if (tileset->name.isNull()) { - return; - } - QString dir_path = root + "/data/tilesets/" + category + "/" + tileset->name.replace("gTileset_", "").toLower(); - - QString graphics_text = readTextFile(root + "/data/tilesets/graphics.inc"); - QList *graphics = parser->parseAsm(graphics_text); - QStringList *tiles_values = getLabelValues(graphics, tileset->tiles_label); - QStringList *palettes_values = getLabelValues(graphics, tileset->palettes_label); - - QString tiles_path; - if (!tiles_values->isEmpty()) { - tiles_path = root + "/" + tiles_values->value(0).section('"', 1, 1); - } else { - tiles_path = dir_path + "/tiles.4bpp"; - if (tileset->is_compressed == "TRUE") { - tiles_path += ".lz"; - } - } - - QStringList *palette_paths = new QStringList; - if (!palettes_values->isEmpty()) { - for (int i = 0; i < palettes_values->length(); i++) { - QString value = palettes_values->value(i); - palette_paths->append(root + "/" + value.section('"', 1, 1)); - } - } else { - QString palettes_dir_path = dir_path + "/palettes"; - for (int i = 0; i < 16; i++) { - palette_paths->append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".gbapal"); - } - } - - QString metatiles_path; - QString metatile_attrs_path; - QString metatiles_text = readTextFile(root + "/data/tilesets/metatiles.inc"); - QList *metatiles_macros = parser->parseAsm(metatiles_text); - QStringList *metatiles_values = getLabelValues(metatiles_macros, tileset->metatiles_label); - if (!metatiles_values->isEmpty()) { - metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1); - } else { - metatiles_path = dir_path + "/metatiles.bin"; - } - QStringList *metatile_attrs_values = getLabelValues(metatiles_macros, tileset->metatile_attrs_label); - if (!metatile_attrs_values->isEmpty()) { - metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1); - } else { - metatile_attrs_path = dir_path + "/metatile_attributes.bin"; - } - - // tiles - tiles_path = fixGraphicPath(tiles_path); - QImage *image = new QImage(tiles_path); - //image->setColor(0, qRgb(0xff, 0, 0)); // debug - - QList *tiles = new QList; - int w = 8; - int h = 8; - for (int y = 0; y < image->height(); y += h) - for (int x = 0; x < image->width(); x += w) { - QImage tile = image->copy(x, y, w, h); - tiles->append(tile); - } - tileset->tiles = tiles; - - // metatiles - QFile metatiles_file(metatiles_path); - if (metatiles_file.open(QIODevice::ReadOnly)) { - QByteArray data = metatiles_file.readAll(); - int num_metatiles = data.length() / 16; - int num_layers = 2; - QList *metatiles = new QList; - for (int i = 0; i < num_metatiles; i++) { - Metatile *metatile = new Metatile; - int index = i * (2 * 4 * num_layers); - for (int j = 0; j < 4 * num_layers; j++) { - uint16_t word = data[index++] & 0xff; - word += (data[index++] & 0xff) << 8; - Tile tile; - tile.tile = word & 0x3ff; - tile.xflip = (word >> 10) & 1; - tile.yflip = (word >> 11) & 1; - tile.palette = (word >> 12) & 0xf; - metatile->tiles->append(tile); - } - metatiles->append(metatile); - } - tileset->metatiles = metatiles; - } else { - tileset->metatiles = new QList; - qDebug() << QString("Could not open '%1'").arg(metatiles_path); - } - - QFile attrs_file(metatile_attrs_path); - //qDebug() << metatile_attrs_path; - if (attrs_file.open(QIODevice::ReadOnly)) { - QByteArray data = attrs_file.readAll(); - int num_metatiles = tileset->metatiles->count(); - int num_metatileAttrs = data.length() / 2; - if (num_metatiles != num_metatileAttrs) { - qDebug() << QString("Metatile count %1 does not match metatile attribute count %2").arg(num_metatiles).arg(num_metatileAttrs); - if (num_metatiles > num_metatileAttrs) - num_metatiles = num_metatileAttrs; - } - for (int i = 0; i < num_metatiles; i++) { - uint16_t word = data[i*2] & 0xff; - word += (data[i*2 + 1] & 0xff) << 8; - tileset->metatiles->value(i)->attr = word; - } - } else { - qDebug() << QString("Could not open '%1'").arg(metatile_attrs_path); - } - - // palettes - QList> *palettes = new QList>; - for (int i = 0; i < palette_paths->length(); i++) { - QString path = palette_paths->value(i); - // the palettes are not compressed. this should never happen. it's only a precaution. - path = path.replace(QRegExp("\\.lz$"), ""); - // TODO default to .pal (JASC-PAL) - // just use .gbapal for now - QFile file(path); - QList palette; - if (file.open(QIODevice::ReadOnly)) { - QByteArray data = file.readAll(); - for (int j = 0; j < 16; j++) { - uint16_t word = data[j*2] & 0xff; - word += (data[j*2 + 1] & 0xff) << 8; - int red = word & 0x1f; - int green = (word >> 5) & 0x1f; - int blue = (word >> 10) & 0x1f; - QRgb color = qRgb(red * 8, green * 8, blue * 8); - palette.append(color); - } - } else { - for (int j = 0; j < 16; j++) { - palette.append(qRgb(j * 16, j * 16, j * 16)); - } - qDebug() << QString("Could not open palette path '%1'").arg(path); - } - - palettes->append(palette); - } - tileset->palettes = palettes; -} - -Blockdata* Project::readBlockdata(QString path) { - Blockdata *blockdata = new Blockdata; - QFile file(path); - if (file.open(QIODevice::ReadOnly)) { - QByteArray data = file.readAll(); - for (int i = 0; (i + 1) < data.length(); i += 2) { - uint16_t word = static_cast((data[i] & 0xff) + ((data[i + 1] & 0xff) << 8)); - blockdata->addBlock(word); - } - } else { - qDebug() << "Failed to open blockdata path '" << path << "'"; - } - - return blockdata; -} - -Map* Project::getMap(QString map_name) { - if (map_cache->contains(map_name)) { - return map_cache->value(map_name); - } else { - Map *map = loadMap(map_name); - return map; - } -} - -Tileset* Project::getTileset(QString label) { - if (tileset_cache->contains(label)) { - return tileset_cache->value(label); - } else { - Tileset *tileset = loadTileset(label); - return tileset; - } -} - -QString Project::readTextFile(QString path) { - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - //QMessageBox::information(0, "Error", QString("Could not open '%1': ").arg(path) + file.errorString()); - qDebug() << QString("Could not open '%1': ").arg(path) + file.errorString(); - return QString(); - } - QTextStream in(&file); - QString text = ""; - while (!in.atEnd()) { - text += in.readLine() + "\n"; - } - return text; -} - -void Project::saveTextFile(QString path, QString text) { - QFile file(path); - if (file.open(QIODevice::WriteOnly)) { - file.write(text.toUtf8()); - } else { - qDebug() << QString("Could not open '%1' for writing: ").arg(path) + file.errorString(); - } -} - -void Project::appendTextFile(QString path, QString text) { - QFile file(path); - if (file.open(QIODevice::Append)) { - file.write(text.toUtf8()); - } else { - qDebug() << QString("Could not open '%1' for appending: ").arg(path) + file.errorString(); - } -} - -void Project::deleteFile(QString path) { - QFile file(path); - if (file.exists() && !file.remove()) { - qDebug() << QString("Could not delete file '%1': ").arg(path) + file.errorString(); - } -} - -void Project::readMapGroups() { - QString text = readTextFile(root + "/data/maps/groups.inc"); - if (text.isNull()) { - return; - } - ParseUtil *parser = new ParseUtil; - QList *commands = parser->parseAsm(text); - - bool in_group_pointers = false; - QStringList *groups = new QStringList; - for (int i = 0; i < commands->length(); i++) { - QStringList params = commands->value(i); - QString macro = params.value(0); - if (macro == ".label") { - if (in_group_pointers) { - break; - } - if (params.value(1) == "gMapGroups") { - in_group_pointers = true; - } - } else if (macro == ".4byte") { - if (in_group_pointers) { - for (int j = 1; j < params.length(); j++) { - groups->append(params.value(j)); - } - } - } - } - - QList groupedMaps; - for (int i = 0; i < groups->length(); i++) { - groupedMaps.append(QStringList()); - } - - QStringList *maps = new QStringList; - int group = -1; - for (int i = 0; i < commands->length(); i++) { - QStringList params = commands->value(i); - QString macro = params.value(0); - if (macro == ".label") { - group = groups->indexOf(params.value(1)); - } else if (macro == ".4byte") { - if (group != -1) { - for (int j = 1; j < params.length(); j++) { - QString mapName = params.value(j); - groupedMaps[group].append(mapName); - maps->append(mapName); - map_groups->insert(mapName, group); - - // Build the mapping and reverse mapping between map constants and map names. - QString mapConstant = Map::mapConstantFromName(mapName); - mapConstantsToMapNames->insert(mapConstant, mapName); - mapNamesToMapConstants->insert(mapName, mapConstant); - } - } - } - } - - mapConstantsToMapNames->insert(NONE_MAP_CONSTANT, NONE_MAP_NAME); - mapNamesToMapConstants->insert(NONE_MAP_NAME, NONE_MAP_CONSTANT); - maps->append(NONE_MAP_NAME); - - groupNames = groups; - groupedMapNames = groupedMaps; - mapNames = maps; - - QString hltext = readTextFile(root + QString("/src/data/heal_locations.h")); - QList* hl = parser->parseHealLocs(hltext); - flyableMaps = *hl; - delete hl; -} - -Map* Project::addNewMapToGroup(QString mapName, int groupNum) { - // Setup new map in memory, but don't write to file until map is actually saved later. - mapNames->append(mapName); - map_groups->insert(mapName, groupNum); - groupedMapNames[groupNum].append(mapName); - - Map *map = new Map; - map->isPersistedToFile = false; - map->setName(mapName); - mapConstantsToMapNames->insert(map->constantName, map->name); - mapNamesToMapConstants->insert(map->name, map->constantName); - setNewMapHeader(map, mapLayoutsTable.size() + 1); - setNewMapLayout(map); - loadMapTilesets(map); - setNewMapBlockdata(map); - setNewMapBorder(map); - setNewMapEvents(map); - setNewMapConnections(map); - map->commit(); - map->history.save(); - map_cache->insert(mapName, map); - - return map; -} - -QString Project::getNewMapName() { - // Ensure default name doesn't already exist. - int i = 0; - QString newMapName; - do { - newMapName = QString("NewMap%1").arg(++i); - } while (mapNames->contains(newMapName)); - - return newMapName; -} - -QList* Project::parseAsm(QString text) { - ParseUtil *parser = new ParseUtil; - return parser->parseAsm(text); -} - -QStringList Project::getVisibilities() { - // TODO - QStringList names; - for (int i = 0; i < 16; i++) { - names.append(QString("%1").arg(i)); - } - return names; -} - -QMap Project::getTilesets() { - QMap allTilesets; - QStringList primaryTilesets; - QStringList secondaryTilesets; - allTilesets.insert("primary", primaryTilesets); - allTilesets.insert("secondary", secondaryTilesets); - QString headers_text = readTextFile(root + "/data/tilesets/headers.inc"); - QList* commands = parseAsm(headers_text); - int i = 0; - while (i < commands->length()) { - if (commands->at(i).length() != 2) - continue; - - if (commands->at(i).at(0) == ".label") { - QString tilesetLabel = commands->at(i).at(1); - // Advance to command specifying whether or not it is a secondary tileset - i += 2; - if (commands->at(i).at(0) != ".byte") { - qDebug() << "Unexpected command found for secondary tileset flag in tileset" << tilesetLabel << ". Expected '.byte', but found: " << commands->at(i).at(0); - continue; - } - - QString secondaryTilesetValue = commands->at(i).at(1); - if (secondaryTilesetValue != "TRUE" && secondaryTilesetValue != "FALSE" && secondaryTilesetValue != "0" && secondaryTilesetValue != "1") { - qDebug() << "Unexpected secondary tileset flag found. Expected \"TRUE\", \"FALSE\", \"0\", or \"1\", but found: " << secondaryTilesetValue; - continue; - } - - bool isSecondaryTileset = (secondaryTilesetValue == "TRUE" || secondaryTilesetValue == "1"); - if (isSecondaryTileset) - allTilesets["secondary"].append(tilesetLabel); - else - allTilesets["primary"].append(tilesetLabel); - } - - i++; - } - - return allTilesets; -} - -void Project::readTilesetProperties() { - QStringList defines; - QString text = readTextFile(root + "/include/fieldmap.h"); - if (!text.isNull()) { - bool error = false; - QStringList definePrefixes; - definePrefixes << "NUM_"; - QMap defines = readCDefines(text, definePrefixes); - auto it = defines.find("NUM_TILES_IN_PRIMARY"); - if (it != defines.end()) { - Project::num_tiles_primary = it.value(); - } - else { - error = true; - } - it = defines.find("NUM_TILES_TOTAL"); - if (it != defines.end()) { - Project::num_tiles_total = it.value(); - } - else { - error = true; - } - it = defines.find("NUM_METATILES_IN_PRIMARY"); - if (it != defines.end()) { - Project::num_metatiles_primary = it.value(); - } - else { - error = true; - } - it = defines.find("NUM_METATILES_TOTAL"); - if (it != defines.end()) { - Project::num_metatiles_total = it.value(); - } - else { - error = true; - } - it = defines.find("NUM_PALS_IN_PRIMARY"); - if (it != defines.end()) { - Project::num_pals_primary = it.value(); - } - else { - error = true; - } - it = defines.find("NUM_PALS_TOTAL"); - if (it != defines.end()) { - Project::num_pals_total = it.value(); - } - else { - error = true; - } - - if (error) - { - qDebug() << "Some global tileset values could not be loaded. Using default values"; - } - } -} - -void Project::readRegionMapSections() { - QString filepath = root + "/include/constants/region_map_sections.h"; - QStringList prefixes = (QStringList() << "MAPSEC_"); - readCDefinesSorted(filepath, prefixes, regionMapSections); -} - -void Project::readItemNames() { - QString filepath = root + "/include/constants/items.h"; - QStringList prefixes = (QStringList() << "ITEM_"); - readCDefinesSorted(filepath, prefixes, itemNames); -} - -void Project::readFlagNames() { - QString filepath = root + "/include/constants/flags.h"; - QStringList prefixes = (QStringList() << "FLAG_"); - readCDefinesSorted(filepath, prefixes, flagNames); -} - -void Project::readVarNames() { - QString filepath = root + "/include/constants/vars.h"; - QStringList prefixes = (QStringList() << "VAR_"); - readCDefinesSorted(filepath, prefixes, varNames); -} - -void Project::readMovementTypes() { - QString filepath = root + "/include/constants/event_object_movement_constants.h"; - QStringList prefixes = (QStringList() << "MOVEMENT_TYPE_"); - readCDefinesSorted(filepath, prefixes, movementTypes); -} - -void Project::readMapTypes() { - QString filepath = root + "/include/constants/map_types.h"; - QStringList prefixes = (QStringList() << "MAP_TYPE_"); - readCDefinesSorted(filepath, prefixes, mapTypes); -} - -void Project::readMapBattleScenes() { - QString filepath = root + "/include/constants/map_types.h"; - QStringList prefixes = (QStringList() << "MAP_BATTLE_SCENE_"); - readCDefinesSorted(filepath, prefixes, mapBattleScenes); -} - -void Project::readWeatherNames() { - QString filepath = root + "/include/constants/weather.h"; - QStringList prefixes = (QStringList() << "WEATHER_"); - readCDefinesSorted(filepath, prefixes, weatherNames); -} - -void Project::readCoordEventWeatherNames() { - QString filepath = root + "/include/constants/weather.h"; - QStringList prefixes = (QStringList() << "COORD_EVENT_WEATHER_"); - readCDefinesSorted(filepath, prefixes, coordEventWeatherNames); -} - -void Project::readSecretBaseIds() { - QString filepath = root + "/include/constants/secret_bases.h"; - QStringList prefixes = (QStringList() << "SECRET_BASE_"); - readCDefinesSorted(filepath, prefixes, secretBaseIds); -} - -void Project::readBgEventFacingDirections() { - QString filepath = root + "/include/constants/bg_event_constants.h"; - QStringList prefixes = (QStringList() << "BG_EVENT_PLAYER_FACING_"); - readCDefinesSorted(filepath, prefixes, bgEventFacingDirections); -} - -void Project::readCDefinesSorted(QString filepath, QStringList prefixes, QStringList* definesToSet) { - QString text = readTextFile(filepath); - if (!text.isNull()) { - QMap defines = readCDefines(text, prefixes); - - // The defines should to be sorted by their underlying value, not alphabetically. - // Reverse the map and read out the resulting keys in order. - QMultiMap definesInverse; - for (QString defineName : defines.keys()) { - definesInverse.insert(defines[defineName], defineName); - } - *definesToSet = definesInverse.values(); - } else { - qDebug() << "Failed to read C defines file: " << filepath; - } -} - -void Project::readMapsWithConnections() { - QString path = root + "/data/maps/connections.inc"; - QString text = readTextFile(path); - if (text.isNull()) { - return; - } - - mapsWithConnections.clear(); - QRegularExpression re("data\\/maps\\/(?\\w+)\\/connections.inc"); - QList* includes = parseAsm(text); - for (QStringList values : *includes) { - if (values.length() != 2) - continue; - - QRegularExpressionMatch match = re.match(values.value(1)); - if (match.hasMatch()) { - QString mapName = match.captured("mapName"); - mapsWithConnections.append(mapName); - } - } -} - -void Project::saveMapsWithConnections() { - QString path = root + "/data/maps/connections.inc"; - QString text = ""; - for (QString mapName : mapsWithConnections) { - if (mapNamesToMapConstants->contains(mapName)) { - text += QString("\t.include \"data/maps/%1/connections.inc\"\n").arg(mapName); - } else { - qDebug() << QString("Failed to write connection include. %1 not a valid map name").arg(mapName); - } - } - saveTextFile(path, text); -} - -QStringList Project::getSongNames() { - QStringList names; - QString text = readTextFile(root + "/include/constants/songs.h"); - if (!text.isNull()) { - QStringList songDefinePrefixes; - songDefinePrefixes << "SE_" << "MUS_"; - QMap songDefines = readCDefines(text, songDefinePrefixes); - names = songDefines.keys(); - } - return names; -} - -QMap Project::getEventObjGfxConstants() { - QMap constants; - QString text = readTextFile(root + "/include/constants/event_objects.h"); - if (!text.isNull()) { - QStringList eventObjGfxPrefixes; - eventObjGfxPrefixes << "EVENT_OBJ_GFX_"; - constants = readCDefines(text, eventObjGfxPrefixes); - } - return constants; -} - -QString Project::fixGraphicPath(QString path) { - path = path.replace(QRegExp("\\.lz$"), ""); - path = path.replace(QRegExp("\\.[1248]bpp$"), ".png"); - return path; -} - -void Project::loadEventPixmaps(QList objects) { - bool needs_update = false; - for (Event *object : objects) { - if (object->pixmap.isNull()) { - needs_update = true; - break; - } - } - if (!needs_update) { - return; - } - - QMap constants = getEventObjGfxConstants(); - - QString pointers_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics_info_pointers.h"); - QString info_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics_info.h"); - QString pic_text = readTextFile(root + "/src/data/field_event_obj/event_object_pic_tables.h"); - QString assets_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics.h"); - - QStringList pointers = readCArray(pointers_text, "gEventObjectGraphicsInfoPointers"); - - for (Event *object : objects) { - if (!object->pixmap.isNull()) { - continue; - } - - object->spriteWidth = 16; - object->spriteHeight = 16; - QString event_type = object->get("event_type"); - if (event_type == EventType::Object) { - object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(0, 0, 16, 16); - } else if (event_type == EventType::Warp) { - object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(16, 0, 16, 16); - } else if (event_type == EventType::CoordScript || event_type == EventType::CoordWeather) { - object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(32, 0, 16, 16); - } else if (event_type == EventType::Sign || event_type == EventType::HiddenItem || event_type == EventType::SecretBase) { - object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(48, 0, 16, 16); - } else if (event_type == EventType::HealLocation) { - object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(64, 0, 16, 16); - } - - if (event_type == EventType::Object) { - int sprite_id = constants.value(object->get("sprite")); - - QString info_label = pointers.value(sprite_id).replace("&", ""); - QStringList gfx_info = readCArray(info_text, info_label); - QString pic_label = gfx_info.value(14); - QString dimensions_label = gfx_info.value(11); - QString subsprites_label = gfx_info.value(12); - QString gfx_label = readCArray(pic_text, pic_label).value(0); - gfx_label = gfx_label.section(QRegExp("[\\(\\)]"), 1, 1); - QString path = readCIncbin(assets_text, gfx_label); - - if (!path.isNull()) { - path = fixGraphicPath(path); - QImage spritesheet(root + "/" + path); - if (!spritesheet.isNull()) { - // Infer the sprite dimensions from the OAM labels. - int spriteWidth = spritesheet.width(); - int spriteHeight = spritesheet.height(); - QRegularExpression re("\\S+_(\\d+)x(\\d+)"); - QRegularExpressionMatch dimensionMatch = re.match(dimensions_label); - if (dimensionMatch.hasMatch()) { - QRegularExpressionMatch oamTablesMatch = re.match(subsprites_label); - if (oamTablesMatch.hasMatch()) { - spriteWidth = dimensionMatch.captured(1).toInt(); - spriteHeight = dimensionMatch.captured(2).toInt(); - } - } - object->setPixmapFromSpritesheet(spritesheet, spriteWidth, spriteHeight); - } - } - } - } -} - -void Project::saveMapEvents(Map *map) { - QString path = root + QString("/data/maps/%1/events.inc").arg(map->name); - QString text = ""; - QString objectEventsLabel = "0x0"; - QString warpEventsLabel = "0x0"; - QString coordEventsLabel = "0x0"; - QString bgEventsLabel = "0x0"; - - if (map->events["object_event_group"].length() > 0) { - objectEventsLabel = Map::objectEventsLabelFromName(map->name); - text += QString("%1::\n").arg(objectEventsLabel); - for (int i = 0; i < map->events["object_event_group"].length(); i++) { - Event *object_event = map->events["object_event_group"].value(i); - text += object_event->buildObjectEventMacro(i); - } - text += "\n"; - } - - if (map->events["warp_event_group"].length() > 0) { - warpEventsLabel = Map::warpEventsLabelFromName(map->name); - text += QString("%1::\n").arg(warpEventsLabel); - for (Event *warp : map->events["warp_event_group"]) { - text += warp->buildWarpEventMacro(mapNamesToMapConstants); - } - text += "\n"; - } - - if (map->events["coord_event_group"].length() > 0) { - coordEventsLabel = Map::coordEventsLabelFromName(map->name); - text += QString("%1::\n").arg(coordEventsLabel); - for (Event *event : map->events["coord_event_group"]) { - QString event_type = event->get("event_type"); - if (event_type == EventType::CoordScript) { - text += event->buildCoordScriptEventMacro(); - } else if (event_type == EventType::CoordWeather) { - text += event->buildCoordWeatherEventMacro(); - } - } - text += "\n"; - } - - if (map->events["bg_event_group"].length() > 0) - { - bgEventsLabel = Map::bgEventsLabelFromName(map->name); - text += QString("%1::\n").arg(bgEventsLabel); - for (Event *event : map->events["bg_event_group"]) { - QString event_type = event->get("event_type"); - if (event_type == EventType::Sign) { - text += event->buildSignEventMacro(); - } else if (event_type == EventType::HiddenItem) { - text += event->buildHiddenItemEventMacro(); - } else if (event_type == EventType::SecretBase) { - text += event->buildSecretBaseEventMacro(); - } - } - text += "\n"; - } - - text += QString("%1::\n").arg(map->events_label); - text += QString("\tmap_events %1, %2, %3, %4\n") - .arg(objectEventsLabel) - .arg(warpEventsLabel) - .arg(coordEventsLabel) - .arg(bgEventsLabel); - - saveTextFile(path, text); - - // save heal event changes - if (map->events["heal_event_group"].length() > 0) { - for (Event *heal : map->events["heal_event_group"]) { - HealLocation hl = heal->buildHealLocation(); - flyableMaps[hl.index - 1] = hl; - } - } - saveHealLocationStruct(map); -} - -void Project::readMapEvents(Map *map) { - if (!map->isPersistedToFile) { - return; - } - - // lazy - QString path = root + QString("/data/maps/%1/events.inc").arg(map->name); - QString text = readTextFile(path); - if (text.isNull()) { - return; - } - - QStringList *labels = getLabelValues(parseAsm(text), map->events_label); - QString objectEventsLabel = labels->value(0); - QString warpEventsLabel = labels->value(1); - QString coordEventsLabel = labels->value(2); - QString bgEventsLabel = labels->value(3); - - QList *object_events = getLabelMacros(parseAsm(text), objectEventsLabel); - map->events["object_event_group"].clear(); - for (QStringList command : *object_events) { - if (command.value(0) == "object_event") { - Event *object = new Event; - object->put("map_name", map->name); - int i = 2; - object->put("sprite", command.value(i++)); - object->put("replacement", command.value(i++)); - object->put("x", command.value(i++).toInt(nullptr, 0)); - object->put("y", command.value(i++).toInt(nullptr, 0)); - object->put("elevation", command.value(i++)); - object->put("movement_type", command.value(i++)); - object->put("radius_x", command.value(i++).toInt(nullptr, 0)); - object->put("radius_y", command.value(i++).toInt(nullptr, 0)); - object->put("is_trainer", command.value(i++)); - object->put("sight_radius_tree_id", command.value(i++)); - object->put("script_label", command.value(i++)); - object->put("event_flag", command.value(i++)); - object->put("event_group_type", "object_event_group"); - object->put("event_type", EventType::Object); - map->events["object_event_group"].append(object); - } - } - - QList *warps = getLabelMacros(parseAsm(text), warpEventsLabel); - map->events["warp_event_group"].clear(); - for (QStringList command : *warps) { - if (command.value(0) == "warp_def") { - Event *warp = new Event; - warp->put("map_name", map->name); - int i = 1; - warp->put("x", command.value(i++)); - warp->put("y", command.value(i++)); - warp->put("elevation", command.value(i++)); - warp->put("destination_warp", command.value(i++)); - - // Ensure the warp destination map constant is valid before adding it to the warps. - QString mapConstant = command.value(i++); - if (mapConstantsToMapNames->contains(mapConstant)) { - warp->put("destination_map_name", mapConstantsToMapNames->value(mapConstant)); - warp->put("event_group_type", "warp_event_group"); - warp->put("event_type", EventType::Warp); - map->events["warp_event_group"].append(warp); - } else if (mapConstant == NONE_MAP_CONSTANT) { - warp->put("destination_map_name", NONE_MAP_NAME); - warp->put("event_group_type", "warp_event_group"); - warp->put("event_type", EventType::Warp); - map->events["warp_event_group"].append(warp); - } else { - qDebug() << QString("Destination map constant '%1' is invalid for warp").arg(mapConstant); - } - } - } - - map->events["heal_event_group"].clear(); - - for (auto it = flyableMaps.begin(); it != flyableMaps.end(); it++) { - - HealLocation loc = *it; - - //if TRUE map is flyable / has healing location - if (loc.name == QString(mapNamesToMapConstants->value(map->name)).remove(0,4)) { - Event *heal = new Event; - heal->put("map_name", map->name); - heal->put("x", loc.x); - heal->put("y", loc.y); - heal->put("loc_name", loc.name); - heal->put("index", loc.index); - heal->put("elevation", 3); // TODO: change this? - heal->put("destination_map_name", mapConstantsToMapNames->value(map->name)); - heal->put("event_group_type", "heal_event_group"); - heal->put("event_type", EventType::HealLocation); - map->events["heal_event_group"].append(heal); - } - - } - - QList *coords = getLabelMacros(parseAsm(text), coordEventsLabel); - map->events["coord_event_group"].clear(); - for (QStringList command : *coords) { - if (command.value(0) == "coord_event") { - Event *coord = new Event; - coord->put("map_name", map->name); - int i = 1; - coord->put("x", command.value(i++)); - coord->put("y", command.value(i++)); - coord->put("elevation", command.value(i++)); - coord->put("script_var", command.value(i++)); - coord->put("script_var_value", command.value(i++)); - coord->put("script_label", command.value(i++)); - coord->put("event_group_type", "coord_event_group"); - coord->put("event_type", EventType::CoordScript); - map->events["coord_event_group"].append(coord); - } else if (command.value(0) == "coord_weather_event") { - Event *coord = new Event; - coord->put("map_name", map->name); - int i = 1; - coord->put("x", command.value(i++)); - coord->put("y", command.value(i++)); - coord->put("elevation", command.value(i++)); - coord->put("weather", command.value(i++)); - coord->put("event_group_type", "coord_event_group"); - coord->put("event_type", EventType::CoordWeather); - map->events["coord_event_group"].append(coord); - } - } - - QList *bgs = getLabelMacros(parseAsm(text), bgEventsLabel); - map->events["bg_event_group"].clear(); - for (QStringList command : *bgs) { - if (command.value(0) == "bg_event") { - Event *bg = new Event; - bg->put("map_name", map->name); - int i = 1; - bg->put("x", command.value(i++)); - bg->put("y", command.value(i++)); - bg->put("elevation", command.value(i++)); - bg->put("player_facing_direction", command.value(i++)); - bg->put("script_label", command.value(i++)); - //sign_unknown7 - bg->put("event_group_type", "bg_event_group"); - bg->put("event_type", EventType::Sign); - map->events["bg_event_group"].append(bg); - } else if (command.value(0) == "bg_hidden_item_event") { - Event *bg = new Event; - bg->put("map_name", map->name); - int i = 1; - bg->put("x", command.value(i++)); - bg->put("y", command.value(i++)); - bg->put("elevation", command.value(i++)); - bg->put("item", command.value(i++)); - bg->put("flag", command.value(i++)); - bg->put("event_group_type", "bg_event_group"); - bg->put("event_type", EventType::HiddenItem); - map->events["bg_event_group"].append(bg); - } else if (command.value(0) == "bg_secret_base_event") { - Event *bg = new Event; - bg->put("map_name", map->name); - int i = 1; - bg->put("x", command.value(i++)); - bg->put("y", command.value(i++)); - bg->put("elevation", command.value(i++)); - bg->put("secret_base_id", command.value(i++)); - bg->put("event_group_type", "bg_event_group"); - bg->put("event_type", EventType::SecretBase); - map->events["bg_event_group"].append(bg); - } - } -} - -void Project::setNewMapEvents(Map *map) { - map->events["object_event_group"].clear(); - map->events["warp_event_group"].clear(); - map->events["heal_event_group"].clear(); - map->events["coord_event_group"].clear(); - map->events["bg_event_group"].clear(); -} - -QStringList Project::readCArray(QString text, QString label) { - QStringList list; - - if (label.isNull()) { - return list; - } - - QRegExp *re = new QRegExp(QString("\\b%1\\b\\s*\\[?\\s*\\]?\\s*=\\s*\\{([^\\}]*)\\}").arg(label)); - int pos = re->indexIn(text); - if (pos != -1) { - QString body = re->cap(1); - body = body.replace(QRegExp("\\s*"), ""); - list = body.split(','); - /* - QRegExp *inner = new QRegExp("&?\\b([A-Za-z0-9_\\(\\)]*)\\b,"); - int pos = 0; - while ((pos = inner->indexIn(body, pos)) != -1) { - list << inner->cap(1); - pos += inner->matchedLength(); - } - */ - } - - return list; -} - -QString Project::readCIncbin(QString text, QString label) { - QString path; - - if (label.isNull()) { - return path; - } - - QRegExp *re = new QRegExp(QString( - "\\b%1\\b" - "\\s*\\[?\\s*\\]?\\s*=\\s*" - "INCBIN_[US][0-9][0-9]?" - "\\(\"([^\"]*)\"\\)").arg(label)); - - int pos = re->indexIn(text); - if (pos != -1) { - path = re->cap(1); - } - - return path; -} - -QMap Project::readCDefines(QString text, QStringList prefixes) { - ParseUtil parser; - QMap allDefines; - QMap filteredDefines; - QRegularExpression re("#define\\s+(?\\w+)[^\\S\\n]+(?.+)"); - QRegularExpressionMatchIterator iter = re.globalMatch(text); - while (iter.hasNext()) { - QRegularExpressionMatch match = iter.next(); - QString name = match.captured("defineName"); - QString expression = match.captured("defineValue"); - expression.replace(QRegularExpression("//.*"), ""); - int value = parser.evaluateDefine(expression, &allDefines); - allDefines.insert(name, value); - for (QString prefix : prefixes) { - if (name.startsWith(prefix)) { - filteredDefines.insert(name, value); - } - } - } - return filteredDefines; -} - -int Project::getNumTilesPrimary() -{ - return Project::num_tiles_primary; -} - -int Project::getNumTilesTotal() -{ - return Project::num_tiles_total; -} - -int Project::getNumMetatilesPrimary() -{ - return Project::num_metatiles_primary; -} - -int Project::getNumMetatilesTotal() -{ - return Project::num_metatiles_total; -} - -int Project::getNumPalettesPrimary() -{ - return Project::num_pals_primary; -} - -int Project::getNumPalettesTotal() -{ - return Project::num_pals_total; -} +#include "project.h" +#include "history.h" +#include "historyitem.h" +#include "parseutil.h" +#include "tile.h" +#include "tileset.h" +#include "event.h" + +#include +#include +#include +#include +#include +#include +#include + +int Project::num_tiles_primary = 512; +int Project::num_tiles_total = 1024; +int Project::num_metatiles_primary = 512; +int Project::num_metatiles_total = 1024; +int Project::num_pals_primary = 6; +int Project::num_pals_total = 13; + +Project::Project() +{ + groupNames = new QStringList; + map_groups = new QMap; + mapNames = new QStringList; + regionMapSections = new QStringList; + itemNames = new QStringList; + flagNames = new QStringList; + varNames = new QStringList; + movementTypes = new QStringList; + mapTypes = new QStringList; + mapBattleScenes = new QStringList; + weatherNames = new QStringList; + coordEventWeatherNames = new QStringList; + secretBaseIds = new QStringList; + bgEventFacingDirections = new QStringList; + map_cache = new QMap; + mapConstantsToMapNames = new QMap; + mapNamesToMapConstants = new QMap; + tileset_cache = new QMap; +} + +QString Project::getProjectTitle() { + if (!root.isNull()) { + return root.section('/', -1); + } else { + return QString(); + } +} + +Map* Project::loadMap(QString map_name) { + Map *map; + if (map_cache->contains(map_name)) { + map = map_cache->value(map_name); + // TODO: uncomment when undo/redo history is fully implemented for all actions. + if (true/*map->hasUnsavedChanges()*/) { + return map; + } + } else { + map = new Map; + map->setName(map_name); + } + + readMapHeader(map); + readMapLayout(map); + readMapEvents(map); + loadMapConnections(map); + map->commit(); + map->metatileHistory.save(); + + map_cache->insert(map_name, map); + return map; +} + +void Project::loadMapConnections(Map *map) { + if (!map->isPersistedToFile) { + return; + } + + map->connections.clear(); + if (!map->connections_label.isNull()) { + QString path = root + QString("/data/maps/%1/connections.inc").arg(map->name); + QString text = readTextFile(path); + if (!text.isNull()) { + QList *commands = parseAsm(text); + QStringList *list = getLabelValues(commands, map->connections_label); + + //// Avoid using this value. It ought to be generated instead. + //int num_connections = list->value(0).toInt(nullptr, 0); + + QString connections_list_label = list->value(1); + QList *connections = getLabelMacros(commands, connections_list_label); + for (QStringList command : *connections) { + QString macro = command.value(0); + if (macro == "connection") { + MapConnection *connection = new MapConnection; + connection->direction = command.value(1); + connection->offset = command.value(2); + QString mapConstant = command.value(3); + if (mapConstantsToMapNames->contains(mapConstant)) { + connection->map_name = mapConstantsToMapNames->value(mapConstant); + map->connections.append(connection); + } else { + qDebug() << QString("Failed to find connected map for map constant '%1'").arg(mapConstant); + } + } + } + } + } +} + +void Project::setNewMapConnections(Map *map) { + map->connections.clear(); +} + +QList* Project::getLabelMacros(QList *list, QString label) { + bool in_label = false; + QList *new_list = new QList; + for (int i = 0; i < list->length(); i++) { + QStringList params = list->value(i); + QString macro = params.value(0); + if (macro == ".label") { + if (params.value(1) == label) { + in_label = true; + } else if (in_label) { + // If nothing has been read yet, assume the label + // we're looking for is in a stack of labels. + if (new_list->length() > 0) { + break; + } + } + } else if (in_label) { + new_list->append(params); + } + } + return new_list; +} + +// For if you don't care about filtering by macro, +// and just want all values associated with some label. +QStringList* Project::getLabelValues(QList *list, QString label) { + list = getLabelMacros(list, label); + QStringList *values = new QStringList; + for (int i = 0; i < list->length(); i++) { + QStringList params = list->value(i); + QString macro = params.value(0); + // Ignore .align + if (macro == ".align") + continue; + if (macro == ".ifdef") + continue; + if (macro == ".ifndef") + continue; + for (int j = 1; j < params.length(); j++) { + values->append(params.value(j)); + } + } + return values; +} + +void Project::readMapHeader(Map* map) { + if (!map->isPersistedToFile) { + return; + } + + QString label = map->name; + ParseUtil *parser = new ParseUtil; + + QString header_text = readTextFile(root + "/data/maps/" + label + "/header.inc"); + if (header_text.isNull()) { + return; + } + QStringList *header = getLabelValues(parser->parseAsm(header_text), label); + map->layout_label = header->value(0); + map->events_label = header->value(1); + map->scripts_label = header->value(2); + map->connections_label = header->value(3); + map->song = header->value(4); + map->layout_id = header->value(5); + map->location = header->value(6); + map->requiresFlash = header->value(7); + map->weather = header->value(8); + map->type = header->value(9); + map->unknown = header->value(10); + map->show_location = header->value(11); + map->battle_scene = header->value(12); +} + +void Project::setNewMapHeader(Map* map, int mapIndex) { + map->layout_label = QString("%1_Layout").arg(map->name); + map->events_label = QString("%1_MapEvents").arg(map->name);; + map->scripts_label = QString("%1_MapScripts").arg(map->name);; + map->connections_label = "0x0"; + map->song = "MUS_DAN02"; + map->layout_id = QString("%1").arg(mapIndex); + map->location = "MAPSEC_LITTLEROOT_TOWN"; + map->requiresFlash = "FALSE"; + map->weather = "WEATHER_SUNNY"; + map->type = "MAP_TYPE_TOWN"; + map->unknown = "0"; + map->show_location = "TRUE"; + map->battle_scene = "MAP_BATTLE_SCENE_NORMAL"; +} + +void Project::saveMapHeader(Map *map) { + QString label = map->name; + QString header_path = root + "/data/maps/" + label + "/header.inc"; + QString text = ""; + text += QString("%1::\n").arg(label); + text += QString("\t.4byte %1\n").arg(map->layout_label); + text += QString("\t.4byte %1\n").arg(map->events_label); + text += QString("\t.4byte %1\n").arg(map->scripts_label); + + if (map->connections.length() == 0) { + map->connections_label = "0x0"; + } else { + map->connections_label = QString("%1_MapConnections").arg(map->name); + } + text += QString("\t.4byte %1\n").arg(map->connections_label); + + text += QString("\t.2byte %1\n").arg(map->song); + text += QString("\t.2byte %1\n").arg(map->layout_id); + text += QString("\t.byte %1\n").arg(map->location); + text += QString("\t.byte %1\n").arg(map->requiresFlash); + text += QString("\t.byte %1\n").arg(map->weather); + text += QString("\t.byte %1\n").arg(map->type); + text += QString("\t.2byte %1\n").arg(map->unknown); + text += QString("\t.byte %1\n").arg(map->show_location); + text += QString("\t.byte %1\n").arg(map->battle_scene); + saveTextFile(header_path, text); +} + +void Project::saveMapConnections(Map *map) { + QString path = root + "/data/maps/" + map->name + "/connections.inc"; + if (map->connections.length() > 0) { + QString text = ""; + QString connectionsListLabel = QString("%1_MapConnectionsList").arg(map->name); + int numValidConnections = 0; + text += QString("%1::\n").arg(connectionsListLabel); + for (MapConnection* connection : map->connections) { + if (mapNamesToMapConstants->contains(connection->map_name)) { + text += QString("\tconnection %1, %2, %3\n") + .arg(connection->direction) + .arg(connection->offset) + .arg(mapNamesToMapConstants->value(connection->map_name)); + numValidConnections++; + } else { + qDebug() << QString("Failed to write map connection. %1 not a valid map name").arg(connection->map_name); + } + } + text += QString("\n"); + text += QString("%1::\n").arg(map->connections_label); + text += QString("\t.4byte %1\n").arg(numValidConnections); + text += QString("\t.4byte %1\n").arg(connectionsListLabel); + saveTextFile(path, text); + } else { + deleteFile(path); + } + + updateMapsWithConnections(map); +} + +void Project::updateMapsWithConnections(Map *map) { + if (map->connections.length() > 0) { + if (!mapsWithConnections.contains(map->name)) { + mapsWithConnections.append(map->name); + } + } else { + if (mapsWithConnections.contains(map->name)) { + mapsWithConnections.removeOne(map->name); + } + } +} + +void Project::readMapLayoutsTable() { + QString layoutsText = readTextFile(getMapLayoutsTableFilepath()); + QList* values = parseAsm(layoutsText); + bool inLayoutPointers = false; + for (int i = 0; i < values->length(); i++) { + QStringList params = values->value(i); + QString macro = params.value(0); + if (macro == ".label") { + if (inLayoutPointers) { + break; + } + if (params.value(1) == "gMapLayouts") { + inLayoutPointers = true; + } + } else if (macro == ".4byte" && inLayoutPointers) { + QString layoutName = params.value(1); + mapLayoutsTable.append(layoutName); + } + } + + // Deep copy + mapLayoutsTableMaster = mapLayoutsTable; + mapLayoutsTableMaster.detach(); +} + +void Project::saveMapLayoutsTable() { + QString text = ""; + text += QString("\t.align 2\n"); + text += QString("gMapLayouts::\n"); + for (QString layoutName : mapLayoutsTableMaster) { + text += QString("\t.4byte %1\n").arg(layoutName); + } + saveTextFile(getMapLayoutsTableFilepath(), text); +} + +QString Project::getMapLayoutsTableFilepath() { + return QString("%1/data/layouts_table.inc").arg(root); +} + +QStringList* Project::readLayoutValues(QString layoutLabel) { + ParseUtil *parser = new ParseUtil; + + QString layoutText = readTextFile(getMapLayoutFilepath(layoutLabel)); + if (layoutText.isNull()) { + return nullptr; + } + + QStringList *layoutValues = getLabelValues(parser->parseAsm(layoutText), layoutLabel); + QString borderLabel = layoutValues->value(2); + QString blockdataLabel = layoutValues->value(3); + QStringList *borderValues = getLabelValues(parser->parseAsm(layoutText), borderLabel); + QString borderPath = borderValues->value(0).replace("\"", ""); + layoutValues->append(borderPath); + QStringList *blockdataValues = getLabelValues(parser->parseAsm(layoutText), blockdataLabel); + QString blockdataPath = blockdataValues->value(0).replace("\"", ""); + layoutValues->append(blockdataPath); + + if (layoutValues->size() != 8) { + qDebug() << "Error: Unexpected number of properties in layout '" << layoutLabel << "'"; + return nullptr; + } + + return layoutValues; +} + +void Project::readMapLayout(Map* map) { + if (!map->isPersistedToFile) { + return; + } + + MapLayout *layout; + if (!mapLayouts.contains(map->layout_label)) { + QStringList *layoutValues = readLayoutValues(map->layout->label); + if (!layoutValues) { + return; + } + + layout = new MapLayout(); + mapLayouts.insert(map->layout_label, layout); + layout->name = MapLayout::getNameFromLabel(map->layout_label); + layout->label = map->layout_label; + layout->width = layoutValues->value(0); + layout->height = layoutValues->value(1); + layout->border_label = layoutValues->value(2); + layout->blockdata_label = layoutValues->value(3); + layout->tileset_primary_label = layoutValues->value(4); + layout->tileset_secondary_label = layoutValues->value(5); + layout->border_path = layoutValues->value(6); + layout->blockdata_path = layoutValues->value(7); + map->layout = layout; + } else { + map->layout = mapLayouts[map->layout_label]; + } + + loadMapTilesets(map); + loadBlockdata(map); + loadMapBorder(map); +} + +void Project::readAllMapLayouts() { + mapLayouts.clear(); + + for (int i = 0; i < mapLayoutsTable.size(); i++) { + QString layoutLabel = mapLayoutsTable[i]; + QStringList *layoutValues = readLayoutValues(layoutLabel); + if (!layoutValues) { + return; + } + + MapLayout *layout = new MapLayout(); + layout->name = MapLayout::getNameFromLabel(layoutLabel); + layout->label = layoutLabel; + layout->index = i; + layout->width = layoutValues->value(0); + layout->height = layoutValues->value(1); + layout->border_label = layoutValues->value(2); + layout->blockdata_label = layoutValues->value(3); + layout->tileset_primary_label = layoutValues->value(4); + layout->tileset_secondary_label = layoutValues->value(5); + layout->border_path = layoutValues->value(6); + layout->blockdata_path = layoutValues->value(7); + mapLayouts.insert(layoutLabel, layout); + } + + // Deep copy + mapLayoutsMaster = mapLayouts; + mapLayoutsMaster.detach(); +} + +void Project::saveAllMapLayouts() { + for (QString layoutName : mapLayoutsTableMaster) { + MapLayout *layout = mapLayouts.value(layoutName); + QString text = QString("%1::\n").arg(layout->border_label); + text += QString("\t.incbin \"%1\"\n").arg(layout->border_path); + text += QString("\n"); + text += QString("%1::\n").arg(layout->blockdata_label); + text += QString("\t.incbin \"%1\"\n").arg(layout->blockdata_path); + text += QString("\n"); + text += QString("\t.align 2\n"); + text += QString("%1::\n").arg(layoutName); + text += QString("\t.4byte %1\n").arg(layout->width); + text += QString("\t.4byte %1\n").arg(layout->height); + text += QString("\t.4byte %1\n").arg(layout->border_label); + text += QString("\t.4byte %1\n").arg(layout->blockdata_label); + text += QString("\t.4byte %1\n").arg(layout->tileset_primary_label); + text += QString("\t.4byte %1\n").arg(layout->tileset_secondary_label); + text += QString("\n"); + saveTextFile(getMapLayoutFilepath(layout->label), text); + } +} + +QString Project::getMapLayoutFilepath(QString layoutLabel) { + return QString("%1/data/layouts/%2/layout.inc").arg(root).arg(MapLayout::getNameFromLabel(layoutLabel)); +} + +void Project::setNewMapLayout(Map* map) { + MapLayout *layout = new MapLayout(); + layout->label = QString("%1_Layout").arg(map->name); + layout->name = MapLayout::getNameFromLabel(layout->label); + layout->width = "20"; + layout->height = "20"; + layout->border_label = QString("%1_MapBorder").arg(map->name); + layout->border_path = QString("data/layouts/%1/border.bin").arg(map->name); + layout->blockdata_label = QString("%1_MapBlockdata").arg(map->name); + layout->blockdata_path = QString("data/layouts/%1/map.bin").arg(map->name); + layout->tileset_primary_label = "gTileset_General"; + layout->tileset_secondary_label = "gTileset_Petalburg"; + map->layout = layout; + map->layout_label = layout->label; + + // Insert new entry into the global map layouts. + mapLayouts.insert(layout->label, layout); + mapLayoutsTable.append(layout->label); +} + +void Project::saveMapGroupsTable() { + QString text = ""; + int groupNum = 0; + for (QStringList mapNames : groupedMapNames) { + text += QString("\t.align 2\n"); + text += QString("gMapGroup%1::\n").arg(groupNum); + for (QString mapName : mapNames) { + text += QString("\t.4byte %1\n").arg(mapName); + } + text += QString("\n"); + groupNum++; + } + + text += QString("\t.align 2\n"); + text += QString("gMapGroups::\n"); + for (int i = 0; i < groupNum; i++) { + text += QString("\t.4byte gMapGroup%1\n").arg(i); + } + + saveTextFile(root + "/data/maps/groups.inc", text); +} + +void Project::saveMapConstantsHeader() { + QString text = QString("#ifndef GUARD_CONSTANTS_MAPS_H\n"); + text += QString("#define GUARD_CONSTANTS_MAPS_H\n"); + text += QString("\n"); + + int groupNum = 0; + for (QStringList mapNames : groupedMapNames) { + text += QString("// Map Group %1\n").arg(groupNum); + int maxLength = 0; + for (QString mapName : mapNames) { + QString mapConstantName = mapNamesToMapConstants->value(mapName); + if (mapConstantName.length() > maxLength) + maxLength = mapConstantName.length(); + } + int groupIndex = 0; + for (QString mapName : mapNames) { + QString mapConstantName = mapNamesToMapConstants->value(mapName); + text += QString("#define %1%2(%3 | (%4 << 8))\n") + .arg(mapConstantName) + .arg(QString(" ").repeated(maxLength - mapConstantName.length() + 1)) + .arg(groupIndex) + .arg(groupNum); + groupIndex++; + } + text += QString("\n"); + groupNum++; + } + + text += QString("\n"); + text += QString("#define MAP_NONE (0x7F | (0x7F << 8))\n"); + text += QString("#define MAP_UNDEFINED (0xFF | (0xFF << 8))\n\n\n"); + text += QString("#define MAP_GROUP(map) (MAP_##map >> 8)\n"); + text += QString("#define MAP_NUM(map) (MAP_##map & 0xFF)\n\n"); + text += QString("#endif // GUARD_CONSTANTS_MAPS_H\n"); + saveTextFile(root + "/include/constants/maps.h", text); +} + +// saves heal location coords in root + /src/data/heal_locations.h +// and indexes as defines in root + /include/constants/heal_locations.h +void Project::saveHealLocationStruct(Map *map) { + QString tab = QString(" "); + + QString data_text = QString("static const struct HealLocation sHealLocations[] =\n{\n"); + + QString constants_text = QString("#ifndef GUARD_CONSTANTS_HEAL_LOCATIONS_H\n"); + constants_text += QString("#define GUARD_CONSTANTS_HEAL_LOCATIONS_H\n\n"); + + QMap flyableMapsDupes; + QSet flyableMapsUnique; + + // set flyableMapsDupes and flyableMapsUnique + for (auto it = flyableMaps.begin(); it != flyableMaps.end(); it++) { + HealLocation loc = *it; + QString xname = loc.name; + if (flyableMapsUnique.contains(xname)) { + flyableMapsDupes[xname] = 1; + } + flyableMapsUnique.insert(xname); + } + + // set new location in flyableMapsList + if (map->events["heal_event_group"].length() > 0) { + for (Event *healEvent : map->events["heal_event_group"]) { + HealLocation hl = HealLocation::fromEvent(healEvent); + flyableMaps[hl.index - 1] = hl; + } + } + + int i = 1; + + for (auto map_in : flyableMaps) { + data_text += QString(" {MAP_GROUP(%1), MAP_NUM(%1), %2, %3},\n") + .arg(map_in.name) + .arg(map_in.x) + .arg(map_in.y); + + QString ending = QString(""); + + // must add _1 / _2 for maps that have duplicates + if (flyableMapsDupes.keys().contains(map_in.name)) { + // map contains multiple heal locations + ending += QString("_%1").arg(flyableMapsDupes[map_in.name]); + flyableMapsDupes[map_in.name]++; + } + if (map_in.index != 0) { + constants_text += QString("#define HEAL_LOCATION_%1 %2\n") + .arg(map_in.name + ending) + .arg(map_in.index); + } + else { + constants_text += QString("#define HEAL_LOCATION_%1 %2\n") + .arg(map_in.name + ending) + .arg(i); + } + i++; + } + + data_text += QString("};\n"); + constants_text += QString("\n#endif // GUARD_CONSTANTS_HEAL_LOCATIONS_H\n"); + + saveTextFile(root + "/src/data/heal_locations.h", data_text); + saveTextFile(root + "/include/constants/heal_locations.h", constants_text); +} + +void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { + saveTilesetMetatileAttributes(primaryTileset); + saveTilesetMetatileAttributes(secondaryTileset); + saveTilesetMetatiles(primaryTileset); + saveTilesetMetatiles(secondaryTileset); + saveTilesetTilesImage(primaryTileset); + saveTilesetTilesImage(secondaryTileset); + saveTilesetPalettes(primaryTileset, true); + saveTilesetPalettes(secondaryTileset, false); +} + +void Project::saveTilesetMetatileAttributes(Tileset *tileset) { + QFile attrs_file(tileset->metatile_attrs_path); + if (attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QByteArray data; + for (Metatile *metatile : *tileset->metatiles) { + data.append(static_cast(metatile->behavior)); + data.append(static_cast((metatile->layerType << 4) & 0xF0)); + } + attrs_file.write(data); + } else { + qDebug() << QString("Could not save tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path); + } +} + +void Project::saveTilesetMetatiles(Tileset *tileset) { + QFile metatiles_file(tileset->metatiles_path); + if (metatiles_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QByteArray data; + for (Metatile *metatile : *tileset->metatiles) { + for (int i = 0; i < 8; i++) { + Tile tile = metatile->tiles->at(i); + uint16_t value = static_cast((tile.tile & 0x3ff) + | ((tile.xflip & 1) << 10) + | ((tile.yflip & 1) << 11) + | ((tile.palette & 0xf) << 12)); + data.append(static_cast(value & 0xff)); + data.append(static_cast((value >> 8) & 0xff)); + } + } + metatiles_file.write(data); + } else { + tileset->metatiles = new QList; + qDebug() << QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path); + } +} + +void Project::saveTilesetTilesImage(Tileset *tileset) { + tileset->tilesImage.save(tileset->tilesImagePath); +} + +void Project::saveTilesetPalettes(Tileset *tileset, bool primary) { + int startPaletteId = primary ? 0 : Project::getNumPalettesPrimary(); + int endPaletteId = primary ? Project::getNumPalettesPrimary() : Project::getNumPalettesTotal(); + for (int i = startPaletteId; i < endPaletteId; i++) { + QString filepath = tileset->palettePaths.at(i); + QString content = "JASC-PAL\r\n"; + content += "0100\r\n"; + content += "16\r\n"; + for (int j = 0; j < 16; j++) { + QRgb color = tileset->palettes->at(i).at(j); + content += QString("%1 %2 %3\r\n") + .arg(qRed(color)) + .arg(qGreen(color)) + .arg(qBlue(color)); + } + + saveTextFile(filepath, content); + } +} + +void Project::loadMapTilesets(Map* map) { + if (map->layout->has_unsaved_changes) { + return; + } + + map->layout->tileset_primary = getTileset(map->layout->tileset_primary_label); + map->layout->tileset_secondary = getTileset(map->layout->tileset_secondary_label); +} + +Tileset* Project::loadTileset(QString label, Tileset *tileset) { + ParseUtil *parser = new ParseUtil; + + QString headers_text = readTextFile(root + "/data/tilesets/headers.inc"); + QStringList *values = getLabelValues(parser->parseAsm(headers_text), label); + if (tileset == nullptr) { + tileset = new Tileset; + } + tileset->name = label; + tileset->is_compressed = values->value(0); + tileset->is_secondary = values->value(1); + tileset->padding = values->value(2); + tileset->tiles_label = values->value(3); + tileset->palettes_label = values->value(4); + tileset->metatiles_label = values->value(5); + tileset->metatile_attrs_label = values->value(6); + tileset->callback_label = values->value(7); + + loadTilesetAssets(tileset); + + tileset_cache->insert(label, tileset); + return tileset; +} + +void Project::loadBlockdata(Map* map) { + if (!map->isPersistedToFile || map->layout->has_unsaved_changes) { + return; + } + + QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path); + map->layout->blockdata = readBlockdata(path); +} + +void Project::setNewMapBlockdata(Map* map) { + Blockdata *blockdata = new Blockdata; + for (int i = 0; i < map->getWidth() * map->getHeight(); i++) { + blockdata->addBlock(qint16(0x3001)); + } + map->layout->blockdata = blockdata; +} + +void Project::loadMapBorder(Map *map) { + if (!map->isPersistedToFile || map->layout->has_unsaved_changes) { + return; + } + + QString path = QString("%1/%2").arg(root).arg(map->layout->border_path); + map->layout->border = readBlockdata(path); +} + +void Project::setNewMapBorder(Map *map) { + Blockdata *blockdata = new Blockdata; + blockdata->addBlock(qint16(0x01D4)); + blockdata->addBlock(qint16(0x01D5)); + blockdata->addBlock(qint16(0x01DC)); + blockdata->addBlock(qint16(0x01DD)); + map->layout->border = blockdata; +} + +void Project::saveMapBorder(Map *map) { + QString path = QString("%1/%2").arg(root).arg(map->layout->border_path); + writeBlockdata(path, map->layout->border); +} + +void Project::saveBlockdata(Map* map) { + QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path); + writeBlockdata(path, map->layout->blockdata); + map->metatileHistory.save(); +} + +void Project::writeBlockdata(QString path, Blockdata *blockdata) { + QFile file(path); + if (file.open(QIODevice::WriteOnly)) { + QByteArray data = blockdata->serialize(); + file.write(data); + } else { + qDebug() << "Failed to open blockdata file for writing: '" << path << "'"; + } +} + +void Project::saveAllMaps() { + QList keys = map_cache->keys(); + for (int i = 0; i < keys.length(); i++) { + QString key = keys.value(i); + Map* map = map_cache->value(key); + saveMap(map); + } +} + +void Project::saveMap(Map *map) { + // Create/Modify a few collateral files for brand new maps. + if (!map->isPersistedToFile) { + QString newMapDataDir = QString(root + "/data/maps/%1").arg(map->name); + if (!QDir::root().mkdir(newMapDataDir)) { + qDebug() << "Error: failed to create directory for new map. " << newMapDataDir; + } + + QString newLayoutDir = QString(root + "/data/layouts/%1").arg(map->name); + if (!QDir::root().mkdir(newLayoutDir)) { + qDebug() << "Error: failed to create directory for new layout. " << newLayoutDir; + } + + // TODO: In the future, these files needs more structure to allow for proper parsing/saving. + // Create file data/maps//scripts.inc + QString text = QString("%1_MapScripts::\n\t.byte 0\n").arg(map->name); + saveTextFile(root + "/data/maps/" + map->name + "/scripts.inc", text); + + // Create file data/maps//text.inc + saveTextFile(root + "/data/maps/" + map->name + "/text.inc", "\n"); + + // Simply append to data/event_scripts.s. + text = QString("\n\t.include \"data/maps/%1/scripts.inc\"\n").arg(map->name); + text += QString("\t.include \"data/maps/%1/text.inc\"\n").arg(map->name); + appendTextFile(root + "/data/event_scripts.s", text); + + // Simply append to data/map_events.s. + text = QString("\n\t.include \"data/maps/%1/events.inc\"\n").arg(map->name); + appendTextFile(root + "/data/map_events.s", text); + + // Simply append to data/maps/headers.inc. + text = QString("\t.include \"data/maps/%1/header.inc\"\n").arg(map->name); + appendTextFile(root + "/data/maps/headers.inc", text); + + // Simply append to data/layouts.inc. + text = QString("\t.include \"data/layouts/%1/layout.inc\"\n").arg(map->layout->name); + appendTextFile(root + "/data/layouts.inc", text); + } + + saveMapBorder(map); + saveMapHeader(map); + saveMapConnections(map); + saveBlockdata(map); + saveMapEvents(map); + + // Update global data structures with current map data. + updateMapLayout(map); + + map->isPersistedToFile = true; + map->layout->has_unsaved_changes = false; +} + +void Project::updateMapLayout(Map* map) { + if (!mapLayoutsTableMaster.contains(map->layout_label)) { + mapLayoutsTableMaster.append(map->layout_label); + } + + // Deep copy + MapLayout *layout = mapLayouts.value(map->layout_label); + MapLayout *newLayout = new MapLayout(); + *newLayout = *layout; + mapLayoutsMaster.insert(map->layout_label, newLayout); +} + +void Project::saveAllDataStructures() { + saveMapLayoutsTable(); + saveAllMapLayouts(); + saveMapGroupsTable(); + saveMapConstantsHeader(); + saveMapsWithConnections(); +} + +void Project::loadTilesetAssets(Tileset* tileset) { + ParseUtil* parser = new ParseUtil; + QString category = (tileset->is_secondary == "TRUE") ? "secondary" : "primary"; + if (tileset->name.isNull()) { + return; + } + QString tilesetName = tileset->name; + QString dir_path = root + "/data/tilesets/" + category + "/" + tilesetName.replace("gTileset_", "").toLower(); + + QString graphics_text = readTextFile(root + "/data/tilesets/graphics.inc"); + QList *graphics = parser->parseAsm(graphics_text); + QStringList *tiles_values = getLabelValues(graphics, tileset->tiles_label); + QStringList *palettes_values = getLabelValues(graphics, tileset->palettes_label); + + QString tiles_path; + if (!tiles_values->isEmpty()) { + tiles_path = root + "/" + tiles_values->value(0).section('"', 1, 1); + } else { + tiles_path = dir_path + "/tiles.4bpp"; + if (tileset->is_compressed == "TRUE") { + tiles_path += ".lz"; + } + } + + if (!palettes_values->isEmpty()) { + for (int i = 0; i < palettes_values->length(); i++) { + QString value = palettes_values->value(i); + tileset->palettePaths.append(this->fixPalettePath(root + "/" + value.section('"', 1, 1))); + } + } else { + QString palettes_dir_path = dir_path + "/palettes"; + for (int i = 0; i < 16; i++) { + tileset->palettePaths.append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".pal"); + } + } + + QString metatiles_text = readTextFile(root + "/data/tilesets/metatiles.inc"); + QList *metatiles_macros = parser->parseAsm(metatiles_text); + QStringList *metatiles_values = getLabelValues(metatiles_macros, tileset->metatiles_label); + if (!metatiles_values->isEmpty()) { + tileset->metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1); + } else { + tileset->metatiles_path = dir_path + "/metatiles.bin"; + } + QStringList *metatile_attrs_values = getLabelValues(metatiles_macros, tileset->metatile_attrs_label); + if (!metatile_attrs_values->isEmpty()) { + tileset->metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1); + } else { + tileset->metatile_attrs_path = dir_path + "/metatile_attributes.bin"; + } + + tiles_path = fixGraphicPath(tiles_path); + tileset->tilesImagePath = tiles_path; + QImage image = QImage(tileset->tilesImagePath); + this->loadTilesetTiles(tileset, image); + this->loadTilesetMetatiles(tileset); + + // palettes + QList> *palettes = new QList>; + for (int i = 0; i < tileset->palettePaths.length(); i++) { + QList palette; + QString path = tileset->palettePaths.value(i); + QString text = readTextFile(path); + if (!text.isNull()) { + QStringList lines = text.split(QRegExp("[\r\n]"),QString::SkipEmptyParts); + if (lines.length() == 19 && lines[0] == "JASC-PAL" && lines[1] == "0100" && lines[2] == "16") { + for (int j = 0; j < 16; j++) { + QStringList rgb = lines[j + 3].split(QRegExp(" "), QString::SkipEmptyParts); + if (rgb.length() != 3) { + qDebug() << QString("Invalid tileset palette RGB value: '%1'").arg(lines[j + 3]); + palette.append(qRgb((j - 3) * 16, (j - 3) * 16, (j - 3) * 16)); + } else { + int red = rgb[0].toInt(); + int green = rgb[1].toInt(); + int blue = rgb[2].toInt(); + QRgb color = qRgb(red, green, blue); + palette.append(color); + } + } + } else { + qDebug() << QString("Invalid JASC-PAL palette file for tileset."); + for (int j = 0; j < 16; j++) { + palette.append(qRgb(j * 16, j * 16, j * 16)); + } + } + } else { + for (int j = 0; j < 16; j++) { + palette.append(qRgb(j * 16, j * 16, j * 16)); + } + qDebug() << QString("Could not open palette path '%1'").arg(path); + } + + palettes->append(palette); + } + tileset->palettes = palettes; +} + +void Project::loadTilesetTiles(Tileset *tileset, QImage image) { + QList *tiles = new QList; + int w = 8; + int h = 8; + for (int y = 0; y < image.height(); y += h) + for (int x = 0; x < image.width(); x += w) { + QImage tile = image.copy(x, y, w, h); + tiles->append(tile); + } + tileset->tilesImage = image; + tileset->tiles = tiles; +} + +void Project::loadTilesetMetatiles(Tileset* tileset) { + QFile metatiles_file(tileset->metatiles_path); + if (metatiles_file.open(QIODevice::ReadOnly)) { + QByteArray data = metatiles_file.readAll(); + int num_metatiles = data.length() / 16; + int num_layers = 2; + QList *metatiles = new QList; + for (int i = 0; i < num_metatiles; i++) { + Metatile *metatile = new Metatile; + int index = i * (2 * 4 * num_layers); + for (int j = 0; j < 4 * num_layers; j++) { + uint16_t word = data[index++] & 0xff; + word += (data[index++] & 0xff) << 8; + Tile tile; + tile.tile = word & 0x3ff; + tile.xflip = (word >> 10) & 1; + tile.yflip = (word >> 11) & 1; + tile.palette = (word >> 12) & 0xf; + metatile->tiles->append(tile); + } + metatiles->append(metatile); + } + tileset->metatiles = metatiles; + } else { + tileset->metatiles = new QList; + qDebug() << QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path); + } + + QFile attrs_file(tileset->metatile_attrs_path); + if (attrs_file.open(QIODevice::ReadOnly)) { + QByteArray data = attrs_file.readAll(); + int num_metatiles = tileset->metatiles->count(); + int num_metatileAttrs = data.length() / 2; + if (num_metatiles != num_metatileAttrs) { + qDebug() << QString("Metatile count %1 does not match metatile attribute count %2").arg(num_metatiles).arg(num_metatileAttrs); + if (num_metatiles > num_metatileAttrs) + num_metatiles = num_metatileAttrs; + } + for (int i = 0; i < num_metatileAttrs; i++) { + int value = (static_cast(data.at(i * 2 + 1)) << 8) | static_cast(data.at(i * 2)); + tileset->metatiles->at(i)->behavior = value & 0xFF; + tileset->metatiles->at(i)->layerType = (value & 0xF000) >> 12; + } + } else { + qDebug() << QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path); + } +} + +Blockdata* Project::readBlockdata(QString path) { + Blockdata *blockdata = new Blockdata; + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + QByteArray data = file.readAll(); + for (int i = 0; (i + 1) < data.length(); i += 2) { + uint16_t word = static_cast((data[i] & 0xff) + ((data[i + 1] & 0xff) << 8)); + blockdata->addBlock(word); + } + } else { + qDebug() << "Failed to open blockdata path '" << path << "'"; + } + + return blockdata; +} + +Map* Project::getMap(QString map_name) { + if (map_cache->contains(map_name)) { + return map_cache->value(map_name); + } else { + Map *map = loadMap(map_name); + return map; + } +} + +Tileset* Project::getTileset(QString label, bool forceLoad) { + Tileset *existingTileset = nullptr; + if (tileset_cache->contains(label)) { + existingTileset = tileset_cache->value(label); + } + + if (existingTileset && !forceLoad) { + return tileset_cache->value(label); + } else { + Tileset *tileset = loadTileset(label, existingTileset); + return tileset; + } +} + +QString Project::readTextFile(QString path) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + //QMessageBox::information(0, "Error", QString("Could not open '%1': ").arg(path) + file.errorString()); + qDebug() << QString("Could not open '%1': ").arg(path) + file.errorString(); + return QString(); + } + QTextStream in(&file); + QString text = ""; + while (!in.atEnd()) { + text += in.readLine() + "\n"; + } + return text; +} + +void Project::saveTextFile(QString path, QString text) { + QFile file(path); + if (file.open(QIODevice::WriteOnly)) { + file.write(text.toUtf8()); + } else { + qDebug() << QString("Could not open '%1' for writing: ").arg(path) + file.errorString(); + } +} + +void Project::appendTextFile(QString path, QString text) { + QFile file(path); + if (file.open(QIODevice::Append)) { + file.write(text.toUtf8()); + } else { + qDebug() << QString("Could not open '%1' for appending: ").arg(path) + file.errorString(); + } +} + +void Project::deleteFile(QString path) { + QFile file(path); + if (file.exists() && !file.remove()) { + qDebug() << QString("Could not delete file '%1': ").arg(path) + file.errorString(); + } +} + +void Project::readMapGroups() { + QString text = readTextFile(root + "/data/maps/groups.inc"); + if (text.isNull()) { + return; + } + ParseUtil *parser = new ParseUtil; + QList *commands = parser->parseAsm(text); + + bool in_group_pointers = false; + QStringList *groups = new QStringList; + for (int i = 0; i < commands->length(); i++) { + QStringList params = commands->value(i); + QString macro = params.value(0); + if (macro == ".label") { + if (in_group_pointers) { + break; + } + if (params.value(1) == "gMapGroups") { + in_group_pointers = true; + } + } else if (macro == ".4byte") { + if (in_group_pointers) { + for (int j = 1; j < params.length(); j++) { + groups->append(params.value(j)); + } + } + } + } + + QList groupedMaps; + for (int i = 0; i < groups->length(); i++) { + groupedMaps.append(QStringList()); + } + + QStringList *maps = new QStringList; + int group = -1; + for (int i = 0; i < commands->length(); i++) { + QStringList params = commands->value(i); + QString macro = params.value(0); + if (macro == ".label") { + group = groups->indexOf(params.value(1)); + } else if (macro == ".4byte") { + if (group != -1) { + for (int j = 1; j < params.length(); j++) { + QString mapName = params.value(j); + groupedMaps[group].append(mapName); + maps->append(mapName); + map_groups->insert(mapName, group); + + // Build the mapping and reverse mapping between map constants and map names. + QString mapConstant = Map::mapConstantFromName(mapName); + mapConstantsToMapNames->insert(mapConstant, mapName); + mapNamesToMapConstants->insert(mapName, mapConstant); + } + } + } + } + + mapConstantsToMapNames->insert(NONE_MAP_CONSTANT, NONE_MAP_NAME); + mapNamesToMapConstants->insert(NONE_MAP_NAME, NONE_MAP_CONSTANT); + maps->append(NONE_MAP_NAME); + + groupNames = groups; + groupedMapNames = groupedMaps; + mapNames = maps; + + QString hltext = readTextFile(root + QString("/src/data/heal_locations.h")); + QList* hl = parser->parseHealLocs(hltext); + flyableMaps = *hl; + delete hl; +} + +Map* Project::addNewMapToGroup(QString mapName, int groupNum) { + // Setup new map in memory, but don't write to file until map is actually saved later. + mapNames->append(mapName); + map_groups->insert(mapName, groupNum); + groupedMapNames[groupNum].append(mapName); + + Map *map = new Map; + map->isPersistedToFile = false; + map->setName(mapName); + mapConstantsToMapNames->insert(map->constantName, map->name); + mapNamesToMapConstants->insert(map->name, map->constantName); + setNewMapHeader(map, mapLayoutsTable.size() + 1); + setNewMapLayout(map); + loadMapTilesets(map); + setNewMapBlockdata(map); + setNewMapBorder(map); + setNewMapEvents(map); + setNewMapConnections(map); + map->commit(); + map->metatileHistory.save(); + map_cache->insert(mapName, map); + + return map; +} + +QString Project::getNewMapName() { + // Ensure default name doesn't already exist. + int i = 0; + QString newMapName; + do { + newMapName = QString("NewMap%1").arg(++i); + } while (mapNames->contains(newMapName)); + + return newMapName; +} + +QList* Project::parseAsm(QString text) { + ParseUtil *parser = new ParseUtil; + return parser->parseAsm(text); +} + +QStringList Project::getVisibilities() { + // TODO + QStringList names; + for (int i = 0; i < 16; i++) { + names.append(QString("%1").arg(i)); + } + return names; +} + +QMap Project::getTilesets() { + QMap allTilesets; + QStringList primaryTilesets; + QStringList secondaryTilesets; + allTilesets.insert("primary", primaryTilesets); + allTilesets.insert("secondary", secondaryTilesets); + QString headers_text = readTextFile(root + "/data/tilesets/headers.inc"); + QList* commands = parseAsm(headers_text); + int i = 0; + while (i < commands->length()) { + if (commands->at(i).length() != 2) + continue; + + if (commands->at(i).at(0) == ".label") { + QString tilesetLabel = commands->at(i).at(1); + // Advance to command specifying whether or not it is a secondary tileset + i += 2; + if (commands->at(i).at(0) != ".byte") { + qDebug() << "Unexpected command found for secondary tileset flag in tileset" << tilesetLabel << ". Expected '.byte', but found: " << commands->at(i).at(0); + continue; + } + + QString secondaryTilesetValue = commands->at(i).at(1); + if (secondaryTilesetValue != "TRUE" && secondaryTilesetValue != "FALSE" && secondaryTilesetValue != "0" && secondaryTilesetValue != "1") { + qDebug() << "Unexpected secondary tileset flag found. Expected \"TRUE\", \"FALSE\", \"0\", or \"1\", but found: " << secondaryTilesetValue; + continue; + } + + bool isSecondaryTileset = (secondaryTilesetValue == "TRUE" || secondaryTilesetValue == "1"); + if (isSecondaryTileset) + allTilesets["secondary"].append(tilesetLabel); + else + allTilesets["primary"].append(tilesetLabel); + } + + i++; + } + + return allTilesets; +} + +void Project::readTilesetProperties() { + QStringList defines; + QString text = readTextFile(root + "/include/fieldmap.h"); + if (!text.isNull()) { + bool error = false; + QStringList definePrefixes; + definePrefixes << "NUM_"; + QMap defines = readCDefines(text, definePrefixes); + auto it = defines.find("NUM_TILES_IN_PRIMARY"); + if (it != defines.end()) { + Project::num_tiles_primary = it.value(); + } + else { + error = true; + } + it = defines.find("NUM_TILES_TOTAL"); + if (it != defines.end()) { + Project::num_tiles_total = it.value(); + } + else { + error = true; + } + it = defines.find("NUM_METATILES_IN_PRIMARY"); + if (it != defines.end()) { + Project::num_metatiles_primary = it.value(); + } + else { + error = true; + } + it = defines.find("NUM_METATILES_TOTAL"); + if (it != defines.end()) { + Project::num_metatiles_total = it.value(); + } + else { + error = true; + } + it = defines.find("NUM_PALS_IN_PRIMARY"); + if (it != defines.end()) { + Project::num_pals_primary = it.value(); + } + else { + error = true; + } + it = defines.find("NUM_PALS_TOTAL"); + if (it != defines.end()) { + Project::num_pals_total = it.value(); + } + else { + error = true; + } + + if (error) + { + qDebug() << "Some global tileset values could not be loaded. Using default values"; + } + } +} + +void Project::readRegionMapSections() { + QString filepath = root + "/include/constants/region_map_sections.h"; + QStringList prefixes = (QStringList() << "MAPSEC_"); + readCDefinesSorted(filepath, prefixes, regionMapSections); +} + +void Project::readItemNames() { + QString filepath = root + "/include/constants/items.h"; + QStringList prefixes = (QStringList() << "ITEM_"); + readCDefinesSorted(filepath, prefixes, itemNames); +} + +void Project::readFlagNames() { + QString filepath = root + "/include/constants/flags.h"; + QStringList prefixes = (QStringList() << "FLAG_"); + readCDefinesSorted(filepath, prefixes, flagNames); +} + +void Project::readVarNames() { + QString filepath = root + "/include/constants/vars.h"; + QStringList prefixes = (QStringList() << "VAR_"); + readCDefinesSorted(filepath, prefixes, varNames); +} + +void Project::readMovementTypes() { + QString filepath = root + "/include/constants/event_object_movement_constants.h"; + QStringList prefixes = (QStringList() << "MOVEMENT_TYPE_"); + readCDefinesSorted(filepath, prefixes, movementTypes); +} + +void Project::readMapTypes() { + QString filepath = root + "/include/constants/map_types.h"; + QStringList prefixes = (QStringList() << "MAP_TYPE_"); + readCDefinesSorted(filepath, prefixes, mapTypes); +} + +void Project::readMapBattleScenes() { + QString filepath = root + "/include/constants/map_types.h"; + QStringList prefixes = (QStringList() << "MAP_BATTLE_SCENE_"); + readCDefinesSorted(filepath, prefixes, mapBattleScenes); +} + +void Project::readWeatherNames() { + QString filepath = root + "/include/constants/weather.h"; + QStringList prefixes = (QStringList() << "WEATHER_"); + readCDefinesSorted(filepath, prefixes, weatherNames); +} + +void Project::readCoordEventWeatherNames() { + QString filepath = root + "/include/constants/weather.h"; + QStringList prefixes = (QStringList() << "COORD_EVENT_WEATHER_"); + readCDefinesSorted(filepath, prefixes, coordEventWeatherNames); +} + +void Project::readSecretBaseIds() { + QString filepath = root + "/include/constants/secret_bases.h"; + QStringList prefixes = (QStringList() << "SECRET_BASE_"); + readCDefinesSorted(filepath, prefixes, secretBaseIds); +} + +void Project::readBgEventFacingDirections() { + QString filepath = root + "/include/constants/bg_event_constants.h"; + QStringList prefixes = (QStringList() << "BG_EVENT_PLAYER_FACING_"); + readCDefinesSorted(filepath, prefixes, bgEventFacingDirections); +} + +void Project::readMetatileBehaviors() { + this->metatileBehaviorMap.clear(); + this->metatileBehaviorMapInverse.clear(); + QString filepath = root + "/include/constants/metatile_behaviors.h"; + QString text = readTextFile(filepath); + if (!text.isNull()) { + QStringList prefixes = (QStringList() << "MB_"); + this->metatileBehaviorMap = readCDefines(text, prefixes); + for (QString defineName : this->metatileBehaviorMap.keys()) { + this->metatileBehaviorMapInverse.insert(this->metatileBehaviorMap[defineName], defineName); + } + } else { + qDebug() << "Failed to read C defines file: " << filepath; + } +} + +void Project::readCDefinesSorted(QString filepath, QStringList prefixes, QStringList* definesToSet) { + QString text = readTextFile(filepath); + if (!text.isNull()) { + QMap defines = readCDefines(text, prefixes); + + // The defines should to be sorted by their underlying value, not alphabetically. + // Reverse the map and read out the resulting keys in order. + QMultiMap definesInverse; + for (QString defineName : defines.keys()) { + definesInverse.insert(defines[defineName], defineName); + } + *definesToSet = definesInverse.values(); + } else { + qDebug() << "Failed to read C defines file: " << filepath; + } +} + +void Project::readMapsWithConnections() { + QString path = root + "/data/maps/connections.inc"; + QString text = readTextFile(path); + if (text.isNull()) { + return; + } + + mapsWithConnections.clear(); + QRegularExpression re("data\\/maps\\/(?\\w+)\\/connections.inc"); + QList* includes = parseAsm(text); + for (QStringList values : *includes) { + if (values.length() != 2) + continue; + + QRegularExpressionMatch match = re.match(values.value(1)); + if (match.hasMatch()) { + QString mapName = match.captured("mapName"); + mapsWithConnections.append(mapName); + } + } +} + +void Project::saveMapsWithConnections() { + QString path = root + "/data/maps/connections.inc"; + QString text = ""; + for (QString mapName : mapsWithConnections) { + if (mapNamesToMapConstants->contains(mapName)) { + text += QString("\t.include \"data/maps/%1/connections.inc\"\n").arg(mapName); + } else { + qDebug() << QString("Failed to write connection include. %1 not a valid map name").arg(mapName); + } + } + saveTextFile(path, text); +} + +QStringList Project::getSongNames() { + QStringList names; + QString text = readTextFile(root + "/include/constants/songs.h"); + if (!text.isNull()) { + QStringList songDefinePrefixes; + songDefinePrefixes << "SE_" << "MUS_"; + QMap songDefines = readCDefines(text, songDefinePrefixes); + names = songDefines.keys(); + } + return names; +} + +QMap Project::getEventObjGfxConstants() { + QMap constants; + QString text = readTextFile(root + "/include/constants/event_objects.h"); + if (!text.isNull()) { + QStringList eventObjGfxPrefixes; + eventObjGfxPrefixes << "EVENT_OBJ_GFX_"; + constants = readCDefines(text, eventObjGfxPrefixes); + } + return constants; +} + +QString Project::fixPalettePath(QString path) { + path = path.replace(QRegExp("\\.gbapal$"), ".pal"); + return path; +} + +QString Project::fixGraphicPath(QString path) { + path = path.replace(QRegExp("\\.lz$"), ""); + path = path.replace(QRegExp("\\.[1248]bpp$"), ".png"); + return path; +} + +void Project::loadEventPixmaps(QList objects) { + bool needs_update = false; + for (Event *object : objects) { + if (object->pixmap.isNull()) { + needs_update = true; + break; + } + } + if (!needs_update) { + return; + } + + QMap constants = getEventObjGfxConstants(); + + QString pointers_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics_info_pointers.h"); + QString info_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics_info.h"); + QString pic_text = readTextFile(root + "/src/data/field_event_obj/event_object_pic_tables.h"); + QString assets_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics.h"); + + QStringList pointers = readCArray(pointers_text, "gEventObjectGraphicsInfoPointers"); + + for (Event *object : objects) { + if (!object->pixmap.isNull()) { + continue; + } + + object->spriteWidth = 16; + object->spriteHeight = 16; + QString event_type = object->get("event_type"); + if (event_type == EventType::Object) { + object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(0, 0, 16, 16); + } else if (event_type == EventType::Warp) { + object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(16, 0, 16, 16); + } else if (event_type == EventType::CoordScript || event_type == EventType::CoordWeather) { + object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(32, 0, 16, 16); + } else if (event_type == EventType::Sign || event_type == EventType::HiddenItem || event_type == EventType::SecretBase) { + object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(48, 0, 16, 16); + } else if (event_type == EventType::HealLocation) { + object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(64, 0, 16, 16); + } + + if (event_type == EventType::Object) { + int sprite_id = constants.value(object->get("sprite")); + + QString info_label = pointers.value(sprite_id).replace("&", ""); + QStringList gfx_info = readCArray(info_text, info_label); + QString pic_label = gfx_info.value(14); + QString dimensions_label = gfx_info.value(11); + QString subsprites_label = gfx_info.value(12); + QString gfx_label = readCArray(pic_text, pic_label).value(0); + gfx_label = gfx_label.section(QRegExp("[\\(\\)]"), 1, 1); + QString path = readCIncbin(assets_text, gfx_label); + + if (!path.isNull()) { + path = fixGraphicPath(path); + QImage spritesheet(root + "/" + path); + if (!spritesheet.isNull()) { + // Infer the sprite dimensions from the OAM labels. + int spriteWidth = spritesheet.width(); + int spriteHeight = spritesheet.height(); + QRegularExpression re("\\S+_(\\d+)x(\\d+)"); + QRegularExpressionMatch dimensionMatch = re.match(dimensions_label); + if (dimensionMatch.hasMatch()) { + QRegularExpressionMatch oamTablesMatch = re.match(subsprites_label); + if (oamTablesMatch.hasMatch()) { + spriteWidth = dimensionMatch.captured(1).toInt(); + spriteHeight = dimensionMatch.captured(2).toInt(); + } + } + object->setPixmapFromSpritesheet(spritesheet, spriteWidth, spriteHeight); + } + } + } + } +} + +void Project::saveMapEvents(Map *map) { + QString path = root + QString("/data/maps/%1/events.inc").arg(map->name); + QString text = ""; + QString objectEventsLabel = "0x0"; + QString warpEventsLabel = "0x0"; + QString coordEventsLabel = "0x0"; + QString bgEventsLabel = "0x0"; + + if (map->events["object_event_group"].length() > 0) { + objectEventsLabel = Map::objectEventsLabelFromName(map->name); + text += QString("%1::\n").arg(objectEventsLabel); + for (int i = 0; i < map->events["object_event_group"].length(); i++) { + Event *object_event = map->events["object_event_group"].value(i); + text += object_event->buildObjectEventMacro(i); + } + text += "\n"; + } + + if (map->events["warp_event_group"].length() > 0) { + warpEventsLabel = Map::warpEventsLabelFromName(map->name); + text += QString("%1::\n").arg(warpEventsLabel); + for (Event *warp : map->events["warp_event_group"]) { + text += warp->buildWarpEventMacro(mapNamesToMapConstants); + } + text += "\n"; + } + + if (map->events["coord_event_group"].length() > 0) { + coordEventsLabel = Map::coordEventsLabelFromName(map->name); + text += QString("%1::\n").arg(coordEventsLabel); + for (Event *event : map->events["coord_event_group"]) { + QString event_type = event->get("event_type"); + if (event_type == EventType::CoordScript) { + text += event->buildCoordScriptEventMacro(); + } else if (event_type == EventType::CoordWeather) { + text += event->buildCoordWeatherEventMacro(); + } + } + text += "\n"; + } + + if (map->events["bg_event_group"].length() > 0) + { + bgEventsLabel = Map::bgEventsLabelFromName(map->name); + text += QString("%1::\n").arg(bgEventsLabel); + for (Event *event : map->events["bg_event_group"]) { + QString event_type = event->get("event_type"); + if (event_type == EventType::Sign) { + text += event->buildSignEventMacro(); + } else if (event_type == EventType::HiddenItem) { + text += event->buildHiddenItemEventMacro(); + } else if (event_type == EventType::SecretBase) { + text += event->buildSecretBaseEventMacro(); + } + } + text += "\n"; + } + + text += QString("%1::\n").arg(map->events_label); + text += QString("\tmap_events %1, %2, %3, %4\n") + .arg(objectEventsLabel) + .arg(warpEventsLabel) + .arg(coordEventsLabel) + .arg(bgEventsLabel); + + saveTextFile(path, text); + + // save heal event changes + if (map->events["heal_event_group"].length() > 0) { + for (Event *healEvent : map->events["heal_event_group"]) { + HealLocation hl = HealLocation::fromEvent(healEvent); + flyableMaps[hl.index - 1] = hl; + } + } + saveHealLocationStruct(map); +} + +void Project::readMapEvents(Map *map) { + if (!map->isPersistedToFile) { + return; + } + + // lazy + QString path = root + QString("/data/maps/%1/events.inc").arg(map->name); + QString text = readTextFile(path); + if (text.isNull()) { + return; + } + + QStringList *labels = getLabelValues(parseAsm(text), map->events_label); + QString objectEventsLabel = labels->value(0); + QString warpEventsLabel = labels->value(1); + QString coordEventsLabel = labels->value(2); + QString bgEventsLabel = labels->value(3); + + QList *object_events = getLabelMacros(parseAsm(text), objectEventsLabel); + map->events["object_event_group"].clear(); + for (QStringList command : *object_events) { + if (command.value(0) == "object_event") { + Event *object = new Event; + object->put("map_name", map->name); + int i = 2; + object->put("sprite", command.value(i++)); + object->put("replacement", command.value(i++)); + object->put("x", command.value(i++).toInt(nullptr, 0)); + object->put("y", command.value(i++).toInt(nullptr, 0)); + object->put("elevation", command.value(i++)); + object->put("movement_type", command.value(i++)); + object->put("radius_x", command.value(i++).toInt(nullptr, 0)); + object->put("radius_y", command.value(i++).toInt(nullptr, 0)); + object->put("is_trainer", command.value(i++)); + object->put("sight_radius_tree_id", command.value(i++)); + object->put("script_label", command.value(i++)); + object->put("event_flag", command.value(i++)); + object->put("event_group_type", "object_event_group"); + object->put("event_type", EventType::Object); + map->events["object_event_group"].append(object); + } + } + + QList *warps = getLabelMacros(parseAsm(text), warpEventsLabel); + map->events["warp_event_group"].clear(); + for (QStringList command : *warps) { + if (command.value(0) == "warp_def") { + Event *warp = new Event; + warp->put("map_name", map->name); + int i = 1; + warp->put("x", command.value(i++)); + warp->put("y", command.value(i++)); + warp->put("elevation", command.value(i++)); + warp->put("destination_warp", command.value(i++)); + + // Ensure the warp destination map constant is valid before adding it to the warps. + QString mapConstant = command.value(i++); + if (mapConstantsToMapNames->contains(mapConstant)) { + warp->put("destination_map_name", mapConstantsToMapNames->value(mapConstant)); + warp->put("event_group_type", "warp_event_group"); + warp->put("event_type", EventType::Warp); + map->events["warp_event_group"].append(warp); + } else if (mapConstant == NONE_MAP_CONSTANT) { + warp->put("destination_map_name", NONE_MAP_NAME); + warp->put("event_group_type", "warp_event_group"); + warp->put("event_type", EventType::Warp); + map->events["warp_event_group"].append(warp); + } else { + qDebug() << QString("Destination map constant '%1' is invalid for warp").arg(mapConstant); + } + } + } + + map->events["heal_event_group"].clear(); + + for (auto it = flyableMaps.begin(); it != flyableMaps.end(); it++) { + + HealLocation loc = *it; + + //if TRUE map is flyable / has healing location + if (loc.name == QString(mapNamesToMapConstants->value(map->name)).remove(0,4)) { + Event *heal = new Event; + heal->put("map_name", map->name); + heal->put("x", loc.x); + heal->put("y", loc.y); + heal->put("loc_name", loc.name); + heal->put("index", loc.index); + heal->put("elevation", 3); // TODO: change this? + heal->put("destination_map_name", mapConstantsToMapNames->value(map->name)); + heal->put("event_group_type", "heal_event_group"); + heal->put("event_type", EventType::HealLocation); + map->events["heal_event_group"].append(heal); + } + + } + + QList *coords = getLabelMacros(parseAsm(text), coordEventsLabel); + map->events["coord_event_group"].clear(); + for (QStringList command : *coords) { + if (command.value(0) == "coord_event") { + Event *coord = new Event; + coord->put("map_name", map->name); + int i = 1; + coord->put("x", command.value(i++)); + coord->put("y", command.value(i++)); + coord->put("elevation", command.value(i++)); + coord->put("script_var", command.value(i++)); + coord->put("script_var_value", command.value(i++)); + coord->put("script_label", command.value(i++)); + coord->put("event_group_type", "coord_event_group"); + coord->put("event_type", EventType::CoordScript); + map->events["coord_event_group"].append(coord); + } else if (command.value(0) == "coord_weather_event") { + Event *coord = new Event; + coord->put("map_name", map->name); + int i = 1; + coord->put("x", command.value(i++)); + coord->put("y", command.value(i++)); + coord->put("elevation", command.value(i++)); + coord->put("weather", command.value(i++)); + coord->put("event_group_type", "coord_event_group"); + coord->put("event_type", EventType::CoordWeather); + map->events["coord_event_group"].append(coord); + } + } + + QList *bgs = getLabelMacros(parseAsm(text), bgEventsLabel); + map->events["bg_event_group"].clear(); + for (QStringList command : *bgs) { + if (command.value(0) == "bg_event") { + Event *bg = new Event; + bg->put("map_name", map->name); + int i = 1; + bg->put("x", command.value(i++)); + bg->put("y", command.value(i++)); + bg->put("elevation", command.value(i++)); + bg->put("player_facing_direction", command.value(i++)); + bg->put("script_label", command.value(i++)); + //sign_unknown7 + bg->put("event_group_type", "bg_event_group"); + bg->put("event_type", EventType::Sign); + map->events["bg_event_group"].append(bg); + } else if (command.value(0) == "bg_hidden_item_event") { + Event *bg = new Event; + bg->put("map_name", map->name); + int i = 1; + bg->put("x", command.value(i++)); + bg->put("y", command.value(i++)); + bg->put("elevation", command.value(i++)); + bg->put("item", command.value(i++)); + bg->put("flag", command.value(i++)); + bg->put("event_group_type", "bg_event_group"); + bg->put("event_type", EventType::HiddenItem); + map->events["bg_event_group"].append(bg); + } else if (command.value(0) == "bg_secret_base_event") { + Event *bg = new Event; + bg->put("map_name", map->name); + int i = 1; + bg->put("x", command.value(i++)); + bg->put("y", command.value(i++)); + bg->put("elevation", command.value(i++)); + bg->put("secret_base_id", command.value(i++)); + bg->put("event_group_type", "bg_event_group"); + bg->put("event_type", EventType::SecretBase); + map->events["bg_event_group"].append(bg); + } + } +} + +void Project::setNewMapEvents(Map *map) { + map->events["object_event_group"].clear(); + map->events["warp_event_group"].clear(); + map->events["heal_event_group"].clear(); + map->events["coord_event_group"].clear(); + map->events["bg_event_group"].clear(); +} + +QStringList Project::readCArray(QString text, QString label) { + QStringList list; + + if (label.isNull()) { + return list; + } + + QRegExp *re = new QRegExp(QString("\\b%1\\b\\s*\\[?\\s*\\]?\\s*=\\s*\\{([^\\}]*)\\}").arg(label)); + int pos = re->indexIn(text); + if (pos != -1) { + QString body = re->cap(1); + body = body.replace(QRegExp("\\s*"), ""); + list = body.split(','); + /* + QRegExp *inner = new QRegExp("&?\\b([A-Za-z0-9_\\(\\)]*)\\b,"); + int pos = 0; + while ((pos = inner->indexIn(body, pos)) != -1) { + list << inner->cap(1); + pos += inner->matchedLength(); + } + */ + } + + return list; +} + +QString Project::readCIncbin(QString text, QString label) { + QString path; + + if (label.isNull()) { + return path; + } + + QRegExp *re = new QRegExp(QString( + "\\b%1\\b" + "\\s*\\[?\\s*\\]?\\s*=\\s*" + "INCBIN_[US][0-9][0-9]?" + "\\(\"([^\"]*)\"\\)").arg(label)); + + int pos = re->indexIn(text); + if (pos != -1) { + path = re->cap(1); + } + + return path; +} + +QMap Project::readCDefines(QString text, QStringList prefixes) { + ParseUtil parser; + QMap allDefines; + QMap filteredDefines; + QRegularExpression re("#define\\s+(?\\w+)[^\\S\\n]+(?.+)"); + QRegularExpressionMatchIterator iter = re.globalMatch(text); + while (iter.hasNext()) { + QRegularExpressionMatch match = iter.next(); + QString name = match.captured("defineName"); + QString expression = match.captured("defineValue"); + expression.replace(QRegularExpression("//.*"), ""); + int value = parser.evaluateDefine(expression, &allDefines); + allDefines.insert(name, value); + for (QString prefix : prefixes) { + if (name.startsWith(prefix)) { + filteredDefines.insert(name, value); + } + } + } + return filteredDefines; +} + +int Project::getNumTilesPrimary() +{ + return Project::num_tiles_primary; +} + +int Project::getNumTilesTotal() +{ + return Project::num_tiles_total; +} + +int Project::getNumMetatilesPrimary() +{ + return Project::num_metatiles_primary; +} + +int Project::getNumMetatilesTotal() +{ + return Project::num_metatiles_total; +} + +int Project::getNumPalettesPrimary() +{ + return Project::num_pals_primary; +} + +int Project::getNumPalettesTotal() +{ + return Project::num_pals_total; +} diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 00000000..27ddf83c --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,6 @@ +#include "settings.h" + +Settings::Settings() +{ + +} diff --git a/src/ui/bordermetatilespixmapitem.cpp b/src/ui/bordermetatilespixmapitem.cpp new file mode 100644 index 00000000..4886e42e --- /dev/null +++ b/src/ui/bordermetatilespixmapitem.cpp @@ -0,0 +1,42 @@ +#include "bordermetatilespixmapitem.h" +#include "imageproviders.h" +#include + +void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + + for (int i = 0; i < selectionDimensions.x() && (i + x) < 2; i++) { + for (int j = 0; j < selectionDimensions.y() && (j + y) < 2; j++) { + int blockIndex = (j + y) * 2 + (i + x); + uint16_t tile = selectedMetatiles->at(j * selectionDimensions.x() + i); + (*map->layout->border->blocks)[blockIndex].tile = tile; + } + } + + draw(); + emit borderMetatilesChanged(); +} + +void BorderMetatilesPixmapItem::draw() { + QImage image(32, 32, QImage::Format_RGBA8888); + QPainter painter(&image); + QList *blocks = map->layout->border->blocks; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + int x = i * 16; + int y = j * 16; + int index = j * 2 + i; + QImage metatile_image = getMetatileImage(blocks->value(index).tile, map->layout->tileset_primary, map->layout->tileset_secondary); + QPoint metatile_origin = QPoint(x, y); + painter.drawImage(metatile_origin, metatile_image); + } + } + + painter.end(); + this->setPixmap(QPixmap::fromImage(image)); +} diff --git a/src/ui/collisionpixmapitem.cpp b/src/ui/collisionpixmapitem.cpp new file mode 100644 index 00000000..839cf24d --- /dev/null +++ b/src/ui/collisionpixmapitem.cpp @@ -0,0 +1,86 @@ +#include "collisionpixmapitem.h" + +void CollisionPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + int x = static_cast(event->pos().x()) / 16; + int y = static_cast(event->pos().y()) / 16; + emit this->hoveredMapMovementPermissionChanged(x, y); + if (this->settings->betterCursors){ + setCursor(this->settings->mapCursor); + } +} +void CollisionPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { + emit this->hoveredMapMovementPermissionCleared(); + if (this->settings->betterCursors){ + unsetCursor(); + } +} +void CollisionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} +void CollisionPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} +void CollisionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} + +void CollisionPixmapItem::draw(bool ignoreCache) { + if (map) { + setPixmap(map->renderCollision(ignoreCache)); + } +} + +void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { + if (map) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + Block *block = map->getBlock(x, y); + if (block) { + block->collision = this->movementPermissionsSelector->getSelectedCollision(); + block->elevation = this->movementPermissionsSelector->getSelectedElevation(); + map->_setBlock(x, y, *block); + } + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + map->commit(); + } + draw(); + } +} + +void CollisionPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { + if (map) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + uint16_t collision = this->movementPermissionsSelector->getSelectedCollision(); + uint16_t elevation = this->movementPermissionsSelector->getSelectedElevation(); + map->floodFillCollisionElevation(x, y, collision, elevation); + draw(); + } +} + +void CollisionPixmapItem::pick(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + Block *block = map->getBlock(x, y); + if (block) { + this->movementPermissionsSelector->select(block->collision, block->elevation); + } +} + +void CollisionPixmapItem::updateMovementPermissionSelection(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + + // Snap point to within map bounds. + if (x < 0) x = 0; + if (x >= map->getWidth()) x = map->getWidth() - 1; + if (y < 0) y = 0; + if (y >= map->getHeight()) y = map->getHeight() - 1; + + Block *block = map->getBlock(x, y); + this->movementPermissionsSelector->select(block->collision, block->elevation); +} diff --git a/src/ui/connectionpixmapitem.cpp b/src/ui/connectionpixmapitem.cpp new file mode 100644 index 00000000..5330fd9f --- /dev/null +++ b/src/ui/connectionpixmapitem.cpp @@ -0,0 +1,72 @@ +#include "connectionpixmapitem.h" + +void ConnectionPixmapItem::render(qreal opacity) { + QPixmap newPixmap = this->basePixmap.copy(0, 0, this->basePixmap.width(), this->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 ConnectionPixmapItem::getMinOffset() { + if (this->connection->direction == "up" || this->connection->direction == "down") + return 1 - (this->pixmap().width() / 16); + else + return 1 - (this->pixmap().height() / 16); +} + +int ConnectionPixmapItem::getMaxOffset() { + if (this->connection->direction == "up" || this->connection->direction == "down") + return this->baseMapWidth - 1; + else + return this->baseMapHeight - 1; +} + +QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == ItemPositionChange) { + QPointF newPos = value.toPointF(); + + qreal x, y; + int newOffset = this->initialOffset; + if (this->connection->direction == "up" || this->connection->direction == "down") { + x = round(newPos.x() / 16) * 16; + newOffset += (x - initialX) / 16; + newOffset = qMin(newOffset, this->getMaxOffset()); + newOffset = qMax(newOffset, this->getMinOffset()); + x = newOffset * 16; + } + else { + x = this->initialX; + } + + if (this->connection->direction == "right" || this->connection->direction == "left") { + y = round(newPos.y() / 16) * 16; + newOffset += (y - this->initialY) / 16; + newOffset = qMin(newOffset, this->getMaxOffset()); + newOffset = qMax(newOffset, this->getMinOffset()); + y = newOffset * 16; + } + else { + y = this->initialY; + } + + this->connection->offset = QString::number(newOffset); + emit connectionMoved(this->connection); + return QPointF(x, y); + } + else { + return QGraphicsItem::itemChange(change, value); + } +} + +void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *) { + emit connectionItemSelected(this); +} + +void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { + emit connectionItemDoubleClicked(this); +} diff --git a/src/ui/currentselectedmetatilespixmapitem.cpp b/src/ui/currentselectedmetatilespixmapitem.cpp new file mode 100644 index 00000000..e0b2f7d6 --- /dev/null +++ b/src/ui/currentselectedmetatilespixmapitem.cpp @@ -0,0 +1,26 @@ +#include "currentselectedmetatilespixmapitem.h" +#include "imageproviders.h" +#include + +void CurrentSelectedMetatilesPixmapItem::draw() { + QList *selectedMetatiles = metatileSelector->getSelectedMetatiles(); + QPoint selectionDimensions = metatileSelector->getSelectionDimensions(); + int width = selectionDimensions.x() * 16; + int height = selectionDimensions.y() * 16; + QImage image(width, height, QImage::Format_RGBA8888); + QPainter painter(&image); + + for (int i = 0; i < selectionDimensions.x(); i++) { + for (int j = 0; j < selectionDimensions.y(); j++) { + int x = i * 16; + int y = j * 16; + int index = j * selectionDimensions.x() + i; + QImage metatile_image = getMetatileImage(selectedMetatiles->at(index), map->layout->tileset_primary, map->layout->tileset_secondary); + QPoint metatile_origin = QPoint(x, y); + painter.drawImage(metatile_origin, metatile_image); + } + } + + painter.end(); + setPixmap(QPixmap::fromImage(image)); +} diff --git a/src/ui/eventpropertiesframe.cpp b/src/ui/eventpropertiesframe.cpp new file mode 100644 index 00000000..f340be50 --- /dev/null +++ b/src/ui/eventpropertiesframe.cpp @@ -0,0 +1,14 @@ +#include "eventpropertiesframe.h" +#include "ui_eventpropertiesframe.h" + +EventPropertiesFrame::EventPropertiesFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::EventPropertiesFrame) +{ + ui->setupUi(this); +} + +EventPropertiesFrame::~EventPropertiesFrame() +{ + delete ui; +} diff --git a/graphicsview.cpp b/src/ui/graphicsview.cpp old mode 100755 new mode 100644 similarity index 96% rename from graphicsview.cpp rename to src/ui/graphicsview.cpp index 02204103..778436df --- a/graphicsview.cpp +++ b/src/ui/graphicsview.cpp @@ -1,17 +1,17 @@ -#include "graphicsview.h" -#include "editor.h" - -void GraphicsView::mousePressEvent(QMouseEvent *event) { - QGraphicsView::mousePressEvent(event); - if (editor) { - editor->objectsView_onMousePress(event); - } -} - -void GraphicsView::mouseMoveEvent(QMouseEvent *event) { - QGraphicsView::mouseMoveEvent(event); -} - -void GraphicsView::mouseReleaseEvent(QMouseEvent *event) { - QGraphicsView::mouseReleaseEvent(event); -} +#include "graphicsview.h" +#include "editor.h" + +void GraphicsView::mousePressEvent(QMouseEvent *event) { + QGraphicsView::mousePressEvent(event); + if (editor) { + editor->objectsView_onMousePress(event); + } +} + +void GraphicsView::mouseMoveEvent(QMouseEvent *event) { + QGraphicsView::mouseMoveEvent(event); +} + +void GraphicsView::mouseReleaseEvent(QMouseEvent *event) { + QGraphicsView::mouseReleaseEvent(event); +} diff --git a/src/ui/imageproviders.cpp b/src/ui/imageproviders.cpp new file mode 100644 index 00000000..b9a6a6ad --- /dev/null +++ b/src/ui/imageproviders.cpp @@ -0,0 +1,97 @@ +#include "imageproviders.h" +#include +#include + +QImage getCollisionMetatileImage(Block block) { + return getCollisionMetatileImage(block.collision, block.elevation); +} + +QImage getCollisionMetatileImage(int collision, int elevation) { + int x = collision * 16; + int y = elevation * 16; + QPixmap collisionImage = QPixmap(":/images/collisions.png").copy(x, y, 16, 16); + return collisionImage.toImage(); +} + +QImage getMetatileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset) { + QImage metatile_image(16, 16, QImage::Format_RGBA8888); + + Metatile* metatile = Tileset::getMetatile(tile, primaryTileset, secondaryTileset); + if (!metatile || !metatile->tiles) { + metatile_image.fill(0xffffffff); + return metatile_image; + } + + Tileset* blockTileset = Tileset::getBlockTileset(tile, primaryTileset, secondaryTileset); + if (!blockTileset) { + metatile_image.fill(0xffffffff); + return metatile_image; + } + QList> palettes = Tileset::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 = getTileImage(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 because garbage will be drawn otherwise. + 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; +} + +QImage getTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset) { + Tileset *tileset = Tileset::getBlockTileset(tile, primaryTileset, secondaryTileset); + int local_index = Metatile::getBlockIndex(tile); + if (!tileset || !tileset->tiles) { + return QImage(); + } + return tileset->tiles->value(local_index, QImage()); +} + +QImage getColoredTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId) { + QList palette = Tileset::getPalette(paletteId, primaryTileset, secondaryTileset); + QImage tileImage = getTileImage(tile, primaryTileset, secondaryTileset); + if (tileImage.isNull()) { + tileImage = QImage(16, 16, QImage::Format_RGBA8888); + QPainter painter(&tileImage); + painter.fillRect(0, 0, 16, 16, palette.at(0)); + } else { + for (int i = 0; i < 16; i++) { + tileImage.setColor(i, palette.at(i)); + } + } + + return tileImage; +} diff --git a/src/ui/mappixmapitem.cpp b/src/ui/mappixmapitem.cpp new file mode 100644 index 00000000..8c3ef46f --- /dev/null +++ b/src/ui/mappixmapitem.cpp @@ -0,0 +1,480 @@ +#include "mappixmapitem.h" + +#define SWAP(a, b) do { if (a != b) { a ^= b; b ^= a; a ^= b; } } while (0) + +void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { + if (map) { + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + map->commit(); + } else { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + + // Paint onto the map. + bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier; + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + if ((this->settings->smartPathsEnabled || smartPathsEnabled) && selectionDimensions.x() == 3 && selectionDimensions.y() == 3) { + paintSmartPath(x, y); + } else { + paintNormal(x, y); + } + } + + draw(); + } +} + +void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { + if (map) { + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + map->commit(); + } else { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + + if (event->type() == QEvent::GraphicsSceneMousePress) { + selection_origin = QPoint(x, y); + selection.clear(); + } else if (event->type() == QEvent::GraphicsSceneMouseMove) { + if (x != selection_origin.x() || y != selection_origin.y()) { + int xDelta = x - selection_origin.x(); + int yDelta = y - selection_origin.y(); + Blockdata *backupBlockdata = map->layout->blockdata->copy(); + for (int i = 0; i < map->getWidth(); i++) + for (int j = 0; j < map->getHeight(); j++) { + int destX = i + xDelta; + int destY = j + yDelta; + if (destX < 0) + do { destX += map->getWidth(); } while (destX < 0); + if (destY < 0) + do { destY += map->getHeight(); } while (destY < 0); + destX %= map->getWidth(); + destY %= map->getHeight(); + + int blockIndex = j * map->getWidth() + i; + Block srcBlock = backupBlockdata->blocks->at(blockIndex); + map->_setBlock(destX, destY, srcBlock); + } + + delete backupBlockdata; + selection_origin = QPoint(x, y); + selection.clear(); + draw(); + } + } + } + } +} + +void MapPixmapItem::paintNormal(int x, int y) { + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); + + // Snap the selected position to the top-left of the block boundary. + // This allows painting via dragging the mouse to tile the painted region. + int xDiff = x - this->paint_tile_initial_x; + int yDiff = y - this->paint_tile_initial_y; + if (xDiff < 0 && xDiff % selectionDimensions.x() != 0) xDiff -= selectionDimensions.x(); + if (yDiff < 0 && yDiff % selectionDimensions.y() != 0) yDiff -= selectionDimensions.y(); + + x = this->paint_tile_initial_x + (xDiff / selectionDimensions.x()) * selectionDimensions.x(); + y = this->paint_tile_initial_y + (yDiff / selectionDimensions.y()) * selectionDimensions.y(); + + for (int i = 0; i < selectionDimensions.x() && i + x < map->getWidth(); i++) + for (int j = 0; j < selectionDimensions.y() && j + y < map->getHeight(); j++) { + int actualX = i + x; + int actualY = j + y; + Block *block = map->getBlock(actualX, actualY); + if (block) { + block->tile = selectedMetatiles->at(j * selectionDimensions.x() + i); + map->_setBlock(actualX, actualY, *block); + } + } +} + +// These are tile offsets from the top-left tile in the 3x3 smart path selection. +// Each entry is for one possibility from the marching squares value for a tile. +// (Marching Squares: https://en.wikipedia.org/wiki/Marching_squares) +QList MapPixmapItem::smartPathTable = QList({ + 4, // 0000 + 4, // 0001 + 4, // 0010 + 6, // 0011 + 4, // 0100 + 4, // 0101 + 0, // 0110 + 3, // 0111 + 4, // 1000 + 8, // 1001 + 4, // 1010 + 7, // 1011 + 2, // 1100 + 5, // 1101 + 1, // 1110 + 4, // 1111 +}); + +#define IS_SMART_PATH_TILE(block) (selectedMetatiles->contains(block->tile)) + +void MapPixmapItem::paintSmartPath(int x, int y) { + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); + + // Smart path should never be enabled without a 3x3 block selection. + if (selectionDimensions.x() != 3 || selectionDimensions.y() != 3) return; + + // Shift to the middle tile of the smart path selection. + uint16_t openTile = selectedMetatiles->at(4); + + // Fill the region with the open tile. + for (int i = 0; i <= 1; i++) + for (int j = 0; j <= 1; j++) { + // Check if in map bounds. + if (!(i + x < map->getWidth() && i + x >= 0 && j + y < map->getHeight() && j + y >= 0)) + continue; + int actualX = i + x; + int actualY = j + y; + Block *block = map->getBlock(actualX, actualY); + if (block) { + block->tile = openTile; + map->_setBlock(actualX, actualY, *block); + } + } + + // Go back and resolve the edge tiles + for (int i = -1; i <= 2; i++) + for (int j = -1; j <= 2; j++) { + // Check if in map bounds. + if (!(i + x < map->getWidth() && i + x >= 0 && j + y < map->getHeight() && j + y >= 0)) + continue; + // Ignore the corners, which can't possible be affected by the smart path. + if ((i == -1 && j == -1) || (i == 2 && j == -1) || + (i == -1 && j == 2) || (i == 2 && j == 2)) + continue; + + // Ignore tiles that aren't part of the smart path set. + int actualX = i + x; + int actualY = j + y; + Block *block = map->getBlock(actualX, actualY); + if (!block || !IS_SMART_PATH_TILE(block)) { + continue; + } + + int id = 0; + Block *top = map->getBlock(actualX, actualY - 1); + Block *right = map->getBlock(actualX + 1, actualY); + Block *bottom = map->getBlock(actualX, actualY + 1); + Block *left = map->getBlock(actualX - 1, actualY); + + // Get marching squares value, to determine which tile to use. + if (top && IS_SMART_PATH_TILE(top)) + id += 1; + if (right && IS_SMART_PATH_TILE(right)) + id += 2; + if (bottom && IS_SMART_PATH_TILE(bottom)) + id += 4; + if (left && IS_SMART_PATH_TILE(left)) + id += 8; + + block->tile = selectedMetatiles->at(smartPathTable[id]); + map->_setBlock(actualX, actualY, *block); + } +} + +void MapPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + + // Snap point to within map bounds. + if (x < 0) x = 0; + if (x >= map->getWidth()) x = map->getWidth() - 1; + if (y < 0) y = 0; + if (y >= map->getHeight()) y = map->getHeight() - 1; + + // Update/apply copied metatiles. + if (event->type() == QEvent::GraphicsSceneMousePress) { + selection_origin = QPoint(x, y); + selection.clear(); + selection.append(QPoint(x, y)); + uint16_t metatileId = map->getBlock(x, y)->tile; + this->metatileSelector->select(metatileId); + } else if (event->type() == QEvent::GraphicsSceneMouseMove) { + QPoint pos = QPoint(x, y); + int x1 = selection_origin.x(); + int y1 = selection_origin.y(); + int x2 = pos.x(); + int y2 = pos.y(); + if (x1 > x2) SWAP(x1, x2); + if (y1 > y2) SWAP(y1, y2); + selection.clear(); + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + selection.append(QPoint(x, y)); + } + } + + QList metatiles; + for (QPoint point : selection) { + metatiles.append(map->getBlock(point.x(), point.y())->tile); + } + + this->metatileSelector->setExternalSelection(x2 - x1 + 1, y2 - y1 + 1, &metatiles); + } +} + +void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { + if (map) { + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + map->commit(); + } else { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + Block *block = map->getBlock(x, y); + QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + int tile = selectedMetatiles->first(); + if (block && block->tile != tile) { + bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier; + if ((this->settings->smartPathsEnabled || smartPathsEnabled) && selectionDimensions.x() == 3 && selectionDimensions.y() == 3) + this->_floodFillSmartPath(x, y); + else + this->_floodFill(x, y); + } + } + + draw(); + } +} + +void MapPixmapItem::_floodFill(int initialX, int initialY) { + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); + + QList todo; + todo.append(QPoint(initialX, initialY)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + int x = point.x(); + int y = point.y(); + + Block *block = map->getBlock(x, y); + if (!block) { + continue; + } + + int xDiff = x - initialX; + int yDiff = y - initialY; + int i = xDiff % selectionDimensions.x(); + int j = yDiff % selectionDimensions.y(); + if (i < 0) i = selectionDimensions.x() + i; + if (j < 0) j = selectionDimensions.y() + j; + uint16_t tile = selectedMetatiles->at(j * selectionDimensions.x() + i); + uint16_t old_tile = block->tile; + if (old_tile == tile) { + continue; + } + + block->tile = tile; + map->_setBlock(x, y, *block); + if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x + 1, y)); + } + if ((block = map->getBlock(x - 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x - 1, y)); + } + if ((block = map->getBlock(x, y + 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y + 1)); + } + if ((block = map->getBlock(x, y - 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y - 1)); + } + } +} + +void MapPixmapItem::_floodFillSmartPath(int initialX, int initialY) { + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); + + // Smart path should never be enabled without a 3x3 block selection. + if (selectionDimensions.x() != 3 || selectionDimensions.y() != 3) return; + + // Shift to the middle tile of the smart path selection. + uint16_t openTile = selectedMetatiles->at(4); + + // Flood fill the region with the open tile. + QList todo; + todo.append(QPoint(initialX, initialY)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + int x = point.x(); + int y = point.y(); + + Block *block = map->getBlock(x, y); + if (!block) { + continue; + } + + uint16_t old_tile = block->tile; + if (old_tile == openTile) { + continue; + } + + block->tile = openTile; + map->_setBlock(x, y, *block); + if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x + 1, y)); + } + if ((block = map->getBlock(x - 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x - 1, y)); + } + if ((block = map->getBlock(x, y + 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y + 1)); + } + if ((block = map->getBlock(x, y - 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y - 1)); + } + } + + // Go back and resolve the flood-filled edge tiles. + // Mark tiles as visited while we go. + int numMetatiles = map->getWidth() * map->getHeight(); + bool *visited = new bool[numMetatiles]; + for (int i = 0; i < numMetatiles; i++) + visited[i] = false; + + todo.append(QPoint(initialX, initialY)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + int x = point.x(); + int y = point.y(); + visited[x + y * map->getWidth()] = true; + + Block *block = map->getBlock(x, y); + if (!block) { + continue; + } + + int id = 0; + Block *top = map->getBlock(x, y - 1); + Block *right = map->getBlock(x + 1, y); + Block *bottom = map->getBlock(x, y + 1); + Block *left = map->getBlock(x - 1, y); + + // Get marching squares value, to determine which tile to use. + if (top && IS_SMART_PATH_TILE(top)) + id += 1; + if (right && IS_SMART_PATH_TILE(right)) + id += 2; + if (bottom && IS_SMART_PATH_TILE(bottom)) + id += 4; + if (left && IS_SMART_PATH_TILE(left)) + id += 8; + + block->tile = selectedMetatiles->at(smartPathTable[id]); + map->_setBlock(x, y, *block); + + // Visit neighbors if they are smart-path tiles, and don't revisit any. + if (!visited[x + 1 + y * map->getWidth()] && (block = map->getBlock(x + 1, y)) && IS_SMART_PATH_TILE(block)) { + todo.append(QPoint(x + 1, y)); + visited[x + 1 + y * map->getWidth()] = true; + } + if (!visited[x - 1 + y * map->getWidth()] && (block = map->getBlock(x - 1, y)) && IS_SMART_PATH_TILE(block)) { + todo.append(QPoint(x - 1, y)); + visited[x - 1 + y * map->getWidth()] = true; + } + if (!visited[x + (y + 1) * map->getWidth()] && (block = map->getBlock(x, y + 1)) && IS_SMART_PATH_TILE(block)) { + todo.append(QPoint(x, y + 1)); + visited[x + (y + 1) * map->getWidth()] = true; + } + if (!visited[x + (y - 1) * map->getWidth()] && (block = map->getBlock(x, y - 1)) && IS_SMART_PATH_TILE(block)) { + todo.append(QPoint(x, y - 1)); + visited[x + (y - 1) * map->getWidth()] = true; + } + } + + delete[] visited; +} + +void MapPixmapItem::pick(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + Block *block = map->getBlock(x, y); + if (block) { + this->metatileSelector->select(block->tile); + } +} + +void MapPixmapItem::select(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + if (event->type() == QEvent::GraphicsSceneMousePress) { + selection_origin = QPoint(x, y); + selection.clear(); + } else if (event->type() == QEvent::GraphicsSceneMouseMove) { + if (event->buttons() & Qt::LeftButton) { + selection.clear(); + selection.append(QPoint(x, y)); + } + } else if (event->type() == QEvent::GraphicsSceneMouseRelease) { + if (!selection.isEmpty()) { + QPoint pos = selection.last(); + int x1 = selection_origin.x(); + int y1 = selection_origin.y(); + int x2 = pos.x(); + int y2 = pos.y(); + if (x1 > x2) SWAP(x1, x2); + if (y1 > y2) SWAP(y1, y2); + selection.clear(); + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + selection.append(QPoint(x, y)); + } + } + qDebug() << QString("selected (%1, %2) -> (%3, %4)").arg(x1).arg(y1).arg(x2).arg(y2); + } + } +} + +void MapPixmapItem::draw(bool ignoreCache) { + if (map) { + setPixmap(map->render(ignoreCache)); + } +} + +void MapPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + int x = static_cast(event->pos().x()) / 16; + int y = static_cast(event->pos().y()) / 16; + emit this->hoveredMapMetatileChanged(x, y); + if (this->settings->betterCursors){ + setCursor(this->settings->mapCursor); + } +} +void MapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { + emit this->hoveredMapMetatileCleared(); + if (this->settings->betterCursors){ + unsetCursor(); + } +} +void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + this->paint_tile_initial_x = x; + this->paint_tile_initial_y = y; + emit mouseEvent(event, this); +} +void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + int x = static_cast(event->pos().x()) / 16; + int y = static_cast(event->pos().y()) / 16; + emit this->hoveredMapMetatileChanged(x, y); + emit mouseEvent(event, this); +} +void MapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + emit mouseEvent(event, this); +} diff --git a/src/ui/mapsceneeventfilter.cpp b/src/ui/mapsceneeventfilter.cpp new file mode 100644 index 00000000..f8ae14cb --- /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*, 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/metatilelayersitem.cpp b/src/ui/metatilelayersitem.cpp new file mode 100644 index 00000000..fe8b164f --- /dev/null +++ b/src/ui/metatilelayersitem.cpp @@ -0,0 +1,45 @@ +#include "metatilelayersitem.h" +#include "imageproviders.h" +#include + +void MetatileLayersItem::draw() { + const QList tileCoords = QList{ + QPoint(0, 0), + QPoint(16, 0), + QPoint(0, 16), + QPoint(16, 16), + QPoint(32, 0), + QPoint(48, 0), + QPoint(32, 16), + QPoint(48, 16), + }; + + QPixmap pixmap(64, 32); + QPainter painter(&pixmap); + for (int i = 0; i < 8; i++) { + Tile tile = this->metatile->tiles->at(i); + QImage tileImage = getColoredTileImage(tile.tile, this->primaryTileset, this->secondaryTileset, tile.palette) + .mirrored(tile.xflip, tile.yflip) + .scaled(16, 16); + painter.drawImage(tileCoords.at(i), tileImage); + } + + this->setPixmap(pixmap); +} + +void MetatileLayersItem::setMetatile(Metatile *metatile) { + this->metatile = metatile; +} + +void MetatileLayersItem::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->draw(); +} + +void MetatileLayersItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = static_cast(pos.x()) / 16; + int y = static_cast(pos.y()) / 16; + emit this->tileChanged(x, y); +} diff --git a/src/ui/metatileselector.cpp b/src/ui/metatileselector.cpp new file mode 100644 index 00000000..5c8c8bfc --- /dev/null +++ b/src/ui/metatileselector.cpp @@ -0,0 +1,147 @@ +#include "imageproviders.h" +#include "metatileselector.h" +#include "project.h" +#include + +QPoint MetatileSelector::getSelectionDimensions() { + if (this->externalSelection) { + return QPoint(this->externalSelectionWidth, this->externalSelectionHeight); + } else { + return SelectablePixmapItem::getSelectionDimensions(); + } +} + +void MetatileSelector::draw() { + if (!this->primaryTileset || !this->primaryTileset->metatiles + || !this->secondaryTileset || !this->secondaryTileset->metatiles) { + this->setPixmap(QPixmap()); + } + + int primaryLength = this->primaryTileset->metatiles->length(); + int length_ = primaryLength + this->secondaryTileset->metatiles->length(); + int height_ = length_ / this->numMetatilesWide; + QImage image(this->numMetatilesWide * 16, height_ * 16, QImage::Format_RGBA8888); + QPainter painter(&image); + for (int i = 0; i < length_; i++) { + int tile = i; + if (i >= primaryLength) { + tile += Project::getNumMetatilesPrimary() - primaryLength; + } + QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset); + int map_y = i / this->numMetatilesWide; + int map_x = i % this->numMetatilesWide; + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + painter.drawImage(metatile_origin, metatile_image); + } + + painter.end(); + this->setPixmap(QPixmap::fromImage(image)); + + if (!this->externalSelection) { + this->drawSelection(); + } +} + +void MetatileSelector::select(uint16_t metatileId) { + this->externalSelection = false; + QPoint coords = this->getMetatileIdCoords(metatileId); + SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0); + this->updateSelectedMetatiles(); + emit selectedMetatilesChanged(); +} + +void MetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->draw(); +} + +QList* MetatileSelector::getSelectedMetatiles() { + if (this->externalSelection) { + return this->externalSelectedMetatiles; + } else { + return this->selectedMetatiles; + } +} + +void MetatileSelector::setExternalSelection(int width, int height, QList *metatiles) { + this->externalSelection = true; + this->externalSelectionWidth = width; + this->externalSelectionHeight = height; + this->externalSelectedMetatiles->clear(); + for (int i = 0; i < metatiles->length(); i++) { + this->externalSelectedMetatiles->append(metatiles->at(i)); + } + + this->draw(); + emit selectedMetatilesChanged(); +} + +void MetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mousePressEvent(event); + this->updateSelectedMetatiles(); + emit selectedMetatilesChanged(); +} + +void MetatileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseMoveEvent(event); + this->updateSelectedMetatiles(); + + QPoint pos = this->getCellPos(event->pos()); + uint16_t metatileId = this->getMetatileId(pos.x(), pos.y()); + emit this->hoveredMetatileSelectionChanged(metatileId); + emit selectedMetatilesChanged(); +} + +void MetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseReleaseEvent(event); + this->updateSelectedMetatiles(); + emit selectedMetatilesChanged(); +} + +void MetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + QPoint pos = this->getCellPos(event->pos()); + uint16_t metatileId = this->getMetatileId(pos.x(), pos.y()); + emit this->hoveredMetatileSelectionChanged(metatileId); +} + +void MetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { + emit this->hoveredMetatileSelectionCleared(); +} + +void MetatileSelector::updateSelectedMetatiles() { + this->externalSelection = false; + this->selectedMetatiles->clear(); + QPoint origin = this->getSelectionStart(); + QPoint dimensions = this->getSelectionDimensions(); + for (int j = 0; j < dimensions.y(); j++) { + for (int i = 0; i < dimensions.x(); i++) { + uint16_t metatileId = this->getMetatileId(origin.x() + i, origin.y() + j); + this->selectedMetatiles->append(metatileId); + } + } +} + +uint16_t MetatileSelector::getMetatileId(int x, int y) { + int index = y * this->numMetatilesWide + x; + if (index < this->primaryTileset->metatiles->length()) { + return static_cast(index); + } else { + return static_cast(Project::getNumMetatilesPrimary() + index - this->primaryTileset->metatiles->length()); + } +} + +QPoint MetatileSelector::getMetatileIdCoords(uint16_t metatileId) { + if (metatileId >= Project::getNumMetatilesTotal() + || (metatileId < Project::getNumMetatilesPrimary() && metatileId >= this->primaryTileset->metatiles->length()) + || (metatileId < Project::getNumMetatilesTotal() && metatileId >= Project::getNumMetatilesPrimary() + this->secondaryTileset->metatiles->length())) + { + // Invalid metatile id. + return QPoint(0, 0); + } + + int index = metatileId < Project::getNumMetatilesPrimary() + ? metatileId + : metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->metatiles->length(); + return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); +} diff --git a/src/ui/movementpermissionsselector.cpp b/src/ui/movementpermissionsselector.cpp new file mode 100644 index 00000000..da6d7178 --- /dev/null +++ b/src/ui/movementpermissionsselector.cpp @@ -0,0 +1,31 @@ +#include "movementpermissionsselector.h" +#include + +void MovementPermissionsSelector::draw() { + QPixmap pixmap(":/images/collisions.png"); + this->setPixmap(pixmap.scaled(64, 512)); + this->drawSelection(); +} + +uint16_t MovementPermissionsSelector::getSelectedCollision() { + return static_cast(this->selectionInitialX); +} + +uint16_t MovementPermissionsSelector::getSelectedElevation() { + return static_cast(this->selectionInitialY); +} + +void MovementPermissionsSelector::select(uint16_t collision, uint16_t elevation) { + SelectablePixmapItem::select(collision, elevation, 0, 0); +} + +void MovementPermissionsSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + QPoint pos = this->getCellPos(event->pos()); + uint16_t collision = static_cast(pos.x()); + uint16_t elevation = static_cast(pos.y()); + emit this->hoveredMovementPermissionChanged(collision, elevation); +} + +void MovementPermissionsSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { + emit this->hoveredMovementPermissionCleared(); +} diff --git a/neweventtoolbutton.cpp b/src/ui/neweventtoolbutton.cpp similarity index 98% rename from neweventtoolbutton.cpp rename to src/ui/neweventtoolbutton.cpp index cfd0adab..7abd7b53 100644 --- a/neweventtoolbutton.cpp +++ b/src/ui/neweventtoolbutton.cpp @@ -10,9 +10,10 @@ NewEventToolButton::NewEventToolButton(QWidget *parent) : setPopupMode(QToolButton::MenuButtonPopup); QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(setDefaultAction(QAction*))); + this->init(); } -void NewEventToolButton::initButton() +void NewEventToolButton::init() { // Add a context menu to select different types of map events. this->newObjectAction = new QAction("New Object", this); diff --git a/noscrollcombobox.cpp b/src/ui/noscrollcombobox.cpp similarity index 100% rename from noscrollcombobox.cpp rename to src/ui/noscrollcombobox.cpp diff --git a/noscrollspinbox.cpp b/src/ui/noscrollspinbox.cpp similarity index 100% rename from noscrollspinbox.cpp rename to src/ui/noscrollspinbox.cpp diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp new file mode 100644 index 00000000..bd67fa5f --- /dev/null +++ b/src/ui/paletteeditor.cpp @@ -0,0 +1,176 @@ +#include "paletteeditor.h" +#include "ui_paletteeditor.h" + +PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, QWidget *parent) : + QMainWindow(parent), + ui(new Ui::PaletteEditor) +{ + this->project = project; + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->ui->setupUi(this); + this->ui->spinBox_PaletteId->setMinimum(0); + this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1); + this->sliders.clear(); + for (int i = 0; i < 16; i++) { + this->sliders.append(QList()); + } + this->sliders[0].append(this->ui->horizontalSlider); + this->sliders[0].append(this->ui->horizontalSlider_2); + this->sliders[0].append(this->ui->horizontalSlider_3); + this->sliders[1].append(this->ui->horizontalSlider_4); + this->sliders[1].append(this->ui->horizontalSlider_5); + this->sliders[1].append(this->ui->horizontalSlider_6); + this->sliders[2].append(this->ui->horizontalSlider_7); + this->sliders[2].append(this->ui->horizontalSlider_8); + this->sliders[2].append(this->ui->horizontalSlider_9); + this->sliders[3].append(this->ui->horizontalSlider_10); + this->sliders[3].append(this->ui->horizontalSlider_11); + this->sliders[3].append(this->ui->horizontalSlider_12); + this->sliders[4].append(this->ui->horizontalSlider_13); + this->sliders[4].append(this->ui->horizontalSlider_14); + this->sliders[4].append(this->ui->horizontalSlider_15); + this->sliders[5].append(this->ui->horizontalSlider_16); + this->sliders[5].append(this->ui->horizontalSlider_17); + this->sliders[5].append(this->ui->horizontalSlider_18); + this->sliders[6].append(this->ui->horizontalSlider_19); + this->sliders[6].append(this->ui->horizontalSlider_20); + this->sliders[6].append(this->ui->horizontalSlider_21); + this->sliders[7].append(this->ui->horizontalSlider_22); + this->sliders[7].append(this->ui->horizontalSlider_23); + this->sliders[7].append(this->ui->horizontalSlider_24); + this->sliders[8].append(this->ui->horizontalSlider_25); + this->sliders[8].append(this->ui->horizontalSlider_26); + this->sliders[8].append(this->ui->horizontalSlider_27); + this->sliders[9].append(this->ui->horizontalSlider_28); + this->sliders[9].append(this->ui->horizontalSlider_29); + this->sliders[9].append(this->ui->horizontalSlider_30); + this->sliders[10].append(this->ui->horizontalSlider_31); + this->sliders[10].append(this->ui->horizontalSlider_32); + this->sliders[10].append(this->ui->horizontalSlider_33); + this->sliders[11].append(this->ui->horizontalSlider_34); + this->sliders[11].append(this->ui->horizontalSlider_35); + this->sliders[11].append(this->ui->horizontalSlider_36); + this->sliders[12].append(this->ui->horizontalSlider_37); + this->sliders[12].append(this->ui->horizontalSlider_38); + this->sliders[12].append(this->ui->horizontalSlider_39); + this->sliders[13].append(this->ui->horizontalSlider_40); + this->sliders[13].append(this->ui->horizontalSlider_41); + this->sliders[13].append(this->ui->horizontalSlider_42); + this->sliders[14].append(this->ui->horizontalSlider_43); + this->sliders[14].append(this->ui->horizontalSlider_44); + this->sliders[14].append(this->ui->horizontalSlider_45); + this->sliders[15].append(this->ui->horizontalSlider_46); + this->sliders[15].append(this->ui->horizontalSlider_47); + this->sliders[15].append(this->ui->horizontalSlider_48); + + this->frames.clear(); + this->frames.append(this->ui->frame); + this->frames.append(this->ui->frame_2); + this->frames.append(this->ui->frame_3); + this->frames.append(this->ui->frame_4); + this->frames.append(this->ui->frame_5); + this->frames.append(this->ui->frame_6); + this->frames.append(this->ui->frame_7); + this->frames.append(this->ui->frame_8); + this->frames.append(this->ui->frame_9); + this->frames.append(this->ui->frame_10); + this->frames.append(this->ui->frame_11); + this->frames.append(this->ui->frame_12); + this->frames.append(this->ui->frame_13); + this->frames.append(this->ui->frame_14); + this->frames.append(this->ui->frame_15); + this->frames.append(this->ui->frame_16); + + this->initColorSliders(); + this->refreshColorSliders(); + this->refreshColors(); +} + +PaletteEditor::~PaletteEditor() +{ + delete ui; +} + +void PaletteEditor::disableSliderSignals() { + for (int i = 0; i < this->sliders.length(); i++) { + this->sliders.at(i).at(0)->blockSignals(true); + this->sliders.at(i).at(1)->blockSignals(true); + this->sliders.at(i).at(2)->blockSignals(true); + } +} + +void PaletteEditor::enableSliderSignals() { + for (int i = 0; i < this->sliders.length(); i++) { + this->sliders.at(i).at(0)->blockSignals(false); + this->sliders.at(i).at(1)->blockSignals(false); + this->sliders.at(i).at(2)->blockSignals(false); + } +} + +void PaletteEditor::initColorSliders() { + for (int i = 0; i < 16; i++) { + connect(this->sliders[i][0], &QSlider::valueChanged, [=](int) { this->setColor(i); }); + connect(this->sliders[i][1], &QSlider::valueChanged, [=](int) { this->setColor(i); }); + connect(this->sliders[i][2], &QSlider::valueChanged, [=](int) { this->setColor(i); }); + } +} + +void PaletteEditor::refreshColorSliders() { + disableSliderSignals(); + int paletteNum = this->ui->spinBox_PaletteId->value(); + for (int i = 0; i < 16; i++) { + QRgb color; + if (paletteNum < Project::getNumPalettesPrimary()) { + color = this->primaryTileset->palettes->at(paletteNum).at(i); + } else { + color = this->secondaryTileset->palettes->at(paletteNum).at(i); + } + + this->sliders[i][0]->setValue(qRed(color) / 8); + this->sliders[i][1]->setValue(qGreen(color) / 8); + this->sliders[i][2]->setValue(qBlue(color) / 8); + } + enableSliderSignals(); +} + +void PaletteEditor::refreshColors() { + for (int i = 0; i < 16; i++) { + this->refreshColor(i); + } +} + +void PaletteEditor::refreshColor(int colorIndex) { + QString stylesheet = QString("background-color: rgb(%1, %2, %3);") + .arg(this->sliders[colorIndex][0]->value() * 8) + .arg(this->sliders[colorIndex][1]->value() * 8) + .arg(this->sliders[colorIndex][2]->value() * 8); + this->frames[colorIndex]->setStyleSheet(stylesheet); +} + +void PaletteEditor::setPaletteId(int paletteId) { + this->ui->spinBox_PaletteId->blockSignals(true); + this->ui->spinBox_PaletteId->setValue(paletteId); + this->refreshColorSliders(); + this->refreshColors(); + this->ui->spinBox_PaletteId->blockSignals(false); +} + +void PaletteEditor::setColor(int colorIndex) { + int paletteNum = this->ui->spinBox_PaletteId->value(); + int red = this->sliders[colorIndex][0]->value() * 8; + int green = this->sliders[colorIndex][1]->value() * 8; + int blue = this->sliders[colorIndex][2]->value() * 8; + Tileset *tileset = paletteNum < Project::getNumPalettesPrimary() + ? this->primaryTileset + : this->secondaryTileset; + (*tileset->palettes)[paletteNum][colorIndex] = qRgb(red, green, blue); + this->refreshColor(colorIndex); + emit this->changedPaletteColor(); +} + +void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) { + this->refreshColorSliders(); + this->refreshColors(); + emit this->changedPalette(paletteId); +} diff --git a/src/ui/selectablepixmapitem.cpp b/src/ui/selectablepixmapitem.cpp new file mode 100644 index 00000000..dbfbaa34 --- /dev/null +++ b/src/ui/selectablepixmapitem.cpp @@ -0,0 +1,108 @@ +#include "selectablepixmapitem.h" +#include + +QPoint SelectablePixmapItem::getSelectionDimensions() +{ + return QPoint(abs(this->selectionOffsetX) + 1, abs(this->selectionOffsetY) + 1); +} + +QPoint SelectablePixmapItem::getSelectionStart() +{ + int x = this->selectionInitialX; + int y = this->selectionInitialY; + if (this->selectionOffsetX < 0) x += this->selectionOffsetX; + if (this->selectionOffsetY < 0) y += this->selectionOffsetY; + return QPoint(x, y); +} + +void SelectablePixmapItem::select(int x, int y, int width, int height) +{ + this->selectionInitialX = x; + this->selectionInitialY = y; + this->selectionOffsetX = qMax(0, qMin(width, this->maxSelectionWidth)); + this->selectionOffsetY = qMax(0, qMin(height, this->maxSelectionHeight)); + this->draw(); +} + +void SelectablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + QPoint pos = this->getCellPos(event->pos()); + this->selectionInitialX = pos.x(); + this->selectionInitialY = pos.y(); + this->selectionOffsetX = 0; + this->selectionOffsetY = 0; + this->updateSelection(pos.x(), pos.y()); +} + +void SelectablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + QPoint pos = this->getCellPos(event->pos()); + this->updateSelection(pos.x(), pos.y()); +} + +void SelectablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + QPoint pos = this->getCellPos(event->pos()); + this->updateSelection(pos.x(), pos.y()); +} + +void SelectablePixmapItem::updateSelection(int x, int y) +{ + // Snap to a valid position inside the selection area. + int width = pixmap().width() / this->cellWidth; + int height = pixmap().height() / this->cellHeight; + if (x < 0) x = 0; + if (x >= width) x = width - 1; + if (y < 0) y = 0; + if (y >= height) y = height - 1; + + this->selectionOffsetX = x - this->selectionInitialX; + this->selectionOffsetY = y - this->selectionInitialY; + + // Respect max selection dimensions by moving the selection's origin. + if (this->selectionOffsetX >= this->maxSelectionWidth) { + this->selectionInitialX += this->selectionOffsetX - this->maxSelectionWidth + 1; + this->selectionOffsetX = this->maxSelectionWidth - 1; + } else if (this->selectionOffsetX <= -this->maxSelectionWidth) { + this->selectionInitialX += this->selectionOffsetX + this->maxSelectionWidth - 1; + this->selectionOffsetX = -this->maxSelectionWidth + 1; + } + if (this->selectionOffsetY >= this->maxSelectionHeight) { + this->selectionInitialY += this->selectionOffsetY - this->maxSelectionHeight + 1; + this->selectionOffsetY = this->maxSelectionHeight - 1; + } else if (this->selectionOffsetY <= -this->maxSelectionHeight) { + this->selectionInitialY += this->selectionOffsetY + this->maxSelectionHeight - 1; + this->selectionOffsetY = -this->maxSelectionHeight + 1; + } + + this->draw(); +} + +QPoint SelectablePixmapItem::getCellPos(QPointF pos) +{ + if (pos.x() < 0) pos.setX(0); + if (pos.y() < 0) pos.setY(0); + if (pos.x() >= this->pixmap().width()) pos.setX(this->pixmap().width() - 1); + if (pos.y() >= this->pixmap().height()) pos.setY(this->pixmap().height() - 1); + return QPoint(static_cast(pos.x()) / this->cellWidth, + static_cast(pos.y()) / this->cellHeight); +} + +void SelectablePixmapItem::drawSelection() +{ + QPixmap pixmap = this->pixmap(); + QPainter painter(&pixmap); + QPoint origin = this->getSelectionStart(); + QPoint dimensions = this->getSelectionDimensions(); + + int rectWidth = dimensions.x() * this->cellWidth; + int rectHeight = dimensions.y() * this->cellHeight; + + painter.setPen(QColor(0xff, 0xff, 0xff)); + painter.drawRect(origin.x() * this->cellWidth, origin.y() * this->cellHeight, rectWidth - 1, rectHeight -1); + painter.setPen(QColor(0, 0, 0)); + painter.drawRect(origin.x() * this->cellWidth - 1, origin.y() * this->cellHeight - 1, rectWidth + 1, rectHeight + 1); + painter.drawRect(origin.x() * this->cellWidth + 1, origin.y() * this->cellHeight + 1, rectWidth - 3, rectHeight - 3); + + this->setPixmap(pixmap); +} diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp new file mode 100644 index 00000000..60fe30d4 --- /dev/null +++ b/src/ui/tileseteditor.cpp @@ -0,0 +1,460 @@ +#include "tileseteditor.h" +#include "ui_tileseteditor.h" +#include "imageproviders.h" +#include +#include +#include +#include + +TilesetEditor::TilesetEditor(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel, QWidget *parent) : + QMainWindow(parent), + ui(new Ui::TilesetEditor) +{ + this->init(project, primaryTilesetLabel, secondaryTilesetLabel); +} + +TilesetEditor::~TilesetEditor() +{ + delete ui; +} + +void TilesetEditor::init(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel) { + ui->setupUi(this); + this->project = project; + + this->hasUnsavedChanges = false; + this->tileXFlip = ui->checkBox_xFlip->isChecked(); + this->tileYFlip = ui->checkBox_yFlip->isChecked(); + this->paletteId = ui->spinBox_paletteSelector->value(); + + Tileset *primaryTileset = project->getTileset(primaryTilesetLabel); + Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel); + if (this->primaryTileset) delete this->primaryTileset; + if (this->secondaryTileset) delete this->secondaryTileset; + this->primaryTileset = primaryTileset->copy(); + this->secondaryTileset = secondaryTileset->copy(); + + QList sortedBehaviors; + for (int num : project->metatileBehaviorMapInverse.keys()) { + this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num); + } + this->ui->comboBox_layerType->addItem("Normal - Middle/Top", 0); + this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", 1); + this->ui->comboBox_layerType->addItem("Split - Bottom/Top", 2); + this->ui->spinBox_paletteSelector->setMinimum(0); + this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1); + + this->initMetatileSelector(); + this->initMetatileLayersItem(); + this->initTileSelector(); + this->initSelectedTileItem(); + this->metatileSelector->select(0); +} + +void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel) { + delete this->primaryTileset; + delete this->secondaryTileset; + Tileset *primaryTileset = project->getTileset(primaryTilesetLabel); + Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel); + this->primaryTileset = primaryTileset->copy(); + this->secondaryTileset = secondaryTileset->copy(); + this->refresh(); +} + +void TilesetEditor::refresh() { + this->metatileSelector->setTilesets(this->primaryTileset, this->secondaryTileset); + this->tileSelector->setTilesets(this->primaryTileset, this->secondaryTileset); + this->metatileLayersItem->setTilesets(this->primaryTileset, this->secondaryTileset); + this->metatileSelector->select(this->metatileSelector->getSelectedMetatile()); + this->drawSelectedTiles(); + + this->ui->graphicsView_Tiles->setSceneRect(0, 0, this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2); + this->ui->graphicsView_Tiles->setFixedSize(this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2); + this->ui->graphicsView_Metatiles->setSceneRect(0, 0, this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2); + this->ui->graphicsView_Metatiles->setFixedSize(this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2); + this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height()); +} + +void TilesetEditor::initMetatileSelector() +{ + this->metatileSelector = new TilesetEditorMetatileSelector(this->primaryTileset, this->secondaryTileset); + connect(this->metatileSelector, SIGNAL(hoveredMetatileChanged(uint16_t)), + this, SLOT(onHoveredMetatileChanged(uint16_t))); + connect(this->metatileSelector, SIGNAL(hoveredMetatileCleared()), + this, SLOT(onHoveredMetatileCleared())); + connect(this->metatileSelector, SIGNAL(selectedMetatileChanged(uint16_t)), + this, SLOT(onSelectedMetatileChanged(uint16_t))); + + this->metatilesScene = new QGraphicsScene; + this->metatilesScene->addItem(this->metatileSelector); + this->metatileSelector->draw(); + + this->ui->graphicsView_Metatiles->setScene(this->metatilesScene); + this->ui->graphicsView_Metatiles->setFixedSize(this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2); +} + +void TilesetEditor::initTileSelector() +{ + this->tileSelector = new TilesetEditorTileSelector(this->primaryTileset, this->secondaryTileset); + connect(this->tileSelector, SIGNAL(hoveredTileChanged(uint16_t)), + this, SLOT(onHoveredTileChanged(uint16_t))); + connect(this->tileSelector, SIGNAL(hoveredTileCleared()), + this, SLOT(onHoveredTileCleared())); + connect(this->tileSelector, SIGNAL(selectedTilesChanged()), + this, SLOT(onSelectedTilesChanged())); + + this->tilesScene = new QGraphicsScene; + this->tilesScene->addItem(this->tileSelector); + this->tileSelector->select(0); + this->tileSelector->draw(); + + this->ui->graphicsView_Tiles->setScene(this->tilesScene); + this->ui->graphicsView_Tiles->setFixedSize(this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2); +} + +void TilesetEditor::initSelectedTileItem() { + this->selectedTileScene = new QGraphicsScene; + this->drawSelectedTiles(); + this->ui->graphicsView_selectedTile->setScene(this->selectedTileScene); + this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height()); +} + +void TilesetEditor::drawSelectedTiles() { + if (!this->selectedTileScene) { + return; + } + + this->selectedTileScene->clear(); + QList tiles = this->tileSelector->getSelectedTiles(); + QPoint dimensions = this->tileSelector->getSelectionDimensions(); + QImage selectionImage(16 * dimensions.x(), 16 * dimensions.y(), QImage::Format_RGBA8888); + QPainter painter(&selectionImage); + int tileIndex = 0; + for (int j = 0; j < dimensions.y(); j++) { + for (int i = 0; i < dimensions.x(); i++) { + QImage tileImage = getColoredTileImage(tiles.at(tileIndex), this->primaryTileset, this->secondaryTileset, this->paletteId) + .mirrored(this->tileXFlip, this->tileYFlip) + .scaled(16, 16); + tileIndex++; + painter.drawImage(i * 16, j * 16, tileImage); + } + } + + this->selectedTilePixmapItem = new QGraphicsPixmapItem(QPixmap::fromImage(selectionImage)); + this->selectedTileScene->addItem(this->selectedTilePixmapItem); + this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height()); +} + +void TilesetEditor::initMetatileLayersItem() { + Metatile *metatile = Tileset::getMetatile(this->metatileSelector->getSelectedMetatile(), this->primaryTileset, this->secondaryTileset); + this->metatileLayersItem = new MetatileLayersItem(metatile, this->primaryTileset, this->secondaryTileset); + connect(this->metatileLayersItem, SIGNAL(tileChanged(int, int)), + this, SLOT(onMetatileLayerTileChanged(int, int))); + + this->metatileLayersScene = new QGraphicsScene; + this->metatileLayersScene->addItem(this->metatileLayersItem); + this->ui->graphicsView_metatileLayers->setScene(this->metatileLayersScene); +} + +void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) { + QString message = QString("Metatile: 0x%1") + .arg(QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper()); + this->ui->statusbar->showMessage(message); +} + +void TilesetEditor::onHoveredMetatileCleared() { + this->ui->statusbar->clearMessage(); +} + +void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) { + this->metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset); + this->metatileLayersItem->setMetatile(metatile); + this->metatileLayersItem->draw(); + this->ui->comboBox_metatileBehaviors->setCurrentIndex(this->ui->comboBox_metatileBehaviors->findData(this->metatile->behavior)); + this->ui->comboBox_layerType->setCurrentIndex(this->ui->comboBox_layerType->findData(this->metatile->layerType)); +} + +void TilesetEditor::onHoveredTileChanged(uint16_t tile) { + QString message = QString("Tile: 0x%1") + .arg(QString("%1").arg(tile, 3, 16, QChar('0')).toUpper()); + this->ui->statusbar->showMessage(message); +} + +void TilesetEditor::onHoveredTileCleared() { + this->ui->statusbar->clearMessage(); +} + +void TilesetEditor::onSelectedTilesChanged() { + this->drawSelectedTiles(); +} + +void TilesetEditor::onMetatileLayerTileChanged(int x, int y) { + int maxTileIndex = x < 2 ? 3 : 7; + QPoint dimensions = this->tileSelector->getSelectionDimensions(); + QList tiles = this->tileSelector->getSelectedTiles(); + int selectedTileIndex = 0; + for (int j = 0; j < dimensions.y(); j++) { + for (int i = 0; i < dimensions.x(); i++) { + int tileIndex = ((x + i) / 2 * 4) + ((y + j) * 2) + ((x + i) % 2); + if (tileIndex <= maxTileIndex) { + Tile tile = this->metatile->tiles->at(tileIndex); + tile.tile = tiles.at(selectedTileIndex); + tile.xflip = this->tileXFlip; + tile.yflip = this->tileYFlip; + tile.palette = this->paletteId; + (*this->metatile->tiles)[tileIndex] = tile; + } + selectedTileIndex++; + } + } + + this->metatileSelector->draw(); + this->metatileLayersItem->draw(); + this->hasUnsavedChanges = true; +} + +void TilesetEditor::on_spinBox_paletteSelector_valueChanged(int paletteId) +{ + this->ui->spinBox_paletteSelector->blockSignals(true); + this->ui->spinBox_paletteSelector->setValue(paletteId); + this->ui->spinBox_paletteSelector->blockSignals(false); + this->paletteId = paletteId; + this->tileSelector->setPaletteId(paletteId); + this->drawSelectedTiles(); + if (this->paletteEditor) { + this->paletteEditor->setPaletteId(paletteId); + } +} + +void TilesetEditor::on_checkBox_xFlip_stateChanged(int checked) +{ + this->tileXFlip = checked; + this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip); + this->drawSelectedTiles(); +} + +void TilesetEditor::on_checkBox_yFlip_stateChanged(int checked) +{ + this->tileYFlip = checked; + this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip); + this->drawSelectedTiles(); +} + +void TilesetEditor::on_comboBox_metatileBehaviors_currentIndexChanged(const QString &metatileBehavior) +{ + if (this->metatile) { + this->metatile->behavior = static_cast(project->metatileBehaviorMap[metatileBehavior]); + } +} + +void TilesetEditor::on_comboBox_layerType_currentIndexChanged(int layerType) +{ + if (this->metatile) { + this->metatile->layerType = static_cast(layerType); + } +} + +void TilesetEditor::on_actionSave_Tileset_triggered() +{ + this->project->saveTilesets(this->primaryTileset, this->secondaryTileset); + emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name); + this->ui->statusbar->showMessage(QString("Saved primary and secondary Tilesets!"), 5000); + this->hasUnsavedChanges = false; +} + +void TilesetEditor::on_actionImport_Primary_Tiles_triggered() +{ + this->importTilesetTiles(this->primaryTileset, true); +} + +void TilesetEditor::on_actionImport_Secondary_Tiles_triggered() +{ + this->importTilesetTiles(this->secondaryTileset, false); +} + +void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) { + QString descriptor = primary ? "primary" : "secondary"; + QString descriptorCaps = primary ? "Primary" : "Secondary"; + + QString filepath = QFileDialog::getOpenFileName( + this, + QString("Import %1 Tileset Tiles Image").arg(descriptorCaps), + this->project->root, + "Image Files (*.png)"); + if (filepath.isEmpty()) { + return; + } + + qDebug() << QString("Importing %1 tileset tiles '%2'").arg(descriptor).arg(filepath); + + // Validate image dimensions. + QImage image = QImage(filepath); + if (image.width() == 0 || image.height() == 0 || image.width() % 8 != 0 || image.height() % 8 != 0) { + QMessageBox msgBox(this); + msgBox.setText("Failed to import tiles."); + msgBox.setInformativeText(QString("The image dimensions (%1 x %2) are invalid. Width and height must be multiples of 8 pixels.") + .arg(image.width()) + .arg(image.height())); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + + // Validate image is properly indexed to 16 colors. + if (image.colorCount() != 16) { + QMessageBox msgBox(this); + msgBox.setText("Failed to import tiles."); + msgBox.setInformativeText(QString("The image must be indexed and contain 16 total colors. The provided image has %1 indexed colors.") + .arg(image.colorCount())); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + + // Validate total number of tiles in image. + int numTilesWide = image.width() / 16; + int numTilesHigh = image.height() / 16; + int totalTiles = numTilesHigh * numTilesWide; + int maxAllowedTiles = primary ? Project::getNumTilesPrimary() : Project::getNumTilesTotal() - Project::getNumTilesPrimary(); + if (totalTiles > maxAllowedTiles) { + QMessageBox msgBox(this); + msgBox.setText("Failed to import tiles."); + msgBox.setInformativeText(QString("The maximum number of tiles allowed in the %1 tileset is %2, but the provided image contains %3 total tiles.") + .arg(descriptor) + .arg(maxAllowedTiles) + .arg(totalTiles)); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.exec(); + return; + } + + this->project->loadTilesetTiles(tileset, image); + this->project->loadTilesetMetatiles(tileset); + this->refresh(); + this->hasUnsavedChanges = true; +} + +void TilesetEditor::closeEvent(QCloseEvent *event) +{ + bool close = true; + if (this->hasUnsavedChanges) { + QMessageBox::StandardButton result = QMessageBox::question(this, "porymap", + "Discard unsaved Tileset changes?", + QMessageBox::No | QMessageBox::Yes, + QMessageBox::Yes); + close = result == QMessageBox::Yes; + } + + if (close) { + event->accept(); + emit closed(); + } else { + event->ignore(); + } +} + +void TilesetEditor::on_actionChange_Metatiles_Count_triggered() +{ + QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + dialog.setWindowTitle("Change Number of Metatiles"); + dialog.setWindowModality(Qt::NonModal); + + QFormLayout form(&dialog); + + QSpinBox *primarySpinBox = new QSpinBox(); + QSpinBox *secondarySpinBox = new QSpinBox(); + primarySpinBox->setMinimum(1); + secondarySpinBox->setMinimum(1); + primarySpinBox->setMaximum(Project::getNumMetatilesPrimary()); + secondarySpinBox->setMaximum(Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()); + primarySpinBox->setValue(this->primaryTileset->metatiles->length()); + secondarySpinBox->setValue(this->secondaryTileset->metatiles->length()); + form.addRow(new QLabel("Primary Tileset"), primarySpinBox); + form.addRow(new QLabel("Secondary Tileset"), secondarySpinBox); + + QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); + connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); + form.addRow(&buttonBox); + + if (dialog.exec() == QDialog::Accepted) { + int numPrimaryMetatiles = primarySpinBox->value(); + int numSecondaryMetatiles = secondarySpinBox->value(); + while (this->primaryTileset->metatiles->length() > numPrimaryMetatiles) { + Metatile *metatile = this->primaryTileset->metatiles->takeLast(); + delete metatile; + } + while (this->primaryTileset->metatiles->length() < numPrimaryMetatiles) { + Tile tile; + tile.palette = 0; + tile.tile = 0; + tile.xflip = 0; + tile.yflip = 0; + Metatile *metatile = new Metatile; + metatile->behavior = 0; + metatile->layerType = 0; + for (int i = 0; i < 8; i++) { + metatile->tiles->append(tile); + } + this->primaryTileset->metatiles->append(metatile); + } + while (this->secondaryTileset->metatiles->length() > numSecondaryMetatiles) { + Metatile *metatile = this->secondaryTileset->metatiles->takeLast(); + delete metatile; + } + while (this->secondaryTileset->metatiles->length() < numSecondaryMetatiles) { + Tile tile; + tile.palette = 0; + tile.tile = 0; + tile.xflip = 0; + tile.yflip = 0; + Metatile *metatile = new Metatile; + metatile->behavior = 0; + metatile->layerType = 0; + for (int i = 0; i < 8; i++) { + metatile->tiles->append(tile); + } + this->secondaryTileset->metatiles->append(metatile); + } + + this->refresh(); + this->hasUnsavedChanges = true; + } +} + +void TilesetEditor::onPaletteEditorClosed() { + if (this->paletteEditor) { + delete this->paletteEditor; + this->paletteEditor = nullptr; + } +} + +void TilesetEditor::on_actionChange_Palettes_triggered() +{ + if (!this->paletteEditor) { + this->paletteEditor = new PaletteEditor(this->project, this->primaryTileset, this->secondaryTileset, this); + connect(this->paletteEditor, SIGNAL(closed()), this, SLOT(onPaletteEditorClosed())); + connect(this->paletteEditor, SIGNAL(changedPaletteColor()), this, SLOT(onPaletteEditorChangedPaletteColor())); + connect(this->paletteEditor, SIGNAL(changedPalette(int)), this, SLOT(onPaletteEditorChangedPalette(int))); + } + + if (!this->paletteEditor->isVisible()) { + this->paletteEditor->show(); + } else if (this->paletteEditor->isMinimized()) { + this->paletteEditor->showNormal(); + } else { + this->paletteEditor->activateWindow(); + } +} + +void TilesetEditor::onPaletteEditorChangedPaletteColor() { + this->refresh(); + this->hasUnsavedChanges = true; +} + +void TilesetEditor::onPaletteEditorChangedPalette(int paletteId) { + this->on_spinBox_paletteSelector_valueChanged(paletteId); +} diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp new file mode 100644 index 00000000..852ab0e9 --- /dev/null +++ b/src/ui/tileseteditormetatileselector.cpp @@ -0,0 +1,114 @@ +#include "tileseteditormetatileselector.h" +#include "imageproviders.h" +#include "project.h" +#include + +void TilesetEditorMetatileSelector::draw() { + if (!this->primaryTileset || !this->primaryTileset->metatiles + || !this->secondaryTileset || !this->secondaryTileset->metatiles) { + this->setPixmap(QPixmap()); + } + + int primaryLength = this->primaryTileset->metatiles->length(); + int length_ = primaryLength + this->secondaryTileset->metatiles->length(); + int height_ = length_ / this->numMetatilesWide; + QImage image(this->numMetatilesWide * 32, height_ * 32, QImage::Format_RGBA8888); + QPainter painter(&image); + for (int i = 0; i < length_; i++) { + int tile = i; + if (i >= primaryLength) { + tile += Project::getNumMetatilesPrimary() - primaryLength; + } + QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset).scaled(32, 32); + int map_y = i / this->numMetatilesWide; + int map_x = i % this->numMetatilesWide; + QPoint metatile_origin = QPoint(map_x * 32, map_y * 32); + painter.drawImage(metatile_origin, metatile_image); + } + + painter.end(); + this->setPixmap(QPixmap::fromImage(image)); + this->drawSelection(); +} + +void TilesetEditorMetatileSelector::select(uint16_t metatileId) { + metatileId = this->getValidMetatileId(metatileId); + QPoint coords = this->getMetatileIdCoords(metatileId); + SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0); + this->selectedMetatile = metatileId; + emit selectedMetatileChanged(metatileId); +} + +void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->draw(); +} + +void TilesetEditorMetatileSelector::updateSelectedMetatile() { + QPoint origin = this->getSelectionStart(); + this->selectedMetatile = this->getMetatileId(origin.x(), origin.y()); +} + +uint16_t TilesetEditorMetatileSelector::getSelectedMetatile() { + return this->selectedMetatile; +} + +uint16_t TilesetEditorMetatileSelector::getMetatileId(int x, int y) { + int index = y * this->numMetatilesWide + x; + if (index < this->primaryTileset->metatiles->length()) { + return static_cast(index); + } else { + return static_cast(Project::getNumMetatilesPrimary() + index - this->primaryTileset->metatiles->length()); + } +} + +void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mousePressEvent(event); + this->updateSelectedMetatile(); + emit selectedMetatileChanged(this->selectedMetatile); +} + +void TilesetEditorMetatileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseMoveEvent(event); + this->updateSelectedMetatile(); + + QPoint pos = this->getCellPos(event->pos()); + uint16_t metatileId = this->getMetatileId(pos.x(), pos.y()); + emit hoveredMetatileChanged(metatileId); + emit selectedMetatileChanged(metatileId); +} + +void TilesetEditorMetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseReleaseEvent(event); + this->updateSelectedMetatile(); + emit selectedMetatileChanged(this->selectedMetatile); +} + +void TilesetEditorMetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + QPoint pos = this->getCellPos(event->pos()); + uint16_t metatileId = this->getMetatileId(pos.x(), pos.y()); + emit this->hoveredMetatileChanged(metatileId); +} + +void TilesetEditorMetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { + emit this->hoveredMetatileCleared(); +} + +QPoint TilesetEditorMetatileSelector::getMetatileIdCoords(uint16_t metatileId) { + int index = metatileId < Project::getNumMetatilesPrimary() + ? metatileId + : metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->metatiles->length(); + return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); +} + +uint16_t TilesetEditorMetatileSelector::getValidMetatileId(uint16_t metatileId) { + if (metatileId >= Project::getNumMetatilesTotal() + || (metatileId < Project::getNumMetatilesPrimary() && metatileId >= this->primaryTileset->metatiles->length()) + || (metatileId < Project::getNumMetatilesTotal() && metatileId >= Project::getNumMetatilesPrimary() + this->secondaryTileset->metatiles->length())) + { + return 0; + } + return metatileId; +} + diff --git a/src/ui/tileseteditortileselector.cpp b/src/ui/tileseteditortileselector.cpp new file mode 100644 index 00000000..6d323f46 --- /dev/null +++ b/src/ui/tileseteditortileselector.cpp @@ -0,0 +1,130 @@ +#include "tileseteditortileselector.h" +#include "imageproviders.h" +#include "project.h" +#include + +void TilesetEditorTileSelector::draw() { + if (!this->primaryTileset || !this->primaryTileset->tiles + || !this->secondaryTileset || !this->secondaryTileset->tiles) { + this->setPixmap(QPixmap()); + } + + int totalTiles = Project::getNumTilesTotal(); + int primaryLength = this->primaryTileset->tiles->length(); + int secondaryLength = this->secondaryTileset->tiles->length(); + int height = totalTiles / this->numTilesWide; + QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset); + QImage image(this->numTilesWide * 16, height * 16, QImage::Format_RGBA8888); + + QPainter painter(&image); + for (uint16_t tile = 0; tile < totalTiles; tile++) { + QImage tileImage; + if (tile < primaryLength) { + tileImage = getColoredTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16); + } else if (tile < Project::getNumTilesPrimary()) { + tileImage = QImage(16, 16, QImage::Format_RGBA8888); + tileImage.fill(palette.at(0)); + } else if (tile < Project::getNumTilesPrimary() + secondaryLength) { + tileImage = getColoredTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16); + } else { + tileImage = QImage(16, 16, QImage::Format_RGBA8888); + QPainter painter(&tileImage); + painter.fillRect(0, 0, 16, 16, palette.at(0)); + } + + int y = tile / this->numTilesWide; + int x = tile % this->numTilesWide; + QPoint origin = QPoint(x * 16, y * 16); + painter.drawImage(origin, tileImage.mirrored(this->xFlip, this->yFlip)); + } + + painter.end(); + this->setPixmap(QPixmap::fromImage(image)); + this->drawSelection(); +} + +void TilesetEditorTileSelector::select(uint16_t tile) { + QPoint coords = this->getTileCoords(tile); + SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0); + this->updateSelectedTiles(); + emit selectedTilesChanged(); +} + +void TilesetEditorTileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { + this->primaryTileset = primaryTileset; + this->secondaryTileset = secondaryTileset; + this->draw(); +} + +void TilesetEditorTileSelector::setPaletteId(int paletteId) { + this->paletteId = paletteId; + this->draw(); +} + +void TilesetEditorTileSelector::setTileFlips(bool xFlip, bool yFlip) { + this->xFlip = xFlip; + this->yFlip = yFlip; + this->draw(); +} + +void TilesetEditorTileSelector::updateSelectedTiles() { + this->selectedTiles.clear(); + QPoint origin = this->getSelectionStart(); + QPoint dimensions = this->getSelectionDimensions(); + for (int j = 0; j < dimensions.y(); j++) { + for (int i = 0; i < dimensions.x(); i++) { + uint16_t metatileId = this->getTileId(origin.x() + i, origin.y() + j); + this->selectedTiles.append(metatileId); + } + } +} + +QList TilesetEditorTileSelector::getSelectedTiles() { + return this->selectedTiles; +} + +uint16_t TilesetEditorTileSelector::getTileId(int x, int y) { + return static_cast(y * this->numTilesWide + x); +} + +void TilesetEditorTileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mousePressEvent(event); + this->updateSelectedTiles(); + emit selectedTilesChanged(); +} + +void TilesetEditorTileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseMoveEvent(event); + this->updateSelectedTiles(); + + QPoint pos = this->getCellPos(event->pos()); + uint16_t tile = this->getTileId(pos.x(), pos.y()); + emit hoveredTileChanged(tile); + emit selectedTilesChanged(); +} + +void TilesetEditorTileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + SelectablePixmapItem::mouseReleaseEvent(event); + this->updateSelectedTiles(); + emit selectedTilesChanged(); +} + +void TilesetEditorTileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + QPoint pos = this->getCellPos(event->pos()); + uint16_t tile = this->getTileId(pos.x(), pos.y()); + emit this->hoveredTileChanged(tile); +} + +void TilesetEditorTileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { + emit this->hoveredTileCleared(); +} + +QPoint TilesetEditorTileSelector::getTileCoords(uint16_t tile) { + if (tile >= Project::getNumTilesTotal()) + { + // Invalid tile. + return QPoint(0, 0); + } + + return QPoint(tile % this->numTilesWide, tile / this->numTilesWide); +} diff --git a/tileset.cpp b/tileset.cpp deleted file mode 100755 index 55fa6258..00000000 --- a/tileset.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#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. - 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; -}