diff --git a/include/mainwindow.h b/include/mainwindow.h index af0649c8..8c343220 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -41,6 +41,23 @@ public: Q_INVOKABLE QJSValue getBlock(int x, int y); Q_INVOKABLE void setBlock(int x, int y, int tile, int collision, int elevation); Q_INVOKABLE void setBlocksFromSelection(int x, int y); + Q_INVOKABLE int getMetatileId(int x, int y); + Q_INVOKABLE void setMetatileId(int x, int y, int metatileId); + Q_INVOKABLE int getCollision(int x, int y); + Q_INVOKABLE void setCollision(int x, int y, int collision); + Q_INVOKABLE int getElevation(int x, int y); + Q_INVOKABLE void setElevation(int x, int y, int elevation); + Q_INVOKABLE void bucketFill(int x, int y, int metatileId); + Q_INVOKABLE void bucketFillFromSelection(int x, int y); + Q_INVOKABLE void magicFill(int x, int y, int metatileId); + Q_INVOKABLE void magicFillFromSelection(int x, int y); + Q_INVOKABLE void shift(int xDelta, int yDelta); + Q_INVOKABLE QJSValue getDimensions(); + Q_INVOKABLE int getWidth(); + Q_INVOKABLE int getHeight(); + Q_INVOKABLE void setDimensions(int width, int height); + Q_INVOKABLE void setWidth(int width); + Q_INVOKABLE void setHeight(int height); public slots: void scaleMapView(int); @@ -210,6 +227,7 @@ private: QString getDefaultMap(); void setRecentMap(QString map_name); QStandardItem* createMapItem(QString mapName, int groupNum, int inGroupNum); + static bool mapDimensionsValid(int width, int height); void drawMapListIcons(QAbstractItemModel *model); void updateMapList(); diff --git a/include/scripting.h b/include/scripting.h index d063b10e..84468e11 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -16,6 +16,7 @@ class Scripting public: Scripting(MainWindow *mainWindow); static QJSValue fromBlock(Block block); + static QJSValue dimensions(int width, int height); static void init(MainWindow *mainWindow); static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock); diff --git a/include/ui/mappixmapitem.h b/include/ui/mappixmapitem.h index 6ea8a090..a361ce37 100644 --- a/include/ui/mappixmapitem.h +++ b/include/ui/mappixmapitem.h @@ -38,14 +38,31 @@ public: virtual void paint(QGraphicsSceneMouseEvent*); virtual void floodFill(QGraphicsSceneMouseEvent*); virtual void magicFill(QGraphicsSceneMouseEvent*); - void _floodFill(int x, int y); - void _floodFillSmartPath(int initialX, int initialY); + void magicFill(int x, int y, uint16_t metatileId, bool fromScriptCall = false); + void magicFill(int x, int y, bool fromScriptCall = false); + void magicFill( + int initialX, + int initialY, + QPoint selectionDimensions, + QList *selectedMetatiles, + QList> *selectedCollisions, + bool fromScriptCall = false); + void floodFill(int x, int y, bool fromScriptCall = false); + void floodFill(int x, int y, uint16_t metatileId, bool fromScriptCall = false); + void floodFill(int initialX, + int initialY, + QPoint selectionDimensions, + QList *selectedMetatiles, + QList> *selectedCollisions, + bool fromScriptCall = false); + void floodFillSmartPath(int initialX, int initialY); virtual void pick(QGraphicsSceneMouseEvent*); virtual void select(QGraphicsSceneMouseEvent*); virtual void shift(QGraphicsSceneMouseEvent*); + void shift(int xDelta, int yDelta); virtual void draw(bool ignoreCache = false); void updateMetatileSelection(QGraphicsSceneMouseEvent *event); - void paintNormal(int x, int y, bool fromScriptCallback = false); + void paintNormal(int x, int y, bool fromScriptCall = false); private: void paintSmartPath(int x, int y); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e32e3cc8..05ebd6ea 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2305,7 +2305,7 @@ void MainWindow::on_pushButton_ChangeDimensions_clicked() int realWidth = widthSpinBox->value() + 15; int realHeight = heightSpinBox->value() + 14; int numMetatiles = realWidth * realHeight; - if (numMetatiles <= 0x2800) { + if (MainWindow::mapDimensionsValid(widthSpinBox->value(), heightSpinBox->value())) { dialog.accept(); } else { QString errorText = QString("Error: The specified width and height are too large.\n" @@ -2330,6 +2330,17 @@ void MainWindow::on_pushButton_ChangeDimensions_clicked() } } +bool MainWindow::mapDimensionsValid(int width, int height) { + // Ensure width and height are an acceptable size. + // The maximum number of metatiles in a map is the following: + // max = (width + 15) * (height + 14) + // This limit can be found in fieldmap.c in pokeruby/pokeemerald. + int realWidth = width + 15; + int realHeight = height + 14; + int numMetatiles = realWidth * realHeight; + return numMetatiles <= 0x2800; +} + void MainWindow::on_checkBox_smartPaths_stateChanged(int selected) { bool enabled = selected == Qt::Checked; @@ -2566,3 +2577,140 @@ void MainWindow::setBlocksFromSelection(int x, int y) { } } +int MainWindow::getMetatileId(int x, int y) { + if (!this->editor || !this->editor->map) + return 0; + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return 0; + } + return block->tile; +} + +void MainWindow::setMetatileId(int x, int y, int metatileId) { + if (!this->editor || !this->editor->map) + return; + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return; + } + this->editor->map->setBlock(x, y, Block(metatileId, block->collision, block->elevation)); +} + +int MainWindow::getCollision(int x, int y) { + if (!this->editor || !this->editor->map) + return 0; + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return 0; + } + return block->collision; +} + +void MainWindow::setCollision(int x, int y, int collision) { + if (!this->editor || !this->editor->map) + return; + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return; + } + this->editor->map->setBlock(x, y, Block(block->tile, collision, block->elevation)); +} + +int MainWindow::getElevation(int x, int y) { + if (!this->editor || !this->editor->map) + return 0; + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return 0; + } + return block->elevation; +} + +void MainWindow::setElevation(int x, int y, int elevation) { + if (!this->editor || !this->editor->map) + return; + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return; + } + this->editor->map->setBlock(x, y, Block(block->tile, block->collision, elevation)); +} + +void MainWindow::bucketFill(int x, int y, int metatileId) { + if (!this->editor || !this->editor->map) + return; + this->editor->map_item->floodFill(x, y, metatileId, true); +} + +void MainWindow::bucketFillFromSelection(int x, int y) { + if (!this->editor || !this->editor->map) + return; + this->editor->map_item->floodFill(x, y, true); +} + +void MainWindow::magicFill(int x, int y, int metatileId) { + if (!this->editor || !this->editor->map) + return; + this->editor->map_item->magicFill(x, y, metatileId, true); +} + +void MainWindow::magicFillFromSelection(int x, int y) { + if (!this->editor || !this->editor->map) + return; + this->editor->map_item->magicFill(x, y, true); +} + +void MainWindow::shift(int xDelta, int yDelta) { + if (!this->editor || !this->editor->map) + return; + this->editor->map_item->shift(xDelta, yDelta); +} + +QJSValue MainWindow::getDimensions() { + if (!this->editor || !this->editor->map) + return QJSValue(); + return Scripting::dimensions(this->editor->map->getWidth(), this->editor->map->getHeight()); +} + +int MainWindow::getWidth() { + if (!this->editor || !this->editor->map) + return 0; + return this->editor->map->getWidth(); +} + +int MainWindow::getHeight() { + if (!this->editor || !this->editor->map) + return 0; + return this->editor->map->getHeight(); +} + +void MainWindow::setDimensions(int width, int height) { + if (!this->editor || !this->editor->map) + return; + if (!MainWindow::mapDimensionsValid(width, height)) + return; + this->editor->map->setDimensions(width, height); + this->editor->map->commit(); + this->onMapNeedsRedrawing(); +} + +void MainWindow::setWidth(int width) { + if (!this->editor || !this->editor->map) + return; + if (!MainWindow::mapDimensionsValid(width, this->editor->map->getHeight())) + return; + this->editor->map->setDimensions(width, this->editor->map->getHeight()); + this->editor->map->commit(); + this->onMapNeedsRedrawing(); +} + +void MainWindow::setHeight(int height) { + if (!this->editor || !this->editor->map) + return; + if (!MainWindow::mapDimensionsValid(this->editor->map->getWidth(), height)) + return; + this->editor->map->setDimensions(this->editor->map->getWidth(), height); + this->editor->map->commit(); + this->onMapNeedsRedrawing(); +} diff --git a/src/scripting.cpp b/src/scripting.cpp index fbc5a33c..f946bef5 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -73,3 +73,10 @@ QJSValue Scripting::fromBlock(Block block) { obj.setProperty("rawValue", block.rawValue()); return obj; } + +QJSValue Scripting::dimensions(int width, int height) { + QJSValue obj = instance->engine->newObject(); + obj.setProperty("width", width); + obj.setProperty("height", height); + return obj; +} diff --git a/src/ui/mappixmapitem.cpp b/src/ui/mappixmapitem.cpp index b2afbbd0..5b565be2 100644 --- a/src/ui/mappixmapitem.cpp +++ b/src/ui/mappixmapitem.cpp @@ -42,24 +42,7 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { 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; + this->shift(xDelta, yDelta); selection_origin = QPoint(x, y); selection.clear(); draw(); @@ -69,12 +52,33 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCallback) { +void MapPixmapItem::shift(int xDelta, int yDelta) { + 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; +} + +void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCall) { QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); - int initialX = fromScriptCallback ? x : this->paint_tile_initial_x; - int initialY = fromScriptCallback ? y : this->paint_tile_initial_y; + int initialX = fromScriptCall ? x : this->paint_tile_initial_x; + int initialY = fromScriptCall ? y : this->paint_tile_initial_y; // Snap the selected position to the top-left of the block boundary. // This allows painting via dragging the mouse to tile the painted region. @@ -98,7 +102,7 @@ void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCallback) { block->collision = selectedCollisions->at(index).first; block->elevation = selectedCollisions->at(index).second; } - map->setBlock(actualX, actualY, *block, !fromScriptCallback); + map->setBlock(actualX, actualY, *block, !fromScriptCall); } } } @@ -271,9 +275,9 @@ void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { if (selectedMetatiles->count() > 1 || (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); + this->floodFillSmartPath(x, y); else - this->_floodFill(x, y); + this->floodFill(x, y); } } @@ -289,46 +293,80 @@ void MapPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { QPointF pos = event->pos(); int initialX = static_cast(pos.x()) / 16; int initialY = static_cast(pos.y()) / 16; - Block *block = map->getBlock(initialX, initialY); - - if (block) { - QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); - QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); - bool setCollisions = selectedCollisions && selectedCollisions->length() == selectedMetatiles->length(); - QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); - uint16_t tile = block->tile; - - for (int y = 0; y < map->getHeight(); y++) { - for (int x = 0; x < map->getWidth(); x++) { - block = map->getBlock(x, y); - if (block && block->tile == tile) { - 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; - int index = j * selectionDimensions.x() + i; - block->tile = selectedMetatiles->at(index); - if (setCollisions) { - block->collision = selectedCollisions->at(index).first; - block->elevation = selectedCollisions->at(index).second; - } - map->setBlock(x, y, *block); - } - } - } - } + this->magicFill(initialX, initialY); } draw(); } } -void MapPixmapItem::_floodFill(int initialX, int initialY) { +void MapPixmapItem::magicFill(int x, int y, uint16_t metatileId, bool fromScriptCall) { + QPoint selectionDimensions(1, 1); + QList *selectedMetatiles = new QList({ metatileId }); + this->magicFill(x, y, selectionDimensions, selectedMetatiles, nullptr, fromScriptCall); +} + +void MapPixmapItem::magicFill(int x, int y, bool fromScriptCall) { QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); + this->magicFill(x, y, selectionDimensions, selectedMetatiles, selectedCollisions, fromScriptCall); +} + +void MapPixmapItem::magicFill( + int initialX, + int initialY, + QPoint selectionDimensions, + QList *selectedMetatiles, + QList> *selectedCollisions, + bool fromScriptCall) { + Block *block = map->getBlock(initialX, initialY); + if (block) { + bool setCollisions = selectedCollisions && selectedCollisions->length() == selectedMetatiles->length(); + uint16_t tile = block->tile; + for (int y = 0; y < map->getHeight(); y++) { + for (int x = 0; x < map->getWidth(); x++) { + block = map->getBlock(x, y); + if (block && block->tile == tile) { + 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; + int index = j * selectionDimensions.x() + i; + block->tile = selectedMetatiles->at(index); + if (setCollisions) { + block->collision = selectedCollisions->at(index).first; + block->elevation = selectedCollisions->at(index).second; + } + map->setBlock(x, y, *block, !fromScriptCall); + } + } + } + } +} + +void MapPixmapItem::floodFill(int initialX, int initialY, bool fromScriptCall) { + QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); + QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); + QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); + this->floodFill(initialX, initialY, selectionDimensions, selectedMetatiles, selectedCollisions, fromScriptCall); +} + +void MapPixmapItem::floodFill(int initialX, int initialY, uint16_t metatileId, bool fromScriptCall) { + QPoint selectionDimensions(1, 1); + QList *selectedMetatiles = new QList({ metatileId }); + this->floodFill(initialX, initialY, selectionDimensions, selectedMetatiles, nullptr, fromScriptCall); +} + +void MapPixmapItem::floodFill( + int initialX, + int initialY, + QPoint selectionDimensions, + QList *selectedMetatiles, + QList> *selectedCollisions, + bool fromScriptCall) { bool setCollisions = selectedCollisions && selectedCollisions->length() == selectedMetatiles->length(); int numMetatiles = map->getWidth() * map->getHeight(); @@ -364,7 +402,7 @@ void MapPixmapItem::_floodFill(int initialX, int initialY) { block->collision = selectedCollisions->at(index).first; block->elevation = selectedCollisions->at(index).second; } - map->setBlock(x, y, *block); + map->setBlock(x, y, *block, !fromScriptCall); } if (!visited[x + 1 + y * map->getWidth()] && (block = map->getBlock(x + 1, y)) && block->tile == old_tile) { todo.append(QPoint(x + 1, y)); @@ -387,7 +425,7 @@ void MapPixmapItem::_floodFill(int initialX, int initialY) { delete[] visited; } -void MapPixmapItem::_floodFillSmartPath(int initialX, int initialY) { +void MapPixmapItem::floodFillSmartPath(int initialX, int initialY) { QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); diff --git a/test_script.js b/test_script.js index df98dc42..4cf9362e 100644 --- a/test_script.js +++ b/test_script.js @@ -9,11 +9,11 @@ const grassTiles = [0x8, 0x9, 0x10, 0x11]; // Porymap callback when a block is painted. export function on_block_changed(x, y, prevBlock, newBlock) { try { - if (grassTiles.indexOf(newBlock.tile) != -1) { - const i = randInt(0, grassTiles.length); - map.setBlock(x, y, grassTiles[i], newBlock.collision, newBlock.elevation); - } - map.setBlocksFromSelection(1, 1) + // if (grassTiles.indexOf(newBlock.tile) != -1) { + // const i = randInt(0, grassTiles.length); + // map.setBlock(x, y, grassTiles[i], newBlock.collision, newBlock.elevation); + // } + console.log("ran", map.getWidth(), map.getHeight()); } catch(err) { console.log(err); }