From 911ba64637028f84654de44bd1eada413ec8c279 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Wed, 26 Sep 2018 18:30:05 -0500 Subject: [PATCH] Move more ui out of editor --- map.cpp => core/map.cpp | 864 +++++++++++----------- map.h => core/map.h | 182 ++--- editor.cpp | 801 ++------------------ editor.h | 165 +---- mainwindow.cpp | 27 +- porymap.pro | 24 +- settings.cpp | 6 + settings.h | 15 + ui/bordermetatilespixmapitem.cpp | 42 ++ ui/bordermetatilespixmapitem.h | 25 + ui/collisionpixmapitem.cpp | 86 +++ ui/collisionpixmapitem.h | 37 + ui/connectionpixmapitem.cpp | 72 ++ ui/connectionpixmapitem.h | 44 ++ ui/currentselectedmetatilespixmapitem.cpp | 26 + ui/currentselectedmetatilespixmapitem.h | 20 + graphicsview.cpp => ui/graphicsview.cpp | 34 +- graphicsview.h => ui/graphicsview.h | 52 +- ui/mappixmapitem.cpp | 480 ++++++++++++ ui/mappixmapitem.h | 55 ++ 20 files changed, 1584 insertions(+), 1473 deletions(-) rename map.cpp => core/map.cpp (96%) mode change 100755 => 100644 rename map.h => core/map.h (96%) mode change 100755 => 100644 create mode 100644 settings.cpp create mode 100644 settings.h create mode 100644 ui/bordermetatilespixmapitem.cpp create mode 100644 ui/bordermetatilespixmapitem.h create mode 100644 ui/collisionpixmapitem.cpp create mode 100644 ui/collisionpixmapitem.h create mode 100644 ui/connectionpixmapitem.cpp create mode 100644 ui/connectionpixmapitem.h create mode 100644 ui/currentselectedmetatilespixmapitem.cpp create mode 100644 ui/currentselectedmetatilespixmapitem.h rename graphicsview.cpp => ui/graphicsview.cpp (96%) mode change 100755 => 100644 rename graphicsview.h => ui/graphicsview.h (95%) mode change 100755 => 100644 create mode 100644 ui/mappixmapitem.cpp create mode 100644 ui/mappixmapitem.h diff --git a/map.cpp b/core/map.cpp old mode 100755 new mode 100644 similarity index 96% rename from map.cpp rename to core/map.cpp index 16762e85..3954df06 --- a/map.cpp +++ b/core/map.cpp @@ -1,432 +1,432 @@ -#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(); - 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; -} +#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(); + 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/map.h b/core/map.h old mode 100755 new mode 100644 similarity index 96% rename from map.h rename to core/map.h index 155db949..851045e2 --- a/map.h +++ b/core/map.h @@ -1,91 +1,91 @@ -#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; - int scale_exp = 0; - double scale_base = sqrt(2); // adjust scale factor with this - 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 +#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; + int scale_exp = 0; + double scale_base = sqrt(2); // adjust scale factor with this + 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/editor.cpp b/editor.cpp index e1d4509d..84f3ee67 100755 --- a/editor.cpp +++ b/editor.cpp @@ -2,6 +2,7 @@ #include "event.h" #include "imageproviders.h" #include "mapconnection.h" +#include "currentselectedmetatilespixmapitem.h" #include #include #include @@ -12,7 +13,8 @@ static bool selectingEvent = false; Editor::Editor(Ui::MainWindow* ui) { this->ui = ui; - selected_events = new QList; + this->selected_events = new QList; + this->settings = new Settings(); } void Editor::saveProject() { @@ -339,13 +341,15 @@ void Editor::onSelectedMetatilesChanged() { } void Editor::onHoveredMapMetatileChanged(int x, int y) { - 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(map->scale_base, map->scale_exp)))); + 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(map->scale_base, map->scale_exp)))); + } } void Editor::onHoveredMapMetatileCleared() { @@ -353,14 +357,16 @@ void Editor::onHoveredMapMetatileCleared() { } void Editor::onHoveredMapMovementPermissionChanged(int x, int y) { - 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); + 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() { @@ -462,45 +468,13 @@ void Editor::displayMap() { 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*))); - 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); - - 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*))); - 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); - - int tw = 16; - int th = 16; - scene->setSceneRect( - -6 * tw, - -6 * th, - map_item->pixmap().width() + 12 * tw, - map_item->pixmap().height() + 12 * th - ); displayMetatileSelector(); + displayMovementPermissionSelector(); + displayMapMetatiles(); + displayMapMovementPermissions(); displayBorderMetatiles(); displayCurrentMetatilesSelection(); - displayMovementPermissionSelector(); displayMapEvents(); displayMapConnections(); displayMapBorder(); @@ -540,6 +514,45 @@ void Editor::displayMetatileSelector() { 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); @@ -547,7 +560,7 @@ void Editor::displayBorderMetatiles() { } scene_selected_border_metatiles = new QGraphicsScene; - selected_border_metatiles_item = new BorderMetatilesPixmapItem(map, this); + 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); @@ -561,7 +574,7 @@ void Editor::displayCurrentMetatilesSelection() { } scene_current_metatile_selection = new QGraphicsScene; - scene_current_metatile_selection_item = new CurrentSelectedMetatilesPixmapItem(map, this); + 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); } @@ -608,7 +621,7 @@ void Editor::displayMapEvents() { selected_events->clear(); - events_group = new EventGroup; + events_group = new QGraphicsItemGroup; scene->addItem(events_group); QList events = map->getAllEvents(); @@ -969,686 +982,6 @@ void Editor::toggleBorderVisibility(bool visible) this->setConnectionsVisibility(visible); } -void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QList *selectedMetatiles = editor->metatile_selector_item->getSelectedMetatiles(); - QPoint selectionDimensions = editor->metatile_selector_item->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(); - setPixmap(QPixmap::fromImage(image)); -} - -void CurrentSelectedMetatilesPixmapItem::draw() { - QList *selectedMetatiles = editor->metatile_selector_item->getSelectedMetatiles(); - QPoint selectionDimensions = editor->metatile_selector_item->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)); -} - -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; - QPoint selectionDimensions = editor->metatile_selector_item->getSelectionDimensions(); - if ((editor->smart_paths_enabled || 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 = editor->metatile_selector_item->getSelectionDimensions(); - QList *selectedMetatiles = editor->metatile_selector_item->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 = editor->metatile_selector_item->getSelectionDimensions(); - QList *selectedMetatiles = editor->metatile_selector_item->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; - editor->metatile_selector_item->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); - } - - editor->metatile_selector_item->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 = editor->metatile_selector_item->getSelectedMetatiles(); - QPoint selectionDimensions = editor->metatile_selector_item->getSelectionDimensions(); - int tile = selectedMetatiles->first(); - if (block && block->tile != tile) { - bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier; - if ((editor->smart_paths_enabled || 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 = editor->metatile_selector_item->getSelectionDimensions(); - QList *selectedMetatiles = editor->metatile_selector_item->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 = editor->metatile_selector_item->getSelectionDimensions(); - QList *selectedMetatiles = editor->metatile_selector_item->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) { - editor->metatile_selector_item->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 (editor->ui->actionBetter_Cursors->isChecked()){ - setCursor(editor->cursor); - } -} -void MapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { - emit this->hoveredMapMetatileCleared(); - 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; - 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); -} - -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 (editor->ui->actionBetter_Cursors->isChecked()){ - setCursor(editor->cursor); - } -} -void CollisionPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { - emit this->hoveredMapMovementPermissionCleared(); - if (editor->ui->actionBetter_Cursors->isChecked()){ - 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 = editor->movement_permissions_selector_item->getSelectedCollision(); - block->elevation = editor->movement_permissions_selector_item->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 = editor->movement_permissions_selector_item->getSelectedCollision(); - uint16_t elevation = editor->movement_permissions_selector_item->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) { - editor->movement_permissions_selector_item->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); - editor->movement_permissions_selector_item->select(block->collision, block->elevation); -} - void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { active = true; last_x = static_cast(mouse->pos().x() + this->pos().x()) / 16; diff --git a/editor.h b/editor.h index 5087d622..014e7d13 100755 --- a/editor.h +++ b/editor.h @@ -14,16 +14,15 @@ #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 MapPixmapItem; -class CollisionPixmapItem; -class ConnectionPixmapItem; class MetatilesPixmapItem; -class BorderMetatilesPixmapItem; -class CurrentSelectedMetatilesPixmapItem; - -#define SWAP(a, b) do { if (a != b) { a ^= b; b ^= a; a ^= b; } } while (0) class Editor : public QObject { @@ -35,6 +34,7 @@ public: QObject *parent = nullptr; Project *project = nullptr; Map *map = nullptr; + Settings *settings; void saveProject(); void save(); void undo(); @@ -42,6 +42,8 @@ public: void setMap(QString map_name); void displayMap(); void displayMetatileSelector(); + void displayMapMetatiles(); + void displayMapMovementPermissions(); void displayBorderMetatiles(); void displayCurrentMetatilesSelection(); void redrawCurrentMetatilesSelection(); @@ -107,8 +109,6 @@ public: QString map_edit_mode; QString prev_edit_mode; - QCursor cursor; - bool smart_paths_enabled = false; void objectsView_onMousePress(QMouseEvent *event); void objectsView_onMouseMove(QMouseEvent *event); @@ -256,151 +256,4 @@ protected: 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; - 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*); -}; - -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 *); - void hoveredMapMovementPermissionChanged(int, int); - void hoveredMapMovementPermissionCleared(); - -protected: - void hoverMoveEvent(QGraphicsSceneHoverEvent*); - void hoverLeaveEvent(QGraphicsSceneHoverEvent*); - void mousePressEvent(QGraphicsSceneMouseEvent*); - void mouseMoveEvent(QGraphicsSceneMouseEvent*); - void mouseReleaseEvent(QGraphicsSceneMouseEvent*); -}; - -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; - } - 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; - MapConnection* 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(MapConnection*); -}; - -class BorderMetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { - Q_OBJECT -public: - BorderMetatilesPixmapItem(Map *map_, Editor *editor_) { - map = map_; - editor = editor_; - setAcceptHoverEvents(true); - } - Editor *editor = nullptr; - 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_, Editor *editor_) { - map = map_; - editor = editor_; - } - Map* map = nullptr; - Editor *editor = nullptr; - virtual void draw(); -}; - #endif // EDITOR_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 89b94818..bf626c6a 100755 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4,6 +4,8 @@ #include "editor.h" #include "eventpropertiesframe.h" #include "ui_objectpropertiesframe.h" +#include "bordermetatilespixmapitem.h" +#include "currentselectedmetatilespixmapitem.h" #include #include @@ -35,7 +37,6 @@ MainWindow::MainWindow(QWidget *parent) : ui->setupUi(this); this->initExtraSignals(); this->initExtraShortcuts(); - this->loadUserSettings(); this->initEditor(); this->openRecentProject(); @@ -63,13 +64,16 @@ void MainWindow::initEditor() { connect(this->editor, SIGNAL(tilesetChanged(QString)), this, SLOT(onTilesetChanged(QString))); connect(this->editor, SIGNAL(warpEventDoubleClicked(QString,QString)), this, SLOT(openWarpMap(QString,QString))); connect(this->editor, SIGNAL(currentMetatilesSelectionChanged()), this, SLOT(currentMetatilesSelectionChanged())); + + this->loadUserSettings(); } void MainWindow::loadUserSettings() { QSettings settings; - if (settings.contains("cursor_mode") && settings.value("cursor_mode") == "0") { - ui->actionBetter_Cursors->setChecked(false); - } + + bool betterCursors = settings.contains("cursor_mode") && settings.value("cursor_mode") != "0"; + ui->actionBetter_Cursors->setChecked(betterCursors); + this->editor->settings->betterCursors = betterCursors; } void MainWindow::openRecentProject() { @@ -627,6 +631,7 @@ void MainWindow::on_actionZoom_Out_triggered() { 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() @@ -975,7 +980,7 @@ void MainWindow::on_toolButton_Open_Scripts_clicked() void MainWindow::on_toolButton_Paint_clicked() { editor->map_edit_mode = "paint"; - editor->cursor = QCursor(QPixmap(":/icons/pencil_cursor.ico"), 10, 10); + editor->settings->mapCursor = QCursor(QPixmap(":/icons/pencil_cursor.ico"), 10, 10); ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -987,7 +992,7 @@ void MainWindow::on_toolButton_Paint_clicked() void MainWindow::on_toolButton_Select_clicked() { editor->map_edit_mode = "select"; - editor->cursor = QCursor(QPixmap(":/icons/cursor.ico"), 0, 0); + editor->settings->mapCursor = QCursor(QPixmap(":/icons/cursor.ico"), 0, 0); ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -999,7 +1004,7 @@ void MainWindow::on_toolButton_Select_clicked() void MainWindow::on_toolButton_Fill_clicked() { editor->map_edit_mode = "fill"; - editor->cursor = QCursor(QPixmap(":/icons/fill_color_cursor.ico"), 10, 10); + editor->settings->mapCursor = QCursor(QPixmap(":/icons/fill_color_cursor.ico"), 10, 10); ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -1011,7 +1016,7 @@ void MainWindow::on_toolButton_Fill_clicked() void MainWindow::on_toolButton_Dropper_clicked() { editor->map_edit_mode = "pick"; - editor->cursor = QCursor(QPixmap(":/icons/pipette_cursor.ico"), 10, 10); + editor->settings->mapCursor = QCursor(QPixmap(":/icons/pipette_cursor.ico"), 10, 10); ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -1023,7 +1028,7 @@ void MainWindow::on_toolButton_Dropper_clicked() void MainWindow::on_toolButton_Move_clicked() { editor->map_edit_mode = "move"; - editor->cursor = QCursor(QPixmap(":/icons/move.ico"), 7, 7); + editor->settings->mapCursor = QCursor(QPixmap(":/icons/move.ico"), 7, 7); ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -1035,7 +1040,7 @@ void MainWindow::on_toolButton_Move_clicked() void MainWindow::on_toolButton_Shift_clicked() { editor->map_edit_mode = "shift"; - editor->cursor = QCursor(QPixmap(":/icons/shift_cursor.ico"), 10, 10); + editor->settings->mapCursor = QCursor(QPixmap(":/icons/shift_cursor.ico"), 10, 10); ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -1183,7 +1188,7 @@ void MainWindow::on_pushButton_clicked() void MainWindow::on_checkBox_smartPaths_stateChanged(int selected) { - editor->smart_paths_enabled = selected == Qt::Checked; + editor->settings->smartPathsEnabled = selected == Qt::Checked; } void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected) diff --git a/porymap.pro b/porymap.pro index cd81dc59..7589cc75 100755 --- a/porymap.pro +++ b/porymap.pro @@ -19,13 +19,20 @@ SOURCES += core/block.cpp \ core/event.cpp \ core/heallocation.cpp \ core/historyitem.cpp \ + core/map.cpp \ core/maplayout.cpp \ core/metatile.cpp \ core/parseutil.cpp \ core/tile.cpp \ core/tileset.cpp \ + ui/bordermetatilespixmapitem.cpp \ + ui/collisionpixmapitem.cpp \ + ui/connectionpixmapitem.cpp \ + ui/currentselectedmetatilespixmapitem.cpp \ ui/eventpropertiesframe.cpp \ + ui/graphicsview.cpp \ ui/imageproviders.cpp \ + ui/mappixmapitem.cpp \ ui/metatileselector.cpp \ ui/movementpermissionsselector.cpp \ ui/neweventtoolbutton.cpp \ @@ -33,11 +40,10 @@ SOURCES += core/block.cpp \ ui/noscrollspinbox.cpp \ ui/selectablepixmapitem.cpp \ editor.cpp \ - graphicsview.cpp \ main.cpp \ mainwindow.cpp \ - map.cpp \ - project.cpp + project.cpp \ + settings.cpp HEADERS += core/block.h \ core/blockdata.h \ @@ -45,14 +51,21 @@ HEADERS += core/block.h \ core/heallocation.h \ core/history.h \ core/historyitem.h \ + core/map.h \ core/mapconnection.h \ core/maplayout.h \ core/metatile.h \ core/parseutil.h \ core/tile.h \ core/tileset.h \ + ui/bordermetatilespixmapitem.h \ + ui/collisionpixmapitem.h \ + ui/connectionpixmapitem.h \ + ui/currentselectedmetatilespixmapitem.h \ ui/eventpropertiesframe.h \ + ui/graphicsview.h \ ui/imageproviders.h \ + ui/mappixmapitem.h \ ui/metatileselector.h \ ui/movementpermissionsselector.h \ ui/neweventtoolbutton.h \ @@ -60,10 +73,9 @@ HEADERS += core/block.h \ ui/noscrollspinbox.h \ ui/selectablepixmapitem.h \ editor.h \ - graphicsview.h \ mainwindow.h \ - map.h \ - project.h + project.h \ + settings.h FORMS += mainwindow.ui \ eventpropertiesframe.ui diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 00000000..27ddf83c --- /dev/null +++ b/settings.cpp @@ -0,0 +1,6 @@ +#include "settings.h" + +Settings::Settings() +{ + +} diff --git a/settings.h b/settings.h new file mode 100644 index 00000000..91699542 --- /dev/null +++ b/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/ui/bordermetatilespixmapitem.cpp b/ui/bordermetatilespixmapitem.cpp new file mode 100644 index 00000000..4886e42e --- /dev/null +++ b/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/ui/bordermetatilespixmapitem.h b/ui/bordermetatilespixmapitem.h new file mode 100644 index 00000000..11aae827 --- /dev/null +++ b/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/ui/collisionpixmapitem.cpp b/ui/collisionpixmapitem.cpp new file mode 100644 index 00000000..839cf24d --- /dev/null +++ b/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/ui/collisionpixmapitem.h b/ui/collisionpixmapitem.h new file mode 100644 index 00000000..82f4aed3 --- /dev/null +++ b/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/ui/connectionpixmapitem.cpp b/ui/connectionpixmapitem.cpp new file mode 100644 index 00000000..5330fd9f --- /dev/null +++ b/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/ui/connectionpixmapitem.h b/ui/connectionpixmapitem.h new file mode 100644 index 00000000..a4cdf4e2 --- /dev/null +++ b/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/ui/currentselectedmetatilespixmapitem.cpp b/ui/currentselectedmetatilespixmapitem.cpp new file mode 100644 index 00000000..e0b2f7d6 --- /dev/null +++ b/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/ui/currentselectedmetatilespixmapitem.h b/ui/currentselectedmetatilespixmapitem.h new file mode 100644 index 00000000..70b3b29e --- /dev/null +++ b/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/graphicsview.cpp b/ui/graphicsview.cpp old mode 100755 new mode 100644 similarity index 96% rename from graphicsview.cpp rename to ui/graphicsview.cpp index 02204103..778436df --- a/graphicsview.cpp +++ b/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/graphicsview.h b/ui/graphicsview.h old mode 100755 new mode 100644 similarity index 95% rename from graphicsview.h rename to ui/graphicsview.h index d4e41b25..0b3a12c7 --- a/graphicsview.h +++ b/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/ui/mappixmapitem.cpp b/ui/mappixmapitem.cpp new file mode 100644 index 00000000..8c3ef46f --- /dev/null +++ b/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/ui/mappixmapitem.h b/ui/mappixmapitem.h new file mode 100644 index 00000000..812cb356 --- /dev/null +++ b/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