diff --git a/editor.cpp b/editor.cpp index 510c3bad..a3e94207 100755 --- a/editor.cpp +++ b/editor.cpp @@ -882,9 +882,6 @@ void MetatilesPixmapItem::updateSelection(QPointF pos, Qt::MouseButton button) { map->paint_tile_index = baseTileY * 8 + baseTileX; map->paint_tile_width = abs(map->paint_metatile_initial_x - x) + 1; map->paint_tile_height = abs(map->paint_metatile_initial_y - y) + 1; - map->smart_paths_enabled = button == Qt::RightButton - && map->paint_tile_width == 3 - && map->paint_tile_height == 3; emit map->paintTileChanged(map); } } @@ -1032,7 +1029,7 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { int x = (int)(pos.x()) / 16; int y = (int)(pos.y()) / 16; - if (map->smart_paths_enabled) { + if (map->smart_paths_enabled && map->paint_tile_width == 3 && map->paint_tile_height == 3) { paintSmartPath(x, y); } else { paintNormal(x, y); @@ -1160,11 +1157,163 @@ void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { QPointF pos = event->pos(); int x = (int)(pos.x()) / 16; int y = (int)(pos.y()) / 16; - map->floodFill(x, y, map->getSelectedBlockIndex(map->paint_tile_index)); + Block *block = map->getBlock(x, y); + int tile = map->getSelectedBlockIndex(map->paint_tile_index); + if (block && block->tile != tile) { + if (map->smart_paths_enabled && map->paint_tile_width == 3 && map->paint_tile_height == 3) + this->_floodFillSmartPath(x, y); + else + this->_floodFill(x, y); + } + + if (event->type() == QEvent::GraphicsSceneMouseRelease) { + map->commit(); + } draw(); } } +void MapPixmapItem::_floodFill(int initialX, int initialY) { + QList todo; + todo.append(QPoint(initialX, initialY)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + int x = point.x(); + int y = point.y(); + + Block *block = map->getBlock(x, y); + if (block == NULL) { + continue; + } + + int xDiff = x - initialX; + int yDiff = y - initialY; + int i = xDiff % map->paint_tile_width; + int j = yDiff % map->paint_tile_height; + if (i < 0) i = map->paint_tile_width + i; + if (j < 0) j = map->paint_tile_height + j; + int tile = map->getSelectedBlockIndex(map->paint_tile_index + i + (j * 8)); + uint old_tile = block->tile; + if (old_tile == tile) { + continue; + } + + block->tile = tile; + map->_setBlock(x, y, *block); + if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x + 1, y)); + } + if ((block = map->getBlock(x - 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x - 1, y)); + } + if ((block = map->getBlock(x, y + 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y + 1)); + } + if ((block = map->getBlock(x, y - 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y - 1)); + } + } +} + +void MapPixmapItem::_floodFillSmartPath(int initialX, int initialY) { + // Smart path should never be enabled without a 3x3 block selection. + if (map->paint_tile_width != 3 || map->paint_tile_height != 3) return; + + // Shift to the middle tile of the smart path selection. + int openTile = map->paint_tile_index + 8 + 1; + + // 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 == NULL) { + continue; + } + + int tile = map->getSelectedBlockIndex(openTile); + uint 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)); + } + } + + // Go back and resolve the flood-filled edge tiles. + // Mark tiles as visited while we go. + bool visited[map->getWidth() * map->getHeight()]; + for (int i = 0; i < sizeof visited; 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 == NULL) { + continue; + } + + int id = 0; + Block *top = map->getBlock(x, y - 1); + Block *right = map->getBlock(x + 1, y); + Block *bottom = map->getBlock(x, y + 1); + Block *left = map->getBlock(x - 1, y); + + // Get marching squares value, to determine which tile to use. + if (top && IS_SMART_PATH_TILE(top)) + id += 1; + if (right && IS_SMART_PATH_TILE(right)) + id += 2; + if (bottom && IS_SMART_PATH_TILE(bottom)) + id += 4; + if (left && IS_SMART_PATH_TILE(left)) + id += 8; + + block->tile = map->getSelectedBlockIndex(map->paint_tile_index + 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; + } + } +} + void MapPixmapItem::pick(QGraphicsSceneMouseEvent *event) { QPointF pos = event->pos(); int x = (int)(pos.x()) / 16; diff --git a/editor.h b/editor.h index 916f2521..c096d1a5 100755 --- a/editor.h +++ b/editor.h @@ -250,6 +250,8 @@ public: 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 draw(bool ignoreCache = false); diff --git a/mainwindow.cpp b/mainwindow.cpp index 6d9ac7a5..87254a63 100755 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -909,3 +909,8 @@ void MainWindow::on_pushButton_clicked() onMapNeedsRedrawing(editor->map); } } + +void MainWindow::on_checkBox_smartPaths_stateChanged(int selected) +{ + editor->map->smart_paths_enabled = selected == Qt::Checked; +} diff --git a/mainwindow.h b/mainwindow.h index 530094d1..04b4cdb6 100755 --- a/mainwindow.h +++ b/mainwindow.h @@ -96,6 +96,8 @@ private slots: void on_pushButton_clicked(); + void on_checkBox_smartPaths_stateChanged(int selected); + private: Ui::MainWindow *ui; QStandardItemModel *mapListModel; diff --git a/mainwindow.ui b/mainwindow.ui index 74c2cf41..59f41023 100755 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -228,10 +228,20 @@ - + margin-left: 10px + + Smart Paths + + + + + + + + Show Grid diff --git a/map.cpp b/map.cpp index 98b859ce..9c76674f 100755 --- a/map.cpp +++ b/map.cpp @@ -389,7 +389,7 @@ void Map::drawSelection(int i, int w, int selectionWidth, int selectionHeight, Q int y = i / w; painter->save(); - QColor penColor = smart_paths_enabled ? QColor(0xff, 0x0, 0xff) : QColor(0xff, 0xff, 0xff); + QColor penColor = QColor(0xff, 0xff, 0xff); painter->setPen(penColor); int rectWidth = selectionWidth * 16; int rectHeight = selectionHeight * 16; @@ -477,38 +477,6 @@ void Map::_setBlock(int x, int y, Block block) { } } -void Map::_floodFill(int x, int y, uint tile) { - 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 == NULL) { - continue; - } - uint old_tile = block->tile; - if (old_tile == tile) { - continue; - } - block->tile = tile; - _setBlock(x, y, *block); - if ((block = getBlock(x + 1, y)) && block->tile == old_tile) { - todo.append(QPoint(x + 1, y)); - } - if ((block = getBlock(x - 1, y)) && block->tile == old_tile) { - todo.append(QPoint(x - 1, y)); - } - if ((block = getBlock(x, y + 1)) && block->tile == old_tile) { - todo.append(QPoint(x, y + 1)); - } - if ((block = getBlock(x, y - 1)) && block->tile == old_tile) { - todo.append(QPoint(x, y - 1)); - } - } -} - void Map::_floodFillCollision(int x, int y, uint collision) { QList todo; todo.append(QPoint(x, y)); @@ -666,14 +634,6 @@ void Map::setBlock(int x, int y, Block block) { } } -void Map::floodFill(int x, int y, uint tile) { - Block *block = getBlock(x, y); - if (block && block->tile != tile) { - _floodFill(x, y, tile); - commit(); - } -} - void Map::floodFillCollision(int x, int y, uint collision) { Block *block = getBlock(x, y); if (block && block->collision != collision) { diff --git a/map.h b/map.h index 19c06c03..1270ce3b 100755 --- a/map.h +++ b/map.h @@ -184,8 +184,6 @@ public: void setBlock(int x, int y, Block block); void _setBlock(int x, int y, Block block); - void floodFill(int x, int y, uint tile); - void _floodFill(int x, int y, uint tile); void floodFillCollision(int x, int y, uint collision); void _floodFillCollision(int x, int y, uint collision); void floodFillElevation(int x, int y, uint elevation);