diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/editor.cpp b/editor.cpp index 962907d9..17909121 100755 --- a/editor.cpp +++ b/editor.cpp @@ -50,7 +50,7 @@ void Editor::setEditingMap() { map_item->draw(); map_item->setVisible(true); map_item->setEnabled(true); - setConnectionsVisibility(true); + setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); } if (collision_item) { collision_item->setVisible(false); @@ -58,7 +58,7 @@ void Editor::setEditingMap() { if (events_group) { events_group->setVisible(false); } - setBorderItemsVisible(true); + setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); setConnectionItemsVisible(false); } @@ -68,7 +68,7 @@ void Editor::setEditingCollision() { displayMapConnections(); collision_item->draw(); collision_item->setVisible(true); - setConnectionsVisibility(true); + setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); } if (map_item) { map_item->setVisible(false); @@ -76,7 +76,7 @@ void Editor::setEditingCollision() { if (events_group) { events_group->setVisible(false); } - setBorderItemsVisible(true); + setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); setConnectionItemsVisible(false); } @@ -88,12 +88,12 @@ void Editor::setEditingObjects() { if (map_item) { map_item->setVisible(true); map_item->setEnabled(false); - setConnectionsVisibility(true); + setConnectionsVisibility(ui->checkBox_ToggleBorder->isChecked()); } if (collision_item) { collision_item->setVisible(false); } - setBorderItemsVisible(true); + setBorderItemsVisible(ui->checkBox_ToggleBorder->isChecked()); setConnectionItemsVisible(false); } @@ -338,33 +338,54 @@ void Editor::setMap(QString map_name) { } void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item) { - if (event->buttons() & Qt::RightButton) { - item->updateMetatileSelection(event); - } else { - if (map_edit_mode == "paint") { - item->paint(event); - } else if (map_edit_mode == "fill") { + if (map_edit_mode == "paint") { + if (event->buttons() & Qt::RightButton) { + item->updateMetatileSelection(event); + } else if (event->buttons() & Qt::MiddleButton) { item->floodFill(event); - } else if (map_edit_mode == "pick") { - item->pick(event); - } else if (map_edit_mode == "select") { - item->select(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 (event->buttons() & Qt::RightButton) { - item->updateMovementPermissionSelection(event); - } else { - if (map_edit_mode == "paint") { - item->paint(event); - } else if (map_edit_mode == "fill") { + if (map_edit_mode == "paint") { + if (event->buttons() & Qt::RightButton) { + item->updateMovementPermissionSelection(event); + } else if (event->buttons() & Qt::MiddleButton) { item->floodFill(event); - } else if (map_edit_mode == "pick") { - item->pick(event); - } else if (map_edit_mode == "select") { - item->select(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); } } @@ -861,6 +882,12 @@ void Editor::updateSecondaryTileset(QString tilesetLabel) } } +void Editor::toggleBorderVisibility(bool visible) +{ + this->setBorderItemsVisible(visible); + this->setConnectionsVisibility(visible); +} + void MetatilesPixmapItem::paintTileChanged(Map *map) { draw(); } @@ -1080,7 +1107,8 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { int y = (int)(pos.y()) / 16; // Paint onto the map. - if (map->smart_paths_enabled && map->selected_metatiles_width == 3 && map->selected_metatiles_height == 3) { + 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); @@ -1091,6 +1119,51 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { } } +void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { + if (map) { + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + map->commit(); + } else { + QPointF pos = event->pos(); + int x = (int)(pos.x()) / 16; + int y = (int)(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 srcX = i; + int srcY = 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. @@ -1145,8 +1218,8 @@ void MapPixmapItem::paintSmartPath(int x, int y) { int openTile = map->selected_metatiles->at(4); // Fill the region with the open tile. - for (int i = -1; i <= 1; i++) - for (int j = -1; j <= 1; j++) { + 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; @@ -1160,14 +1233,14 @@ void MapPixmapItem::paintSmartPath(int x, int y) { } // Go back and resolve the edge tiles - for (int i = -2; i <= 2; i++) - for (int j = -2; j <= 2; j++) { + 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 == -2 && j == -2) || (i == 2 && j == -2) || - (i == -2 && j == 2) || (i == 2 && j == 2)) + 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. @@ -1260,7 +1333,8 @@ void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { Block *block = map->getBlock(x, y); int tile = map->selected_metatiles->first(); if (block && block->tile != tile) { - if (map->smart_paths_enabled && map->selected_metatiles_width == 3 && map->selected_metatiles_height == 3) + 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); @@ -1484,9 +1558,15 @@ void MapPixmapItem::updateCurHoveredTile(QPointF pos) { void MapPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { updateCurHoveredTile(event->pos()); + if (editor->ui->actionBetter_Cursors->isChecked()){ + setCursor(editor->cursor); + } } void MapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { map->clearHoveredTile(); + if (editor->ui->actionBetter_Cursors->isChecked()){ + unsetCursor(); + } } void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { QPointF pos = event->pos(); @@ -1653,6 +1733,7 @@ QList *Editor::getObjects() { 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); diff --git a/editor.h b/editor.h index 863b096f..ab09c365 100755 --- a/editor.h +++ b/editor.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "project.h" #include "ui_mainwindow.h" @@ -66,6 +67,7 @@ public: void setSelectedConnectionFromMap(QString mapName); void updatePrimaryTileset(QString tilesetLabel); void updateSecondaryTileset(QString tilesetLabel); + void toggleBorderVisibility(bool visible); DraggablePixmapItem *addMapEvent(Event *event); void selectMapEvent(DraggablePixmapItem *object); @@ -108,6 +110,8 @@ public: QList *copiedMetatileSelection = new QList; QString map_edit_mode; + QString prev_edit_mode; + QCursor cursor; void objectsView_onMousePress(QMouseEvent *event); void objectsView_onMouseMove(QMouseEvent *event); @@ -130,6 +134,7 @@ private: void updateMirroredConnection(Connection*, QString, QString, bool isDelete = false); Event* createNewObjectEvent(); Event* createNewWarpEvent(); + Event* createNewHealLocationEvent(); Event* createNewCoordScriptEvent(); Event* createNewCoordWeatherEvent(); Event* createNewSignEvent(); @@ -173,10 +178,8 @@ public: int last_x; int last_y; void updatePosition() { - int x = event->x() * 16; - int y = event->y() * 16; - x -= pixmap().width() / 32 * 16; - y -= pixmap().height() - 16; + int x = event->getPixelX(); + int y = event->getPixelY(); setX(x); setY(y); setZValue(event->y()); @@ -192,6 +195,7 @@ public: objects.append(event); event->pixmap = QPixmap(); editor->project->loadEventPixmaps(objects); + this->updatePosition(); editor->redrawObject(this); emit spriteChanged(event->pixmap); } @@ -270,6 +274,7 @@ public: 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); diff --git a/event.cpp b/event.cpp index e26b4540..a1c6f5e4 100755 --- a/event.cpp +++ b/event.cpp @@ -1,4 +1,5 @@ #include "event.h" +#include "map.h" QString EventType::Object = "event_object"; QString EventType::Warp = "event_warp"; @@ -7,9 +8,12 @@ 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) @@ -19,6 +23,8 @@ Event* Event::createNewEvent(QString event_type, QString map_name) 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) { @@ -64,6 +70,15 @@ Event* Event::createNewWarpEvent(QString 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; @@ -113,6 +128,16 @@ Event* Event::createNewSecretBaseEvent() 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"); @@ -150,6 +175,21 @@ QString Event::buildWarpEventMacro(QMap *mapNamesToMapConstant 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->get("x").toInt(); + hl.y = this->get("y").toInt(); + return hl; +} + QString Event::buildCoordScriptEventMacro() { QString text = ""; @@ -208,3 +248,13 @@ QString Event::buildSecretBaseEventMacro() 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/event.h b/event.h index 03313d27..fc71e8a7 100755 --- a/event.h +++ b/event.h @@ -1,6 +1,7 @@ #ifndef EVENT_H #define EVENT_H +#include "heallocation.h" #include #include #include @@ -16,6 +17,7 @@ public: static QString Sign; static QString HiddenItem; static QString SecretBase; + static QString HealLocation; }; class Event @@ -54,6 +56,7 @@ public: 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(); @@ -62,14 +65,20 @@ public: 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 diff --git a/heallocation.cpp b/heallocation.cpp new file mode 100644 index 00000000..7652b96a --- /dev/null +++ b/heallocation.cpp @@ -0,0 +1,19 @@ +#include "heallocation.h" + +//HealLocation::HealLocation() {} + +HealLocation::HealLocation(QString map, int i, size_t x0, size_t y0) { + + name = map; + index = i; + x = x0; + y = y0; + +} + +QDebug operator<<(QDebug debug, const HealLocation &hl) { + + debug << "HealLocation_" + hl.name << "(" << hl.x << ',' << hl.y << ")"; + return debug; + +} diff --git a/heallocation.h b/heallocation.h new file mode 100644 index 00000000..bf01b30a --- /dev/null +++ b/heallocation.h @@ -0,0 +1,23 @@ +#ifndef HEALLOCATION_H +#define HEALLOCATION_H + +#include +#include + +class HealLocation { + +public: + HealLocation()=default; + HealLocation(QString, int, size_t, size_t); + friend QDebug operator<<(QDebug debug, const HealLocation &hl); + +public: + //QString group; + QString name; + int index; + size_t x; + size_t y; + +}; + +#endif // HEALLOCATION_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 0320f809..1b05d0f1 100755 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -15,8 +15,14 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), @@ -53,6 +59,9 @@ MainWindow::MainWindow(QWidget *parent) : } } + if (settings.contains("cursor_mode") && settings.value("cursor_mode") == "0") { + ui->actionBetter_Cursors->setChecked(false); + } } MainWindow::~MainWindow() @@ -551,6 +560,12 @@ 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(); @@ -591,6 +606,66 @@ 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 = (double)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); + + ui->graphicsView_Map->setFixedSize((editor->scene->width() + 2) * pow(base,exp), + (editor->scene->height() + 2) * pow(base,exp)); + + ui->graphicsView_Objects_Map->setFixedSize((editor->scene->width() + 2) * pow(base,exp), + (editor->scene->height() + 2) * pow(base,exp)); +} + void MainWindow::addNewEvent(QString event_type) { if (editor) { @@ -649,9 +724,12 @@ void MainWindow::updateSelectedObjects() { 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) + 1) + .arg(editor->project->getMap(map_name)->events.value(event_group_type).indexOf(item->event) + event_offs) .arg(map_name) .arg(event_type) ); @@ -709,8 +787,8 @@ void MainWindow::updateSelectedObjects() { fields << "sight_radius_tree_id"; } else if (event_type == EventType::Warp) { - fields << "destination_warp"; fields << "destination_map_name"; + fields << "destination_warp"; } else if (event_type == EventType::CoordScript) { fields << "script_label"; @@ -864,38 +942,96 @@ void MainWindow::on_toolButton_deleteObject_clicked() if (editor && editor->selected_events) { if (editor->selected_events->length()) { for (DraggablePixmapItem *item : *editor->selected_events) { - editor->deleteEvent(item->event); - if (editor->scene->items().contains(item)) { - editor->scene->removeItem(item); + 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"); } - editor->selected_events->removeOne(item); } 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(); } @@ -904,6 +1040,8 @@ void MainWindow::checkToolButtons() { 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) { @@ -1038,3 +1176,9 @@ 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); +} diff --git a/mainwindow.h b/mainwindow.h index b8a12f8b..b020ee52 100755 --- a/mainwindow.h +++ b/mainwindow.h @@ -37,6 +37,8 @@ private slots: void undo(); void redo(); + void openInTextEditor(); + void onLoadMapRequested(QString, QString); void onMapChanged(Map *map); void onMapNeedsRedrawing(Map *map); @@ -58,7 +60,18 @@ private slots: 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(); @@ -71,6 +84,10 @@ private slots: 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); @@ -102,6 +119,8 @@ private slots: void on_checkBox_Visibility_clicked(bool checked); + void on_checkBox_ToggleBorder_stateChanged(int arg1); + private: Ui::MainWindow *ui; QStandardItemModel *mapListModel; @@ -124,6 +143,8 @@ private: void displayMapProperties(); void checkToolButtons(); + + void scaleMapView(int); }; enum MapListUserRoles { diff --git a/mainwindow.ui b/mainwindow.ui index dccacb4c..936ccaf2 100755 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -171,7 +171,7 @@ true - <html><head/><body><p>Editor</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> + <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 @@ -214,7 +214,7 @@ - <html><head/><body><p>Flood Fill</p><p>Fills all similar tiles in a region with the selected metatiles or collision attributes</p></body></html> + <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 @@ -231,7 +231,7 @@ - <html><head/><body><p>Eye Dropper</p><p>Click to select a metatile or collision attribute.</p></body></html> + <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 @@ -245,10 +245,44 @@ + + + + <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></body></html> + <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 @@ -267,7 +301,17 @@ - Show Grid + Grid + + + + + + + Border + + + true @@ -334,7 +378,7 @@ 0 0 - 429 + 469 620 @@ -362,9 +406,18 @@ 0 + + false + false + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + @@ -708,7 +761,7 @@ 0 0 - 315 + 275 86 @@ -814,7 +867,7 @@ 0 0 - 365 + 325 405 @@ -1330,6 +1383,29 @@ + + + + Open Map Scripts + + + false + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + @@ -2044,8 +2120,29 @@ + + + View + + + + + + + + Tools + + + + + + + + + + @@ -2104,6 +2201,84 @@ 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 + + diff --git a/map.cpp b/map.cpp index f114884f..626724f2 100755 --- a/map.cpp +++ b/map.cpp @@ -608,10 +608,11 @@ bool Map::hasUnsavedChanges() { } void Map::hoveredTileChanged(int x, int y, int block) { - emit statusBarMessage(QString("X: %1, Y: %2, Metatile: 0x%3") + 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("%1").arg(block, 3, 16, QChar('0')).toUpper()) + .arg(QString::number(pow(this->scale_base,this->scale_exp)))); } void Map::clearHoveredTile() { diff --git a/map.h b/map.h index 82e70675..b41c97c4 100755 --- a/map.h +++ b/map.h @@ -9,6 +9,7 @@ #include #include #include +#include class HistoryItem { public: @@ -128,12 +129,15 @@ public: 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; diff --git a/neweventtoolbutton.cpp b/neweventtoolbutton.cpp index 51d4ee2b..cfd0adab 100644 --- a/neweventtoolbutton.cpp +++ b/neweventtoolbutton.cpp @@ -23,6 +23,12 @@ void NewEventToolButton::initButton() this->newWarpAction->setIcon(QIcon(":/icons/add.ico")); connect(this->newWarpAction, SIGNAL(triggered(bool)), this, SLOT(newWarp())); + /* // disable this functionality for now + this->newHealLocationAction = new QAction("New Heal Location", this); + this->newHealLocationAction->setIcon(QIcon(":/icons/add.ico")); + connect(this->newHealLocationAction, SIGNAL(triggered(bool)), this, SLOT(newHealLocation())); + */ + this->newCoordScriptAction = new QAction("New Coord Script", this); this->newCoordScriptAction->setIcon(QIcon(":/icons/add.ico")); connect(this->newCoordScriptAction, SIGNAL(triggered(bool)), this, SLOT(newCoordScript())); @@ -46,6 +52,7 @@ void NewEventToolButton::initButton() QMenu *alignMenu = new QMenu(); alignMenu->addAction(this->newObjectAction); alignMenu->addAction(this->newWarpAction); + //alignMenu->addAction(this->newHealLocationAction); alignMenu->addAction(this->newCoordScriptAction); alignMenu->addAction(this->newCoordWeatherAction); alignMenu->addAction(this->newSignAction); @@ -72,6 +79,12 @@ void NewEventToolButton::newWarp() emit newEventAdded(this->selectedEventType); } +void NewEventToolButton::newHealLocation() +{ + this->selectedEventType = EventType::HealLocation; + emit newEventAdded(this->selectedEventType); +} + void NewEventToolButton::newCoordScript() { this->selectedEventType = EventType::CoordScript; diff --git a/neweventtoolbutton.h b/neweventtoolbutton.h index f4bb7f55..d75be3b4 100644 --- a/neweventtoolbutton.h +++ b/neweventtoolbutton.h @@ -14,6 +14,7 @@ public: public slots: void newObject(); void newWarp(); + void newHealLocation(); void newCoordScript(); void newCoordWeather(); void newSign(); @@ -25,6 +26,7 @@ private: QString selectedEventType; QAction *newObjectAction; QAction *newWarpAction; + QAction *newHealLocationAction; QAction *newCoordScriptAction; QAction *newCoordWeatherAction; QAction *newSignAction; diff --git a/parseutil.cpp b/parseutil.cpp index 9b481159..15c24b16 100755 --- a/parseutil.cpp +++ b/parseutil.cpp @@ -68,6 +68,24 @@ int ParseUtil::evaluateDefine(QString define, QMap* knownDefines) 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].toInt(), li[3].toInt()); + parsed->append(hloc); + i++; + } + } + return parsed; +} + QList ParseUtil::tokenizeExpression(QString expression, QMap* knownIdentifiers) { QList tokens; diff --git a/parseutil.h b/parseutil.h index dfd52c16..23e095fc 100755 --- a/parseutil.h +++ b/parseutil.h @@ -1,6 +1,8 @@ #ifndef PARSEUTIL_H #define PARSEUTIL_H +#include "heallocation.h" + #include #include #include @@ -35,6 +37,7 @@ public: 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); diff --git a/porymap.pro b/porymap.pro index 7c1e9358..f1d68efb 100755 --- a/porymap.pro +++ b/porymap.pro @@ -1,54 +1,56 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2016-08-31T15:19:13 -# -#------------------------------------------------- - -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = porymap -TEMPLATE = app -RC_ICONS = resources/icons/porymap-icon-1.ico -ICON = resources/icons/porymap-icon-1.ico - - -SOURCES += main.cpp\ - mainwindow.cpp \ - project.cpp \ - map.cpp \ - blockdata.cpp \ - block.cpp \ - tileset.cpp \ - tile.cpp \ - event.cpp \ - editor.cpp \ - objectpropertiesframe.cpp \ - graphicsview.cpp \ - parseutil.cpp \ - neweventtoolbutton.cpp \ - noscrollcombobox.cpp \ - noscrollspinbox.cpp - -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 - -FORMS += mainwindow.ui \ - objectpropertiesframe.ui - -RESOURCES += \ - resources/images.qrc +#------------------------------------------------- +# +# Project created by QtCreator 2016-08-31T15:19:13 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = porymap +TEMPLATE = app +RC_ICONS = resources/icons/porymap-icon-1.ico +ICON = resources/icons/porymap-icon-1.ico + + +SOURCES += main.cpp\ + mainwindow.cpp \ + project.cpp \ + map.cpp \ + blockdata.cpp \ + block.cpp \ + tileset.cpp \ + tile.cpp \ + event.cpp \ + editor.cpp \ + objectpropertiesframe.cpp \ + graphicsview.cpp \ + parseutil.cpp \ + neweventtoolbutton.cpp \ + noscrollcombobox.cpp \ + noscrollspinbox.cpp \ + heallocation.cpp + +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 + +FORMS += mainwindow.ui \ + objectpropertiesframe.ui + +RESOURCES += \ + resources/images.qrc diff --git a/project.cpp b/project.cpp index fd352dfa..5bd0d2bb 100755 --- a/project.cpp +++ b/project.cpp @@ -501,6 +501,73 @@ void Project::saveMapConstantsHeader() { 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; @@ -947,6 +1014,11 @@ void Project::readMapGroups() { 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) { @@ -1212,6 +1284,9 @@ void Project::loadEventPixmaps(QList 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); @@ -1221,22 +1296,39 @@ void Project::loadEventPixmaps(QList objects) { 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("&", ""); - QString pic_label = readCArray(info_text, info_label).value(14); + 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); - QPixmap pixmap(root + "/" + path); - if (!pixmap.isNull()) { - object->pixmap = pixmap; + 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); } } } @@ -1309,6 +1401,15 @@ void Project::saveMapEvents(Map *map) { .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) { @@ -1379,6 +1480,29 @@ void Project::readMapEvents(Map *map) { } } + 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) { @@ -1455,6 +1579,7 @@ void Project::readMapEvents(Map *map) { 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(); } diff --git a/project.h b/project.h index 90796295..49e348b2 100755 --- a/project.h +++ b/project.h @@ -3,6 +3,7 @@ #include "map.h" #include "blockdata.h" +#include "heallocation.h" #include #include @@ -17,6 +18,7 @@ public: QMap *map_groups; QList groupedMapNames; QStringList *mapNames = NULL; + QList flyableMaps; QMap* mapConstantsToMapNames; QMap* mapNamesToMapConstants; QList mapLayoutsTable; @@ -77,6 +79,7 @@ public: void saveAllMapLayouts(); void saveMapGroupsTable(); void saveMapConstantsHeader(); + void saveHealLocationStruct(Map*); QList* parseAsm(QString text); QStringList getSongNames(); diff --git a/resources/icons/fill_color_cursor.ico b/resources/icons/fill_color_cursor.ico new file mode 100644 index 00000000..712ab992 Binary files /dev/null and b/resources/icons/fill_color_cursor.ico differ diff --git a/resources/icons/move.ico b/resources/icons/move.ico new file mode 100644 index 00000000..1d247401 Binary files /dev/null and b/resources/icons/move.ico differ diff --git a/resources/icons/pencil_cursor.ico b/resources/icons/pencil_cursor.ico new file mode 100644 index 00000000..1ff8d8fb Binary files /dev/null and b/resources/icons/pencil_cursor.ico differ diff --git a/resources/icons/pipette_cursor.ico b/resources/icons/pipette_cursor.ico new file mode 100644 index 00000000..8c226945 Binary files /dev/null and b/resources/icons/pipette_cursor.ico differ diff --git a/resources/icons/shift.ico b/resources/icons/shift.ico new file mode 100644 index 00000000..dab33a4e Binary files /dev/null and b/resources/icons/shift.ico differ diff --git a/resources/icons/shift_cursor.ico b/resources/icons/shift_cursor.ico new file mode 100644 index 00000000..0c8722c3 Binary files /dev/null and b/resources/icons/shift_cursor.ico differ diff --git a/resources/images.qrc b/resources/images.qrc index 920c6aad..78fbe6a1 100755 --- a/resources/images.qrc +++ b/resources/images.qrc @@ -9,6 +9,7 @@ icons/map.ico icons/cursor.ico icons/fill_color.ico + icons/move.ico icons/pencil.ico icons/pipette.ico images/Entities_16x16.png @@ -16,6 +17,11 @@ 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 index db480f6e..2d35d31d 100755 Binary files a/resources/images/Entities_16x16.png and b/resources/images/Entities_16x16.png differ