From 267cd5e2cbd3b8d10d320ffdff2066392c7aa358 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sun, 26 Apr 2020 19:38:20 -0500 Subject: [PATCH 01/17] Implement proof of concept for scripting capabilities --- include/core/block.h | 1 + include/core/map.h | 3 +- include/mainwindow.h | 2 + include/scripting.h | 31 +++++++++++++++ porymap.pro | 6 ++- src/core/block.cpp | 6 +++ src/core/map.cpp | 21 +++++------ src/mainwindow.cpp | 7 ++++ src/scripting.cpp | 69 ++++++++++++++++++++++++++++++++++ src/ui/collisionpixmapitem.cpp | 2 +- src/ui/mappixmapitem.cpp | 16 ++++---- test_script.js | 15 ++++++++ 12 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 include/scripting.h create mode 100644 src/scripting.cpp create mode 100644 test_script.js diff --git a/include/core/block.h b/include/core/block.h index b31f18b4..798e179c 100644 --- a/include/core/block.h +++ b/include/core/block.h @@ -8,6 +8,7 @@ class Block public: Block(); Block(uint16_t); + Block(uint16_t tile, uint16_t collision, uint16_t elevation); Block(const Block&); bool operator ==(Block); bool operator !=(Block); diff --git a/include/core/map.h b/include/core/map.h index 32eb6e70..33a9db68 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -74,8 +74,7 @@ public: 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 setBlock(int x, int y, Block block, bool invokeCallback = false); 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 magicFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); diff --git a/include/mainwindow.h b/include/mainwindow.h index 1e5a65be..117b264e 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -37,6 +37,8 @@ public: MainWindow(const MainWindow &) = delete; MainWindow & operator = (const MainWindow &) = delete; + Q_INVOKABLE void scriptapi_setBlock(int x, int y, int tile, int collision, int elevation); + public slots: void scaleMapView(int); diff --git a/include/scripting.h b/include/scripting.h new file mode 100644 index 00000000..c5dc4323 --- /dev/null +++ b/include/scripting.h @@ -0,0 +1,31 @@ +#ifndef SCRIPTING_H +#define SCRIPTING_H + +#include "mainwindow.h" +#include "block.h" + +#include +#include + +enum CallbackType { + OnBlockChanged, +}; + +class Scripting +{ +public: + Scripting(MainWindow *mainWindow); + QJSValue newBlockObject(Block block); + static void init(MainWindow *mainWindow); + static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock); + +private: + QJSEngine *engine; + QStringList filepaths; + QList modules; + + void loadModules(QStringList moduleFiles); + void invokeCallback(CallbackType type, QJSValueList args); +}; + +#endif // SCRIPTING_H diff --git a/porymap.pro b/porymap.pro index ad12b9f0..1a58b420 100644 --- a/porymap.pro +++ b/porymap.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui +QT += core gui qml greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -70,6 +70,7 @@ SOURCES += src/core/block.cpp \ src/main.cpp \ src/mainwindow.cpp \ src/project.cpp \ + src/scripting.cpp \ src/settings.cpp \ src/log.cpp \ src/ui/newtilesetdialog.cpp @@ -94,7 +95,7 @@ HEADERS += include/core/block.h \ include/core/wildmoninfo.h \ include/lib/orderedmap.h \ include/lib/orderedjson.h \ - include/ui/aboutporymap.h \ + include/ui/aboutporymap.h \ include/ui/bordermetatilespixmapitem.h \ include/ui/collisionpixmapitem.h \ include/ui/connectionpixmapitem.h \ @@ -132,6 +133,7 @@ HEADERS += include/core/block.h \ include/editor.h \ include/mainwindow.h \ include/project.h \ + include/scripting.h \ include/settings.h \ include/log.h \ include/ui/newtilesetdialog.h diff --git a/src/core/block.cpp b/src/core/block.cpp index bc65c923..2a6b057d 100644 --- a/src/core/block.cpp +++ b/src/core/block.cpp @@ -3,6 +3,12 @@ Block::Block() : tile(0), collision(0), elevation(0) { } +Block::Block(uint16_t tile, uint16_t collision, uint16_t elevation) { + this->tile = tile; + this->collision = collision; + this->elevation = elevation; +} + Block::Block(uint16_t word) { tile = word & 0x3ff; diff --git a/src/core/map.cpp b/src/core/map.cpp index 667c151a..aada600b 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -2,6 +2,7 @@ #include "historyitem.h" #include "map.h" #include "imageproviders.h" +#include "scripting.h" #include #include @@ -363,10 +364,14 @@ Block* Map::getBlock(int x, int y) { return nullptr; } -void Map::_setBlock(int x, int y, Block block) { +void Map::setBlock(int x, int y, Block block, bool invokeCallback) { int i = y * getWidth() + x; - if (layout->blockdata && layout->blockdata->blocks) { + if (layout->blockdata && layout->blockdata->blocks && i < layout->blockdata->blocks->size()) { + Block prevBlock = layout->blockdata->blocks->value(i); layout->blockdata->blocks->replace(i, block); + if (invokeCallback) { + Scripting::cb_MetatileChanged(x, y, prevBlock, block); + } } } @@ -390,7 +395,7 @@ void Map::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_ block->collision = collision; block->elevation = elevation; - _setBlock(x, y, *block); + setBlock(x, y, *block); if ((block = getBlock(x + 1, y)) && block->collision == old_coll && block->elevation == old_elev) { todo.append(QPoint(x + 1, y)); } @@ -495,14 +500,6 @@ void Map::commit() { } } -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)) { @@ -523,7 +520,7 @@ void Map::magicFillCollisionElevation(int initialX, int initialY, uint16_t colli if (block && block->collision == old_coll && block->elevation == old_elev) { block->collision = collision; block->elevation = elevation; - _setBlock(x, y, *block); + setBlock(x, y, *block); } } } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2a8cbd77..2e68858e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -8,6 +8,8 @@ #include "ui_eventpropertiesframe.h" #include "bordermetatilespixmapitem.h" #include "currentselectedmetatilespixmapitem.h" +#include "customattributestable.h" +#include "scripting.h" #include #include @@ -44,6 +46,7 @@ MainWindow::MainWindow(QWidget *parent) : QCoreApplication::setApplicationName("porymap"); QApplication::setApplicationDisplayName("porymap"); QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-2.ico")); + Scripting::init(this); ui->setupUi(this); this->initWindow(); @@ -2540,3 +2543,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { QMainWindow::closeEvent(event); } + +void MainWindow::scriptapi_setBlock(int x, int y, int tile, int collision, int elevation) { + this->editor->map->setBlock(x, y, Block(tile, collision, elevation)); +} diff --git a/src/scripting.cpp b/src/scripting.cpp new file mode 100644 index 00000000..48ec12e3 --- /dev/null +++ b/src/scripting.cpp @@ -0,0 +1,69 @@ +#include "scripting.h" +#include "log.h" + +QMap callbackFunctions = { + {OnBlockChanged, "on_block_changed"}, +}; + +Scripting *instance = nullptr; + +void Scripting::init(MainWindow *mainWindow) { + instance = new Scripting(mainWindow); +} + +Scripting::Scripting(MainWindow *mainWindow) { + this->engine = new QJSEngine(mainWindow); + this->engine->installExtensions(QJSEngine::ConsoleExtension); + this->engine->globalObject().setProperty("api", this->engine->newQObject(mainWindow)); + this->filepaths.append("D:\\devkitProOld\\msys\\home\\huder\\pretmap\\test_script.js"); + this->loadModules(this->filepaths); +} + +void Scripting::loadModules(QStringList moduleFiles) { + for (QString filepath : moduleFiles) { + QJSValue module = this->engine->importModule(filepath); + if (module.isError()) { + logError(QString("Failed to load custom script file '%1'").arg(filepath)); + continue; + } + + this->modules.append(module); + } +} + +void Scripting::invokeCallback(CallbackType type, QJSValueList args) { + for (QJSValue module : this->modules) { + QString functionName = callbackFunctions[type]; + QJSValue callbackFunction = module.property(functionName); + if (callbackFunction.isError()) { + continue; + } + + QJSValue result = callbackFunction.call(args); + if (result.isError()) { + logError(QString("Module %1 encountered an error when calling '%2'").arg(module.toString()).arg(functionName)); + continue; + } + } +} + +void Scripting::cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock) { + if (!instance) return; + + QJSValueList args { + x, + y, + instance->newBlockObject(prevBlock), + instance->newBlockObject(newBlock), + }; + instance->invokeCallback(OnBlockChanged, args); +} + +QJSValue Scripting::newBlockObject(Block block) { + QJSValue obj = this->engine->newObject(); + obj.setProperty("tile", block.tile); + obj.setProperty("collision", block.collision); + obj.setProperty("elevation", block.elevation); + obj.setProperty("rawValue", block.rawValue()); + return obj; +} diff --git a/src/ui/collisionpixmapitem.cpp b/src/ui/collisionpixmapitem.cpp index 94589a8d..252b9f1a 100644 --- a/src/ui/collisionpixmapitem.cpp +++ b/src/ui/collisionpixmapitem.cpp @@ -39,7 +39,7 @@ void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { if (block) { block->collision = this->movementPermissionsSelector->getSelectedCollision(); block->elevation = this->movementPermissionsSelector->getSelectedElevation(); - map->_setBlock(x, y, *block); + map->setBlock(x, y, *block); } if (event->type() == QEvent::GraphicsSceneMouseRelease) { map->commit(); diff --git a/src/ui/mappixmapitem.cpp b/src/ui/mappixmapitem.cpp index cf67e8c9..412892d2 100644 --- a/src/ui/mappixmapitem.cpp +++ b/src/ui/mappixmapitem.cpp @@ -56,7 +56,7 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { int blockIndex = j * map->getWidth() + i; Block srcBlock = backupBlockdata->blocks->at(blockIndex); - map->_setBlock(destX, destY, srcBlock); + map->setBlock(destX, destY, srcBlock); } delete backupBlockdata; @@ -96,7 +96,7 @@ void MapPixmapItem::paintNormal(int x, int y) { block->collision = selectedCollisions->at(index).first; block->elevation = selectedCollisions->at(index).second; } - map->_setBlock(actualX, actualY, *block); + map->setBlock(actualX, actualY, *block, true); } } } @@ -159,7 +159,7 @@ void MapPixmapItem::paintSmartPath(int x, int y) { block->collision = openTileCollision; block->elevation = openTileElevation; } - map->_setBlock(actualX, actualY, *block); + map->setBlock(actualX, actualY, *block); } } @@ -203,7 +203,7 @@ void MapPixmapItem::paintSmartPath(int x, int y) { block->collision = selectedCollisions->at(smartPathTable[id]).first; block->elevation = selectedCollisions->at(smartPathTable[id]).second; } - map->_setBlock(actualX, actualY, *block); + map->setBlock(actualX, actualY, *block); } } @@ -312,7 +312,7 @@ void MapPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { block->collision = selectedCollisions->at(index).first; block->elevation = selectedCollisions->at(index).second; } - map->_setBlock(x, y, *block); + map->setBlock(x, y, *block); } } } @@ -362,7 +362,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); } if (!visited[x + 1 + y * map->getWidth()] && (block = map->getBlock(x + 1, y)) && block->tile == old_tile) { todo.append(QPoint(x + 1, y)); @@ -427,7 +427,7 @@ void MapPixmapItem::_floodFillSmartPath(int initialX, int initialY) { block->collision = openTileCollision; block->elevation = openTileElevation; } - map->_setBlock(x, y, *block); + map->setBlock(x, y, *block); if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) { todo.append(QPoint(x + 1, y)); } @@ -482,7 +482,7 @@ void MapPixmapItem::_floodFillSmartPath(int initialX, int initialY) { block->collision = selectedCollisions->at(smartPathTable[id]).first; block->elevation = selectedCollisions->at(smartPathTable[id]).second; } - map->_setBlock(x, y, *block); + 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)) { diff --git a/test_script.js b/test_script.js new file mode 100644 index 00000000..52f654fd --- /dev/null +++ b/test_script.js @@ -0,0 +1,15 @@ +function randInt(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min)) + min; +} + +const grassTiles = [0x8, 0x9, 0x10, 0x11]; + +// Porymap callback when a block is painted. +export function on_block_changed(x, y, prevBlock, newBlock) { + if (grassTiles.indexOf(newBlock.tile) != -1) { + const i = randInt(0, grassTiles.length); + api.scriptapi_setBlock(x, y, grassTiles[i], newBlock.collision, newBlock.elevation); + } +} From d685718f8d23909ffa03692187838742bf79753c Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Wed, 29 Apr 2020 20:41:19 -0500 Subject: [PATCH 02/17] Implement three map scripting functions --- include/core/map.h | 2 +- include/mainwindow.h | 5 ++++- include/scripting.h | 2 +- include/ui/mappixmapitem.h | 2 +- src/core/map.cpp | 4 ++-- src/mainwindow.cpp | 21 ++++++++++++++++++++- src/scripting.cpp | 18 ++++++++++++------ src/ui/mappixmapitem.cpp | 14 ++++++++------ test_script.js | 11 ++++++++--- 9 files changed, 57 insertions(+), 22 deletions(-) diff --git a/include/core/map.h b/include/core/map.h index 33a9db68..1afd69c3 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -74,7 +74,7 @@ public: void cacheBlockdata(); void cacheCollision(); Block *getBlock(int x, int y); - void setBlock(int x, int y, Block block, bool invokeCallback = false); + void setBlock(int x, int y, Block block, bool enableScriptCallback = false); 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 magicFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); diff --git a/include/mainwindow.h b/include/mainwindow.h index 117b264e..af0649c8 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "project.h" #include "config.h" #include "map.h" @@ -37,7 +38,9 @@ public: MainWindow(const MainWindow &) = delete; MainWindow & operator = (const MainWindow &) = delete; - Q_INVOKABLE void scriptapi_setBlock(int x, int y, int tile, int collision, int elevation); + 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); public slots: void scaleMapView(int); diff --git a/include/scripting.h b/include/scripting.h index c5dc4323..d063b10e 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -15,7 +15,7 @@ class Scripting { public: Scripting(MainWindow *mainWindow); - QJSValue newBlockObject(Block block); + static QJSValue fromBlock(Block block); 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 e0111eb0..6ea8a090 100644 --- a/include/ui/mappixmapitem.h +++ b/include/ui/mappixmapitem.h @@ -45,9 +45,9 @@ public: virtual void shift(QGraphicsSceneMouseEvent*); virtual void draw(bool ignoreCache = false); void updateMetatileSelection(QGraphicsSceneMouseEvent *event); + void paintNormal(int x, int y, bool fromScriptCallback = false); private: - void paintNormal(int x, int y); void paintSmartPath(int x, int y); static QList smartPathTable; diff --git a/src/core/map.cpp b/src/core/map.cpp index aada600b..7101da04 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -364,12 +364,12 @@ Block* Map::getBlock(int x, int y) { return nullptr; } -void Map::setBlock(int x, int y, Block block, bool invokeCallback) { +void Map::setBlock(int x, int y, Block block, bool enableScriptCallback) { int i = y * getWidth() + x; if (layout->blockdata && layout->blockdata->blocks && i < layout->blockdata->blocks->size()) { Block prevBlock = layout->blockdata->blocks->value(i); layout->blockdata->blocks->replace(i, block); - if (invokeCallback) { + if (enableScriptCallback) { Scripting::cb_MetatileChanged(x, y, prevBlock, block); } } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2e68858e..e32e3cc8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2544,6 +2544,25 @@ void MainWindow::closeEvent(QCloseEvent *event) { QMainWindow::closeEvent(event); } -void MainWindow::scriptapi_setBlock(int x, int y, int tile, int collision, int elevation) { +QJSValue MainWindow::getBlock(int x, int y) { + if (!this->editor || !this->editor->map) + return QJSValue(); + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return Scripting::fromBlock(Block()); + } + return Scripting::fromBlock(*block); +} + +void MainWindow::setBlock(int x, int y, int tile, int collision, int elevation) { + if (!this->editor || !this->editor->map) + return; this->editor->map->setBlock(x, y, Block(tile, collision, elevation)); } + +void MainWindow::setBlocksFromSelection(int x, int y) { + if (this->editor && this->editor->map_item) { + this->editor->map_item->paintNormal(x, y, true); + } +} + diff --git a/src/scripting.cpp b/src/scripting.cpp index 48ec12e3..fbc5a33c 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -14,7 +14,7 @@ void Scripting::init(MainWindow *mainWindow) { Scripting::Scripting(MainWindow *mainWindow) { this->engine = new QJSEngine(mainWindow); this->engine->installExtensions(QJSEngine::ConsoleExtension); - this->engine->globalObject().setProperty("api", this->engine->newQObject(mainWindow)); + this->engine->globalObject().setProperty("map", this->engine->newQObject(mainWindow)); this->filepaths.append("D:\\devkitProOld\\msys\\home\\huder\\pretmap\\test_script.js"); this->loadModules(this->filepaths); } @@ -23,7 +23,13 @@ void Scripting::loadModules(QStringList moduleFiles) { for (QString filepath : moduleFiles) { QJSValue module = this->engine->importModule(filepath); if (module.isError()) { - logError(QString("Failed to load custom script file '%1'").arg(filepath)); + logError(QString("Failed to load custom script file '%1'\nName: %2\nMessage: %3\nFile: %4\nLine Number: %5\nStack: %6") + .arg(filepath) + .arg(module.property("name").toString()) + .arg(module.property("message").toString()) + .arg(module.property("fileName").toString()) + .arg(module.property("lineNumber").toString()) + .arg(module.property("stack").toString())); continue; } @@ -53,14 +59,14 @@ void Scripting::cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock QJSValueList args { x, y, - instance->newBlockObject(prevBlock), - instance->newBlockObject(newBlock), + instance->fromBlock(prevBlock), + instance->fromBlock(newBlock), }; instance->invokeCallback(OnBlockChanged, args); } -QJSValue Scripting::newBlockObject(Block block) { - QJSValue obj = this->engine->newObject(); +QJSValue Scripting::fromBlock(Block block) { + QJSValue obj = instance->engine->newObject(); obj.setProperty("tile", block.tile); obj.setProperty("collision", block.collision); obj.setProperty("elevation", block.elevation); diff --git a/src/ui/mappixmapitem.cpp b/src/ui/mappixmapitem.cpp index 412892d2..b2afbbd0 100644 --- a/src/ui/mappixmapitem.cpp +++ b/src/ui/mappixmapitem.cpp @@ -69,20 +69,22 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::paintNormal(int x, int y) { +void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCallback) { 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; // 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; + int xDiff = x - initialX; + int yDiff = y - initialY; 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(); + x = initialX + (xDiff / selectionDimensions.x()) * selectionDimensions.x(); + y = initialY + (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++) { @@ -96,7 +98,7 @@ void MapPixmapItem::paintNormal(int x, int y) { block->collision = selectedCollisions->at(index).first; block->elevation = selectedCollisions->at(index).second; } - map->setBlock(actualX, actualY, *block, true); + map->setBlock(actualX, actualY, *block, !fromScriptCallback); } } } diff --git a/test_script.js b/test_script.js index 52f654fd..df98dc42 100644 --- a/test_script.js +++ b/test_script.js @@ -8,8 +8,13 @@ const grassTiles = [0x8, 0x9, 0x10, 0x11]; // Porymap callback when a block is painted. export function on_block_changed(x, y, prevBlock, newBlock) { - if (grassTiles.indexOf(newBlock.tile) != -1) { - const i = randInt(0, grassTiles.length); - api.scriptapi_setBlock(x, y, grassTiles[i], newBlock.collision, newBlock.elevation); + 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) + } catch(err) { + console.log(err); } } From 95012838fd735d8b345cc40d1817eb49b573772e Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Thu, 30 Apr 2020 18:30:24 -0500 Subject: [PATCH 03/17] Implement more map api functions --- include/mainwindow.h | 18 +++++ include/scripting.h | 1 + include/ui/mappixmapitem.h | 23 +++++- src/mainwindow.cpp | 150 +++++++++++++++++++++++++++++++++++- src/scripting.cpp | 7 ++ src/ui/mappixmapitem.cpp | 152 +++++++++++++++++++++++-------------- test_script.js | 10 +-- 7 files changed, 295 insertions(+), 66 deletions(-) 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); } From 567a45b7e40958dbbbe58d7b11aa952460256cba Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sat, 2 May 2020 16:25:35 -0500 Subject: [PATCH 04/17] Implement some overlay functions and on_map_opened callback --- include/mainwindow.h | 6 +++ include/scripting.h | 2 + include/ui/graphicsview.h | 3 ++ include/ui/overlay.h | 87 +++++++++++++++++++++++++++++++++++++++ porymap.pro | 4 +- src/mainwindow.cpp | 32 ++++++++++++++ src/scripting.cpp | 12 +++++- src/ui/graphicsview.cpp | 6 +++ src/ui/overlay.cpp | 46 +++++++++++++++++++++ test_script.js | 31 +++++++------- 10 files changed, 210 insertions(+), 19 deletions(-) create mode 100644 include/ui/overlay.h create mode 100644 src/ui/overlay.cpp diff --git a/include/mainwindow.h b/include/mainwindow.h index 8c343220..c69351f4 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -58,6 +58,12 @@ public: Q_INVOKABLE void setDimensions(int width, int height); Q_INVOKABLE void setWidth(int width); Q_INVOKABLE void setHeight(int height); + Q_INVOKABLE void clearOverlay(); + Q_INVOKABLE void addText(QString text, int x, int y, QString color = "#000000", int fontSize = 12); + Q_INVOKABLE void addRect(int x, int y, int width, int height, QString color = "#000000"); + Q_INVOKABLE void addFilledRect(int x, int y, int width, int height, QString color = "#000000"); + Q_INVOKABLE void addImage(int x, int y, QString filepath); + public slots: void scaleMapView(int); diff --git a/include/scripting.h b/include/scripting.h index 84468e11..7b8b0f91 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -9,6 +9,7 @@ enum CallbackType { OnBlockChanged, + OnMapOpened, }; class Scripting @@ -19,6 +20,7 @@ public: static QJSValue dimensions(int width, int height); static void init(MainWindow *mainWindow); static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock); + static void cb_MapOpened(QString mapName); private: QJSEngine *engine; diff --git a/include/ui/graphicsview.h b/include/ui/graphicsview.h index 0b3a12c7..85fb7a73 100644 --- a/include/ui/graphicsview.h +++ b/include/ui/graphicsview.h @@ -1,6 +1,7 @@ #ifndef GRAPHICSVIEW_H #define GRAPHICSVIEW_H +#include "overlay.h" #include #include @@ -15,10 +16,12 @@ public: public: // GraphicsView_Object object; Editor *editor; + Overlay overlay; protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); + void drawForeground(QPainter *painter, const QRectF &rect); }; //Q_DECLARE_METATYPE(GraphicsView) diff --git a/include/ui/overlay.h b/include/ui/overlay.h new file mode 100644 index 00000000..a8e13337 --- /dev/null +++ b/include/ui/overlay.h @@ -0,0 +1,87 @@ +#ifndef OVERLAY_H +#define OVERLAY_H + +#include +#include +#include +#include + +class OverlayItem { +public: + OverlayItem() {} + virtual ~OverlayItem() {}; + virtual void render(QPainter *painter) {}; +}; + +class OverlayText : public OverlayItem { +public: + OverlayText(QString text, int x, int y, QColor color, int fontSize) { + this->text = text; + this->x = x; + this->y = y; + this->color = color; + this->fontSize = fontSize; + } + ~OverlayText() {} + virtual void render(QPainter *painter); +private: + QString text; + int x; + int y; + QColor color; + int fontSize; +}; + +class OverlayRect : public OverlayItem { +public: + OverlayRect(int x, int y, int width, int height, QColor color, bool filled) { + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->color = color; + this->filled = filled; + } + ~OverlayRect() {} + virtual void render(QPainter *painter); +private: + int x; + int y; + int width; + int height; + QColor color; + bool filled; +}; + +class OverlayImage : public OverlayItem { +public: + OverlayImage(int x, int y, QImage image) { + this->x = x; + this->y = y; + this->image = image; + } + ~OverlayImage() {} + virtual void render(QPainter *painter); +private: + int x; + int y; + QImage image; +}; + +class Overlay +{ +public: + Overlay() {} + ~Overlay() { + this->clearItems(); + } + QList getItems(); + void clearItems(); + void addText(QString text, int x, int y, QString color = "#000000", int fontSize = 12); + void addRect(int x, int y, int width, int height, QString color = "#000000", bool filled = false); + void addImage(int x, int y, QString filepath); +private: + QList items; +}; + +#endif // OVERLAY_H diff --git a/porymap.pro b/porymap.pro index 1a58b420..2a138e30 100644 --- a/porymap.pro +++ b/porymap.pro @@ -36,6 +36,7 @@ SOURCES += src/core/block.cpp \ src/ui/collisionpixmapitem.cpp \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ + src/ui/overlay.cpp \ src/ui/regionmaplayoutpixmapitem.cpp \ src/ui/regionmapentriespixmapitem.cpp \ src/ui/cursortilerect.cpp \ @@ -136,7 +137,8 @@ HEADERS += include/core/block.h \ include/scripting.h \ include/settings.h \ include/log.h \ - include/ui/newtilesetdialog.h + include/ui/newtilesetdialog.h \ + include/ui/overlay.h FORMS += forms/mainwindow.ui \ forms/eventpropertiesframe.ui \ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 05ebd6ea..39b44058 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -446,6 +446,8 @@ bool MainWindow::setMap(QString map_name, bool scrollTreeView) { setRecentMap(map_name); updateMapList(); updateTilesetEditor(); + + Scripting::cb_MapOpened(map_name); return true; } @@ -2714,3 +2716,33 @@ void MainWindow::setHeight(int height) { this->editor->map->commit(); this->onMapNeedsRedrawing(); } + +void MainWindow::clearOverlay() { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.clearItems(); +} + +void MainWindow::addText(QString text, int x, int y, QString color, int fontSize) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addText(text, x, y, color, fontSize); +} + +void MainWindow::addRect(int x, int y, int width, int height, QString color) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, false); +} + +void MainWindow::addFilledRect(int x, int y, int width, int height, QString color) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, true); +} + +void MainWindow::addImage(int x, int y, QString filepath) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addImage(x, y, filepath); +} diff --git a/src/scripting.cpp b/src/scripting.cpp index f946bef5..8b64f296 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -3,6 +3,7 @@ QMap callbackFunctions = { {OnBlockChanged, "on_block_changed"}, + {OnMapOpened, "on_map_opened"}, }; Scripting *instance = nullptr; @@ -65,9 +66,18 @@ void Scripting::cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock instance->invokeCallback(OnBlockChanged, args); } +void Scripting::cb_MapOpened(QString mapName) { + if (!instance) return; + + QJSValueList args { + mapName, + }; + instance->invokeCallback(OnMapOpened, args); +} + QJSValue Scripting::fromBlock(Block block) { QJSValue obj = instance->engine->newObject(); - obj.setProperty("tile", block.tile); + obj.setProperty("metatileId", block.tile); obj.setProperty("collision", block.collision); obj.setProperty("elevation", block.elevation); obj.setProperty("rawValue", block.rawValue()); diff --git a/src/ui/graphicsview.cpp b/src/ui/graphicsview.cpp index 778436df..19cf1a76 100644 --- a/src/ui/graphicsview.cpp +++ b/src/ui/graphicsview.cpp @@ -15,3 +15,9 @@ void GraphicsView::mouseMoveEvent(QMouseEvent *event) { void GraphicsView::mouseReleaseEvent(QMouseEvent *event) { QGraphicsView::mouseReleaseEvent(event); } + +void GraphicsView::drawForeground(QPainter *painter, const QRectF &rect) { + for (auto item : this->overlay.getItems()) { + item->render(painter); + } +} diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp new file mode 100644 index 00000000..f1be1a94 --- /dev/null +++ b/src/ui/overlay.cpp @@ -0,0 +1,46 @@ +#include "overlay.h" + +void OverlayText::render(QPainter *painter) { + QFont font = painter->font(); + font.setPixelSize(this->fontSize); + painter->setFont(font); + painter->setPen(this->color); + painter->drawText(this->x, this->y, this->text); +} + +void OverlayRect::render(QPainter *painter) { + if (this->filled) { + painter->fillRect(this->x, this->y, this->width, this->height, this->color); + } else { + painter->setPen(this->color); + painter->drawRect(this->x, this->y, this->width, this->height); + } +} + +void OverlayImage::render(QPainter *painter) { + painter->drawImage(this->x, this->y, this->image); +} + +void Overlay::clearItems() { + for (auto item : this->items) { + delete item; + } + this->items.clear(); +} + +QList Overlay::getItems() { + return this->items; +} + +void Overlay::addText(QString text, int x, int y, QString color, int fontSize) { + this->items.append(new OverlayText(text, x, y, QColor(color), fontSize)); +} + +void Overlay::addRect(int x, int y, int width, int height, QString color, bool filled) { + this->items.append(new OverlayRect(x, y, width, height, QColor(color), filled)); +} + +void Overlay::addImage(int x, int y, QString filepath) { + this->items.append(new OverlayImage(x, y, QImage(filepath))); +} + diff --git a/test_script.js b/test_script.js index 4cf9362e..ed74953e 100644 --- a/test_script.js +++ b/test_script.js @@ -1,20 +1,17 @@ -function randInt(min, max) { - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(Math.random() * (max - min)) + min; -} - -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); - // } - console.log("ran", map.getWidth(), map.getHeight()); - } catch(err) { - console.log(err); - } + map.clearOverlay() + map.addFilledRect(0, 0, map.getWidth() * 16 - 1, map.getHeight() * 16 - 1, "#80FF0040") + map.addRect(10, 10, 100, 30, "#FF00FF") + map.addImage(80, 80, "D:\\cygwin64\\home\\huder\\scratch\\github-avatar.png") + map.addText(`coords ${x}, ${y}`, 20, 20, "#00FF00", 24) + map.addText(`block ${prevBlock.metatileId}`, 20, 60, "#00FFFF", 18) + console.log("ran", x, y) +} + +// Porymap callback when a map is opened. +export function on_map_opened(mapName) { + map.clearOverlay() + map.addFilledRect(0, 0, map.getWidth() * 16 - 1, map.getHeight() * 16 - 1, "#4000FF00") + console.log(`opened ${mapName}`) } From 8697adf18623a658356b9262b04875fbe1330a27 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sun, 3 May 2020 10:00:56 -0500 Subject: [PATCH 05/17] Add true tileset scripting functions --- include/core/map.h | 2 +- include/editor.h | 3 +++ include/mainwindow.h | 6 +++++ include/project.h | 2 +- include/scripting.h | 1 + src/core/map.cpp | 4 +-- src/editor.cpp | 23 ++++++++++++++++ src/mainwindow.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++- src/project.cpp | 6 ++--- src/scripting.cpp | 4 +++ test_script.js | 33 ++++++++++++++--------- 11 files changed, 127 insertions(+), 20 deletions(-) diff --git a/include/core/map.h b/include/core/map.h index 1afd69c3..a679a55b 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -85,7 +85,7 @@ public: void removeEvent(Event*); void addEvent(Event*); QPixmap renderConnection(MapConnection, MapLayout *); - QPixmap renderBorder(); + QPixmap renderBorder(bool ignoreCache = false); void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true); void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true); void cacheBorder(); diff --git a/include/editor.h b/include/editor.h index 1f9d9232..baf32b5e 100644 --- a/include/editor.h +++ b/include/editor.h @@ -66,6 +66,9 @@ public: void displayMapGrid(); void displayWildMonTables(); + void updateMapBorder(); + void updateMapConnections(); + void setEditingMap(); void setEditingCollision(); void setEditingObjects(); diff --git a/include/mainwindow.h b/include/mainwindow.h index c69351f4..fd320576 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -63,6 +63,12 @@ public: Q_INVOKABLE void addRect(int x, int y, int width, int height, QString color = "#000000"); Q_INVOKABLE void addFilledRect(int x, int y, int width, int height, QString color = "#000000"); Q_INVOKABLE void addImage(int x, int y, QString filepath); + void setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors); + Q_INVOKABLE void setPrimaryTilesetPalette(int paletteIndex, QList> colors); + Q_INVOKABLE void setSecondaryTilesetPalette(int paletteIndex, QList> colors); + QJSValue getTilesetPalette(Tileset *tileset, int paletteIndex); + Q_INVOKABLE QJSValue getPrimaryTilesetPalette(int paletteIndex); + Q_INVOKABLE QJSValue getSecondaryTilesetPalette(int paletteIndex); public slots: diff --git a/include/project.h b/include/project.h index e9147b2c..a1f13f98 100644 --- a/include/project.h +++ b/include/project.h @@ -142,7 +142,7 @@ public: void saveTilesetMetatileAttributes(Tileset*); void saveTilesetMetatiles(Tileset*); void saveTilesetTilesImage(Tileset*); - void saveTilesetPalettes(Tileset*, bool); + void saveTilesetPalettes(Tileset*); QString defaultSong; QStringList getSongNames(); diff --git a/include/scripting.h b/include/scripting.h index 7b8b0f91..614965ea 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -18,6 +18,7 @@ public: Scripting(MainWindow *mainWindow); static QJSValue fromBlock(Block block); static QJSValue dimensions(int width, int height); + static QJSEngine *getEngine(); static void init(MainWindow *mainWindow); static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock); static void cb_MapOpened(QString mapName); diff --git a/src/core/map.cpp b/src/core/map.cpp index 7101da04..c2229293 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -221,7 +221,7 @@ QPixmap Map::render(bool ignoreCache = false, MapLayout * fromLayout) { return pixmap; } -QPixmap Map::renderBorder() { +QPixmap Map::renderBorder(bool ignoreCache) { bool changed_any = false, border_resized = false; int width_ = getBorderWidth(); int height_ = getBorderHeight(); @@ -239,7 +239,7 @@ QPixmap Map::renderBorder() { } QPainter painter(&layout->border_image); for (int i = 0; i < layout->border->blocks->length(); i++) { - if (!border_resized && !borderBlockChanged(i, layout->cached_border)) { + if (!ignoreCache && (!border_resized && !borderBlockChanged(i, layout->cached_border))) { continue; } diff --git a/src/editor.cpp b/src/editor.cpp index 0e8a0c30..92c254ad 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1440,6 +1440,29 @@ void Editor::displayMapBorder() { } } +void Editor::updateMapBorder() { + QPixmap pixmap = this->map->renderBorder(true); + for (auto item : this->borderItems) { + item->setPixmap(pixmap); + } +} + +void Editor::updateMapConnections() { + if (connection_items.size() != connection_edit_items.size()) + return; + + for (int i = 0; i < connection_items.size(); i++) { + Map *connected_map = project->getMap(connection_edit_items[i]->connection->map_name); + if (!connected_map) + continue; + + QPixmap pixmap = connected_map->renderConnection(*(connection_edit_items[i]->connection), map->layout); + connection_items[i]->setPixmap(pixmap); + connection_edit_items[i]->basePixmap = pixmap; + connection_edit_items[i]->setPixmap(pixmap); + } +} + int Editor::getBorderDrawDistance(int dimension) { // Draw sufficient border blocks to fill the player's view (BORDER_DISTANCE) if (dimension >= BORDER_DISTANCE) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 39b44058..29b73ed7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1073,7 +1073,7 @@ void MainWindow::on_actionNew_Tileset_triggered() { editor->project->saveTilesetTilesImage(newSet); editor->project->saveTilesetMetatiles(newSet); editor->project->saveTilesetMetatileAttributes(newSet); - editor->project->saveTilesetPalettes(newSet, !createTilesetDialog->isSecondary); + editor->project->saveTilesetPalettes(newSet); //append to tileset specific files @@ -2746,3 +2746,64 @@ void MainWindow::addImage(int x, int y, QString filepath) { return; this->ui->graphicsView_Map->overlay.addImage(x, y, filepath); } + +void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout) + return; + if (paletteIndex >= tileset->palettes->size()) + return; + if (colors.size() != 16) + return; + + for (int i = 0; i < 16; i++) { + if (colors[i].size() != 3) + continue; + auto palettes = tileset->palettes; + (*palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); + } + + if (this->tilesetEditor) { + this->tilesetEditor->setTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); + } + this->editor->metatile_selector_item->draw(); + this->editor->selected_border_metatiles_item->draw(); + this->editor->map_item->draw(true); + this->editor->updateMapBorder(); + this->editor->updateMapConnections(); + this->editor->project->saveTilesetPalettes(tileset); +} + +void MainWindow::setPrimaryTilesetPalette(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + this->setTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex, colors); +} + +void MainWindow::setSecondaryTilesetPalette(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + this->setTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex, colors); +} + +QJSValue MainWindow::getTilesetPalette(Tileset *tileset, int paletteIndex) { + if (paletteIndex >= tileset->palettes->size()) + return QJSValue(); + + QList> palette; + for (auto color : tileset->palettes->value(paletteIndex)) { + palette.append(QList({qRed(color), qGreen(color), qBlue(color)})); + } + return Scripting::getEngine()->toScriptValue(palette); +} + +QJSValue MainWindow::getPrimaryTilesetPalette(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex); +} + +QJSValue MainWindow::getSecondaryTilesetPalette(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex); +} diff --git a/src/project.cpp b/src/project.cpp index f791e50b..7e2a8731 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1012,8 +1012,8 @@ void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { saveTilesetMetatiles(secondaryTileset); saveTilesetTilesImage(primaryTileset); saveTilesetTilesImage(secondaryTileset); - saveTilesetPalettes(primaryTileset, true); - saveTilesetPalettes(secondaryTileset, false); + saveTilesetPalettes(primaryTileset); + saveTilesetPalettes(secondaryTileset); } void Project::saveTilesetMetatileLabels(Tileset *primaryTileset, Tileset *secondaryTileset) { @@ -1150,7 +1150,7 @@ void Project::saveTilesetTilesImage(Tileset *tileset) { exportIndexed4BPPPng(tileset->tilesImage, tileset->tilesImagePath); } -void Project::saveTilesetPalettes(Tileset *tileset, bool /*primary*/) { +void Project::saveTilesetPalettes(Tileset *tileset) { PaletteUtil paletteParser; for (int i = 0; i < Project::getNumPalettesTotal(); i++) { QString filepath = tileset->palettePaths.at(i); diff --git a/src/scripting.cpp b/src/scripting.cpp index 8b64f296..4fdab50d 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -90,3 +90,7 @@ QJSValue Scripting::dimensions(int width, int height) { obj.setProperty("height", height); return obj; } + +QJSEngine *Scripting::getEngine() { + return instance->engine; +} diff --git a/test_script.js b/test_script.js index ed74953e..3eae34b3 100644 --- a/test_script.js +++ b/test_script.js @@ -1,17 +1,26 @@ -// Porymap callback when a block is painted. -export function on_block_changed(x, y, prevBlock, newBlock) { - map.clearOverlay() - map.addFilledRect(0, 0, map.getWidth() * 16 - 1, map.getHeight() * 16 - 1, "#80FF0040") - map.addRect(10, 10, 100, 30, "#FF00FF") - map.addImage(80, 80, "D:\\cygwin64\\home\\huder\\scratch\\github-avatar.png") - map.addText(`coords ${x}, ${y}`, 20, 20, "#00FF00", 24) - map.addText(`block ${prevBlock.metatileId}`, 20, 60, "#00FFFF", 18) - console.log("ran", x, y) +const nightTint = [0.6, 0.55, 1.0]; + +function applyTint(palette, tint) { + for (let i = 0; i < palette.length; i++) { + const color = palette[i]; + for (let j = 0; j < tint.length; j++) { + color[j] = Math.floor(color[j] * tint[j]); + } + } } // Porymap callback when a map is opened. export function on_map_opened(mapName) { - map.clearOverlay() - map.addFilledRect(0, 0, map.getWidth() * 16 - 1, map.getHeight() * 16 - 1, "#4000FF00") - console.log(`opened ${mapName}`) + try { + for (let i = 0; i < 13; i++) { + const primaryPalette = map.getPrimaryTilesetPalette(i) + applyTint(primaryPalette, nightTint) + map.setPrimaryTilesetPalette(i, primaryPalette) + const secondaryPalette = map.getSecondaryTilesetPalette(i) + applyTint(secondaryPalette, nightTint) + map.setSecondaryTilesetPalette(i, secondaryPalette) + } + } catch (err) { + console.log(err) + } } From 0ef3c6a89823a43a6977370d3fd3c46423cd1728 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sun, 3 May 2020 10:31:44 -0500 Subject: [PATCH 06/17] Add palette preview scripting commands --- include/core/tileset.h | 5 +-- include/mainwindow.h | 3 ++ include/ui/imageproviders.h | 4 +-- src/core/tileset.cpp | 23 +++++++++---- src/mainwindow.cpp | 41 ++++++++++++++++++++++-- src/project.cpp | 3 ++ src/ui/imageproviders.cpp | 8 ++--- src/ui/metatilelayersitem.cpp | 2 +- src/ui/paletteeditor.cpp | 5 +++ src/ui/tileseteditor.cpp | 2 +- src/ui/tileseteditormetatileselector.cpp | 2 +- src/ui/tileseteditortileselector.cpp | 10 +++--- test_script.js | 9 +++--- 13 files changed, 89 insertions(+), 28 deletions(-) diff --git a/include/core/tileset.h b/include/core/tileset.h index cf0bb67c..9113788a 100644 --- a/include/core/tileset.h +++ b/include/core/tileset.h @@ -28,13 +28,14 @@ public: QList *tiles = nullptr; QList *metatiles = nullptr; QList> *palettes = nullptr; + QList> *palettePreviews = nullptr; Tileset* copy(); static Tileset* getBlockTileset(int, Tileset*, Tileset*); static Metatile* getMetatile(int, Tileset*, Tileset*); - static QList> getBlockPalettes(Tileset*, Tileset*); - static QList getPalette(int, Tileset*, Tileset*); + static QList> getBlockPalettes(Tileset*, Tileset*, bool useTruePalettes = false); + static QList getPalette(int, Tileset*, Tileset*, bool useTruePalettes = false); static bool metatileIsValid(uint16_t metatileId, Tileset *, Tileset *); bool appendToHeaders(QString headerFile, QString friendlyName); diff --git a/include/mainwindow.h b/include/mainwindow.h index fd320576..518c3d9d 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -69,6 +69,9 @@ public: QJSValue getTilesetPalette(Tileset *tileset, int paletteIndex); Q_INVOKABLE QJSValue getPrimaryTilesetPalette(int paletteIndex); Q_INVOKABLE QJSValue getSecondaryTilesetPalette(int paletteIndex); + void setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors); + Q_INVOKABLE void setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors); + Q_INVOKABLE void setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors); public slots: diff --git a/include/ui/imageproviders.h b/include/ui/imageproviders.h index de54e1aa..89dfdce9 100644 --- a/include/ui/imageproviders.h +++ b/include/ui/imageproviders.h @@ -8,9 +8,9 @@ QImage getCollisionMetatileImage(Block); QImage getCollisionMetatileImage(int, int); -QImage getMetatileImage(uint16_t, Tileset*, Tileset*); +QImage getMetatileImage(uint16_t, Tileset*, Tileset*, bool useTruePalettes = false); QImage getTileImage(uint16_t, Tileset*, Tileset*); -QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int); +QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false); QImage getGreyscaleTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset); static QList greyscalePalette({ diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index 450458b8..4c9a9fbb 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -46,6 +46,14 @@ Tileset* Tileset::copy() { } copy->palettes->append(copyPalette); } + copy->palettePreviews = new QList>; + for (QList palette : *this->palettePreviews) { + QList copyPalette; + for (QRgb color : palette) { + copyPalette.append(color); + } + copy->palettePreviews->append(copyPalette); + } return copy; } @@ -80,24 +88,27 @@ bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tile return true; } -QList> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset) { +QList> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) { QList> palettes; + auto primaryPalettes = useTruePalettes ? primaryTileset->palettes : primaryTileset->palettePreviews; for (int i = 0; i < Project::getNumPalettesPrimary(); i++) { - palettes.append(primaryTileset->palettes->at(i)); + palettes.append(primaryPalettes->at(i)); } + auto secondaryPalettes = useTruePalettes ? secondaryTileset->palettes : secondaryTileset->palettePreviews; for (int i = Project::getNumPalettesPrimary(); i < Project::getNumPalettesTotal(); i++) { - palettes.append(secondaryTileset->palettes->at(i)); + palettes.append(secondaryPalettes->at(i)); } return palettes; } -QList Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset) { +QList Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) { QList paletteTable; Tileset *tileset = paletteId < Project::getNumPalettesPrimary() ? primaryTileset : secondaryTileset; - for (int i = 0; i < tileset->palettes->at(paletteId).length(); i++) { - paletteTable.append(tileset->palettes->at(paletteId).at(i)); + auto palettes = useTruePalettes ? tileset->palettes : tileset->palettePreviews; + for (int i = 0; i < palettes->at(paletteId).length(); i++) { + paletteTable.append(palettes->at(paletteId).at(i)); } return paletteTable; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 29b73ed7..ed9653d0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1056,6 +1056,7 @@ void MainWindow::on_actionNew_Tileset_triggered() { newSet->metatiles->append(mt); } newSet->palettes = new QList>(); + newSet->palettePreviews = new QList>(); newSet->palettePaths = *new QList(); for(int i = 0; i < 16; ++i) { QList *currentPal = new QList(); @@ -1063,11 +1064,13 @@ void MainWindow::on_actionNew_Tileset_triggered() { currentPal->append(qRgb(0,0,0)); } newSet->palettes->append(*currentPal); + newSet->palettePreviews->append(*currentPal); QString fileName; fileName.sprintf("%02d.pal", i); newSet->palettePaths.append(fullDirectoryPath+"/palettes/" + fileName); } (*newSet->palettes)[0][1] = qRgb(255,0,255); + (*newSet->palettePreviews)[0][1] = qRgb(255,0,255); newSet->is_compressed = "TRUE"; newSet->padding = "0"; editor->project->saveTilesetTilesImage(newSet); @@ -2758,8 +2761,8 @@ void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QListpalettes; - (*palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); + (*tileset->palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); + (*tileset->palettePreviews)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); } if (this->tilesetEditor) { @@ -2807,3 +2810,37 @@ QJSValue MainWindow::getSecondaryTilesetPalette(int paletteIndex) { return QJSValue(); return this->getTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex); } + +void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout) + return; + if (paletteIndex >= tileset->palettePreviews->size()) + return; + if (colors.size() != 16) + return; + + for (int i = 0; i < 16; i++) { + if (colors[i].size() != 3) + continue; + auto palettes = tileset->palettePreviews; + (*palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); + } + + this->editor->metatile_selector_item->draw(); + this->editor->selected_border_metatiles_item->draw(); + this->editor->map_item->draw(true); + this->editor->updateMapBorder(); + this->editor->updateMapConnections(); +} + +void MainWindow::setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, paletteIndex, colors); +} + +void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, paletteIndex, colors); +} diff --git a/src/project.cpp b/src/project.cpp index 7e2a8731..ce8420bc 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1585,6 +1585,7 @@ void Project::loadTilesetAssets(Tileset* tileset) { // palettes QList> *palettes = new QList>; + QList> *palettePreviews = new QList>; for (int i = 0; i < tileset->palettePaths.length(); i++) { QList palette; QString path = tileset->palettePaths.value(i); @@ -1619,8 +1620,10 @@ void Project::loadTilesetAssets(Tileset* tileset) { } palettes->append(palette); + palettePreviews->append(palette); } tileset->palettes = palettes; + tileset->palettePreviews = palettePreviews; } void Project::loadTilesetTiles(Tileset *tileset, QImage image) { diff --git a/src/ui/imageproviders.cpp b/src/ui/imageproviders.cpp index 286badb3..f060450e 100644 --- a/src/ui/imageproviders.cpp +++ b/src/ui/imageproviders.cpp @@ -13,7 +13,7 @@ QImage getCollisionMetatileImage(int collision, int elevation) { return collisionImage.toImage(); } -QImage getMetatileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset) { +QImage getMetatileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) { QImage metatile_image(16, 16, QImage::Format_RGBA8888); Metatile* metatile = Tileset::getMetatile(tile, primaryTileset, secondaryTileset); @@ -27,7 +27,7 @@ QImage getMetatileImage(uint16_t tile, Tileset *primaryTileset, Tileset *seconda metatile_image.fill(Qt::magenta); return metatile_image; } - QList> palettes = Tileset::getBlockPalettes(primaryTileset, secondaryTileset); + QList> palettes = Tileset::getBlockPalettes(primaryTileset, secondaryTileset, useTruePalettes); QPainter metatile_painter(&metatile_image); for (int layer = 0; layer < 2; layer++) @@ -95,8 +95,8 @@ QImage getColoredTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *seco return tileImage; } -QImage getPalettedTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId) { - QList palette = Tileset::getPalette(paletteId, primaryTileset, secondaryTileset); +QImage getPalettedTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId, bool useTruePalettes) { + QList palette = Tileset::getPalette(paletteId, primaryTileset, secondaryTileset, useTruePalettes); return getColoredTileImage(tile, primaryTileset, secondaryTileset, palette); } diff --git a/src/ui/metatilelayersitem.cpp b/src/ui/metatilelayersitem.cpp index 774e1388..cf4c3b9f 100644 --- a/src/ui/metatilelayersitem.cpp +++ b/src/ui/metatilelayersitem.cpp @@ -18,7 +18,7 @@ void MetatileLayersItem::draw() { QPainter painter(&pixmap); for (int i = 0; i < 8; i++) { Tile tile = this->metatile->tiles->at(i); - QImage tileImage = getPalettedTileImage(tile.tile, this->primaryTileset, this->secondaryTileset, tile.palette) + QImage tileImage = getPalettedTileImage(tile.tile, this->primaryTileset, this->secondaryTileset, tile.palette, true) .mirrored(tile.xflip, tile.yflip) .scaled(16, 16); painter.drawImage(tileCoords.at(i), tileImage); diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp index 87169784..fd9eeb9a 100644 --- a/src/ui/paletteeditor.cpp +++ b/src/ui/paletteeditor.cpp @@ -203,6 +203,7 @@ void PaletteEditor::setColor(int colorIndex) { ? this->primaryTileset : this->secondaryTileset; (*tileset->palettes)[paletteNum][colorIndex] = qRgb(red, green, blue); + (*tileset->palettePreviews)[paletteNum][colorIndex] = qRgb(red, green, blue); this->refreshColor(colorIndex); this->commitEditHistory(paletteNum); emit this->changedPaletteColor(); @@ -246,8 +247,10 @@ void PaletteEditor::setColorsFromHistory(PaletteHistoryItem *history, int palett for (int i = 0; i < 16; i++) { if (paletteId < Project::getNumPalettesPrimary()) { (*this->primaryTileset->palettes)[paletteId][i] = history->colors.at(i); + (*this->primaryTileset->palettePreviews)[paletteId][i] = history->colors.at(i); } else { (*this->secondaryTileset->palettes)[paletteId][i] = history->colors.at(i); + (*this->secondaryTileset->palettePreviews)[paletteId][i] = history->colors.at(i); } } @@ -296,8 +299,10 @@ void PaletteEditor::on_actionImport_Palette_triggered() for (int i = 0; i < 16; i++) { if (paletteId < Project::getNumPalettesPrimary()) { (*this->primaryTileset->palettes)[paletteId][i] = palette.at(i); + (*this->primaryTileset->palettePreviews)[paletteId][i] = palette.at(i); } else { (*this->secondaryTileset->palettes)[paletteId][i] = palette.at(i); + (*this->secondaryTileset->palettePreviews)[paletteId][i] = palette.at(i); } } diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 069a9e23..3b09a069 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -177,7 +177,7 @@ void TilesetEditor::drawSelectedTiles() { int tileIndex = 0; for (int j = 0; j < dimensions.y(); j++) { for (int i = 0; i < dimensions.x(); i++) { - QImage tileImage = getPalettedTileImage(tiles.at(tileIndex).tile, this->primaryTileset, this->secondaryTileset, tiles.at(tileIndex).palette) + QImage tileImage = getPalettedTileImage(tiles.at(tileIndex).tile, this->primaryTileset, this->secondaryTileset, tiles.at(tileIndex).palette, true) .mirrored(tiles.at(tileIndex).xflip, tiles.at(tileIndex).yflip) .scaled(16, 16); tileIndex++; diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp index eae7d4cc..b5732c99 100644 --- a/src/ui/tileseteditormetatileselector.cpp +++ b/src/ui/tileseteditormetatileselector.cpp @@ -22,7 +22,7 @@ void TilesetEditorMetatileSelector::draw() { if (i >= primaryLength) { tile += Project::getNumMetatilesPrimary() - primaryLength; } - QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset).scaled(32, 32); + QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset, true).scaled(32, 32); int map_y = i / this->numMetatilesWide; int map_x = i % this->numMetatilesWide; QPoint metatile_origin = QPoint(map_x * 32, map_y * 32); diff --git a/src/ui/tileseteditortileselector.cpp b/src/ui/tileseteditortileselector.cpp index d41cc754..40d58952 100644 --- a/src/ui/tileseteditortileselector.cpp +++ b/src/ui/tileseteditortileselector.cpp @@ -22,19 +22,19 @@ void TilesetEditorTileSelector::draw() { int primaryLength = this->primaryTileset->tiles->length(); int secondaryLength = this->secondaryTileset->tiles->length(); int height = totalTiles / this->numTilesWide; - QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset); + QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset, true); QImage image(this->numTilesWide * 16, height * 16, QImage::Format_RGBA8888); QPainter painter(&image); for (uint16_t tile = 0; tile < totalTiles; tile++) { QImage tileImage; if (tile < primaryLength) { - tileImage = getPalettedTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16); + tileImage = getPalettedTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId, true).scaled(16, 16); } else if (tile < Project::getNumTilesPrimary()) { tileImage = QImage(16, 16, QImage::Format_RGBA8888); tileImage.fill(palette.at(0)); } else if (tile < Project::getNumTilesPrimary() + secondaryLength) { - tileImage = getPalettedTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16); + tileImage = getPalettedTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId, true).scaled(16, 16); } else { tileImage = QImage(16, 16, QImage::Format_RGBA8888); QPainter painter(&tileImage); @@ -224,7 +224,7 @@ QImage TilesetEditorTileSelector::buildPrimaryTilesIndexedImage() { // Image is first converted using greyscale so that palettes with duplicate colors // are properly represented in the final image. QImage indexedImage = image.convertToFormat(QImage::Format::Format_Indexed8, greyscalePalette.toVector()); - QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset); + QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset, true); indexedImage.setColorTable(palette.toVector()); return indexedImage; } @@ -261,7 +261,7 @@ QImage TilesetEditorTileSelector::buildSecondaryTilesIndexedImage() { // Image is first converted using greyscale so that palettes with duplicate colors // are properly represented in the final image. QImage indexedImage = image.convertToFormat(QImage::Format::Format_Indexed8, greyscalePalette.toVector()); - QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset); + QList palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset, true); indexedImage.setColorTable(palette.toVector()); return indexedImage; } diff --git a/test_script.js b/test_script.js index 3eae34b3..28b5deb1 100644 --- a/test_script.js +++ b/test_script.js @@ -1,3 +1,4 @@ +const morningTint = [0.8, 0.7, 0.9]; const nightTint = [0.6, 0.55, 1.0]; function applyTint(palette, tint) { @@ -14,11 +15,11 @@ export function on_map_opened(mapName) { try { for (let i = 0; i < 13; i++) { const primaryPalette = map.getPrimaryTilesetPalette(i) - applyTint(primaryPalette, nightTint) - map.setPrimaryTilesetPalette(i, primaryPalette) + applyTint(primaryPalette, morningTint) + map.setPrimaryTilesetPalettePreview(i, primaryPalette) const secondaryPalette = map.getSecondaryTilesetPalette(i) - applyTint(secondaryPalette, nightTint) - map.setSecondaryTilesetPalette(i, secondaryPalette) + applyTint(secondaryPalette, morningTint) + map.setSecondaryTilesetPalettePreview(i, secondaryPalette) } } catch (err) { console.log(err) From 434bafe0b256e22c00ee362ff170af26e212d4aa Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sun, 3 May 2020 10:48:48 -0500 Subject: [PATCH 07/17] Call on_block_changed from more places --- include/ui/mappixmapitem.h | 4 ++-- src/core/map.cpp | 4 ++-- src/ui/collisionpixmapitem.cpp | 2 +- src/ui/mappixmapitem.cpp | 12 ++++++------ test_script.js | 24 ++---------------------- 5 files changed, 13 insertions(+), 33 deletions(-) diff --git a/include/ui/mappixmapitem.h b/include/ui/mappixmapitem.h index a361ce37..4ee10e56 100644 --- a/include/ui/mappixmapitem.h +++ b/include/ui/mappixmapitem.h @@ -55,7 +55,7 @@ public: QList *selectedMetatiles, QList> *selectedCollisions, bool fromScriptCall = false); - void floodFillSmartPath(int initialX, int initialY); + void floodFillSmartPath(int initialX, int initialY, bool fromScriptCall = false); virtual void pick(QGraphicsSceneMouseEvent*); virtual void select(QGraphicsSceneMouseEvent*); virtual void shift(QGraphicsSceneMouseEvent*); @@ -65,7 +65,7 @@ public: void paintNormal(int x, int y, bool fromScriptCall = false); private: - void paintSmartPath(int x, int y); + void paintSmartPath(int x, int y, bool fromScriptCall = false); static QList smartPathTable; signals: diff --git a/src/core/map.cpp b/src/core/map.cpp index c2229293..2e777190 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -395,7 +395,7 @@ void Map::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_ block->collision = collision; block->elevation = elevation; - setBlock(x, y, *block); + setBlock(x, y, *block, true); if ((block = getBlock(x + 1, y)) && block->collision == old_coll && block->elevation == old_elev) { todo.append(QPoint(x + 1, y)); } @@ -520,7 +520,7 @@ void Map::magicFillCollisionElevation(int initialX, int initialY, uint16_t colli if (block && block->collision == old_coll && block->elevation == old_elev) { block->collision = collision; block->elevation = elevation; - setBlock(x, y, *block); + setBlock(x, y, *block, true); } } } diff --git a/src/ui/collisionpixmapitem.cpp b/src/ui/collisionpixmapitem.cpp index 252b9f1a..4b4bd376 100644 --- a/src/ui/collisionpixmapitem.cpp +++ b/src/ui/collisionpixmapitem.cpp @@ -39,7 +39,7 @@ void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { if (block) { block->collision = this->movementPermissionsSelector->getSelectedCollision(); block->elevation = this->movementPermissionsSelector->getSelectedElevation(); - map->setBlock(x, y, *block); + map->setBlock(x, y, *block, true); } if (event->type() == QEvent::GraphicsSceneMouseRelease) { map->commit(); diff --git a/src/ui/mappixmapitem.cpp b/src/ui/mappixmapitem.cpp index 5b565be2..d6f4b2c4 100644 --- a/src/ui/mappixmapitem.cpp +++ b/src/ui/mappixmapitem.cpp @@ -131,7 +131,7 @@ QList MapPixmapItem::smartPathTable = QList({ #define IS_SMART_PATH_TILE(block) (selectedMetatiles->contains(block->tile)) -void MapPixmapItem::paintSmartPath(int x, int y) { +void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) { QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); @@ -165,7 +165,7 @@ void MapPixmapItem::paintSmartPath(int x, int y) { block->collision = openTileCollision; block->elevation = openTileElevation; } - map->setBlock(actualX, actualY, *block); + map->setBlock(actualX, actualY, *block, !fromScriptCall); } } @@ -209,7 +209,7 @@ void MapPixmapItem::paintSmartPath(int x, int y) { block->collision = selectedCollisions->at(smartPathTable[id]).first; block->elevation = selectedCollisions->at(smartPathTable[id]).second; } - map->setBlock(actualX, actualY, *block); + map->setBlock(actualX, actualY, *block, !fromScriptCall); } } @@ -425,7 +425,7 @@ void MapPixmapItem::floodFill( delete[] visited; } -void MapPixmapItem::floodFillSmartPath(int initialX, int initialY) { +void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScriptCall) { QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); @@ -467,7 +467,7 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY) { block->collision = openTileCollision; block->elevation = openTileElevation; } - map->setBlock(x, y, *block); + map->setBlock(x, y, *block, !fromScriptCall); if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) { todo.append(QPoint(x + 1, y)); } @@ -522,7 +522,7 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY) { block->collision = selectedCollisions->at(smartPathTable[id]).first; block->elevation = selectedCollisions->at(smartPathTable[id]).second; } - map->setBlock(x, y, *block); + map->setBlock(x, y, *block, !fromScriptCall); // 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)) { diff --git a/test_script.js b/test_script.js index 28b5deb1..bd14687c 100644 --- a/test_script.js +++ b/test_script.js @@ -1,26 +1,6 @@ -const morningTint = [0.8, 0.7, 0.9]; -const nightTint = [0.6, 0.55, 1.0]; - -function applyTint(palette, tint) { - for (let i = 0; i < palette.length; i++) { - const color = palette[i]; - for (let j = 0; j < tint.length; j++) { - color[j] = Math.floor(color[j] * tint[j]); - } - } -} - -// Porymap callback when a map is opened. -export function on_map_opened(mapName) { +export function on_block_changed(x, y, prevBlock, newBlock) { try { - for (let i = 0; i < 13; i++) { - const primaryPalette = map.getPrimaryTilesetPalette(i) - applyTint(primaryPalette, morningTint) - map.setPrimaryTilesetPalettePreview(i, primaryPalette) - const secondaryPalette = map.getSecondaryTilesetPalette(i) - applyTint(secondaryPalette, morningTint) - map.setSecondaryTilesetPalettePreview(i, secondaryPalette) - } + console.log("on_block_changed", x, y) } catch (err) { console.log(err) } From 09a892f5251d4d7a73b59fd5cb04e485208df6fd Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sun, 3 May 2020 11:28:02 -0500 Subject: [PATCH 08/17] Add ability to register custom actions --- include/mainwindow.h | 1 + include/scripting.h | 6 ++++++ src/mainwindow.cpp | 17 +++++++++++++++ src/scripting.cpp | 39 ++++++++++++++++++++++++++++++++++ test_script.js | 50 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 110 insertions(+), 3 deletions(-) diff --git a/include/mainwindow.h b/include/mainwindow.h index 518c3d9d..751c1e90 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -72,6 +72,7 @@ public: void setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors); Q_INVOKABLE void setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors); Q_INVOKABLE void setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors); + Q_INVOKABLE void registerAction(QString functionName, QString actionName); public slots: diff --git a/include/scripting.h b/include/scripting.h index 614965ea..e9fc9916 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -8,6 +8,7 @@ #include enum CallbackType { + OnProjectOpened, OnBlockChanged, OnMapOpened, }; @@ -20,6 +21,10 @@ public: static QJSValue dimensions(int width, int height); static QJSEngine *getEngine(); static void init(MainWindow *mainWindow); + static void registerAction(QString functionName, QString actionName); + static int numRegisteredActions(); + static void invokeAction(QString actionName); + static void cb_ProjectOpened(QString projectPath); static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock); static void cb_MapOpened(QString mapName); @@ -27,6 +32,7 @@ private: QJSEngine *engine; QStringList filepaths; QList modules; + QMap registeredActions; void loadModules(QStringList moduleFiles); void invokeCallback(CallbackType type, QJSValueList args); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ed9653d0..3ba56dde 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -348,6 +348,10 @@ bool MainWindow::openProject(QString dir) { } + if (success) { + Scripting::cb_ProjectOpened(dir); + } + return success; } @@ -2844,3 +2848,16 @@ void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QListsetTilesetPalettePreview(this->editor->map->layout->tileset_secondary, paletteIndex, colors); } + +void MainWindow::registerAction(QString functionName, QString actionName) { + if (!this->ui || !this->ui->menuTools) + return; + + Scripting::registerAction(functionName, actionName); + if (Scripting::numRegisteredActions() == 1) { + this->ui->menuTools->addSeparator(); + } + this->ui->menuTools->addAction(actionName, [actionName](){ + Scripting::invokeAction(actionName); + }); +} diff --git a/src/scripting.cpp b/src/scripting.cpp index 4fdab50d..ecd34d51 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -2,6 +2,7 @@ #include "log.h" QMap callbackFunctions = { + {OnProjectOpened, "on_project_opened"}, {OnBlockChanged, "on_block_changed"}, {OnMapOpened, "on_map_opened"}, }; @@ -54,6 +55,44 @@ void Scripting::invokeCallback(CallbackType type, QJSValueList args) { } } +void Scripting::registerAction(QString functionName, QString actionName) { + if (!instance) return; + instance->registeredActions.insert(actionName, functionName); +} + +int Scripting::numRegisteredActions() { + if (!instance) return 0; + return instance->registeredActions.size(); +} + +void Scripting::invokeAction(QString actionName) { + if (!instance) return; + if (!instance->registeredActions.contains(actionName)) return; + + QString functionName = instance->registeredActions.value(actionName); + for (QJSValue module : instance->modules) { + QJSValue callbackFunction = module.property(functionName); + if (callbackFunction.isError()) { + continue; + } + + QJSValue result = callbackFunction.call(QJSValueList()); + if (result.isError()) { + logError(QString("Module %1 encountered an error when calling '%2'").arg(module.toString()).arg(functionName)); + continue; + } + } +} + +void Scripting::cb_ProjectOpened(QString projectPath) { + if (!instance) return; + + QJSValueList args { + projectPath, + }; + instance->invokeCallback(OnProjectOpened, args); +} + void Scripting::cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock) { if (!instance) return; diff --git a/test_script.js b/test_script.js index bd14687c..85b3e72c 100644 --- a/test_script.js +++ b/test_script.js @@ -1,7 +1,51 @@ -export function on_block_changed(x, y, prevBlock, newBlock) { +const normalTint = [1, 1, 1] +const morningTint = [0.8, 0.7, 0.9]; +const nightTint = [0.6, 0.55, 1.0]; + +function applyTint(palette, tint) { + for (let i = 0; i < palette.length; i++) { + const color = palette[i]; + for (let j = 0; j < tint.length; j++) { + color[j] = Math.floor(color[j] * tint[j]); + } + } +} + +function applyTintToPalettes(tint) { try { - console.log("on_block_changed", x, y) - } catch (err) { + for (let i = 0; i < 13; i++) { + const primaryPalette = map.getPrimaryTilesetPalette(i) + applyTint(primaryPalette, tint) + map.setPrimaryTilesetPalettePreview(i, primaryPalette) + const secondaryPalette = map.getSecondaryTilesetPalette(i) + applyTint(secondaryPalette, tint) + map.setSecondaryTilesetPalettePreview(i, secondaryPalette) + } + } catch(err) { console.log(err) } } + +// Porymap callback when project is opened. +export function on_project_opened(projectPath) { + try { + console.log(`opened ${projectPath}`) + map.registerAction("resetTint", "View Day Tint") + map.registerAction("applyMorningTint", "View Morning Tint") + map.registerAction("applyNightTint", "View Night Tint") + } catch(err) { + console.log(err) + } +} + +export function resetTint() { + applyTintToPalettes(normalTint) +} + +export function applyMorningTint() { + applyTintToPalettes(morningTint) +} + +export function applyNightTint() { + applyTintToPalettes(nightTint) +} From bf64764103458a6f5ed718de93139dec630c8f39 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Thu, 7 May 2020 20:00:14 -0500 Subject: [PATCH 09/17] Implement the rest of the palette api --- include/mainwindow.h | 19 +++++- src/mainwindow.cpp | 156 +++++++++++++++++++++++++++++++++++++------ test_script.js | 19 +++--- 3 files changed, 163 insertions(+), 31 deletions(-) diff --git a/include/mainwindow.h b/include/mainwindow.h index 751c1e90..bd7f4408 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -63,15 +63,32 @@ public: Q_INVOKABLE void addRect(int x, int y, int width, int height, QString color = "#000000"); Q_INVOKABLE void addFilledRect(int x, int y, int width, int height, QString color = "#000000"); Q_INVOKABLE void addImage(int x, int y, QString filepath); + void refreshAfterPaletteChange(Tileset *tileset); void setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors); Q_INVOKABLE void setPrimaryTilesetPalette(int paletteIndex, QList> colors); + Q_INVOKABLE void setPrimaryTilesetPalettes(QList>> palettes); Q_INVOKABLE void setSecondaryTilesetPalette(int paletteIndex, QList> colors); - QJSValue getTilesetPalette(Tileset *tileset, int paletteIndex); + Q_INVOKABLE void setSecondaryTilesetPalettes(QList>> palettes); + QJSValue getTilesetPalette(QList> *palettes, int paletteIndex); + QJSValue getTilesetPalettes(QList> *palettes); Q_INVOKABLE QJSValue getPrimaryTilesetPalette(int paletteIndex); + Q_INVOKABLE QJSValue getPrimaryTilesetPalettes(); Q_INVOKABLE QJSValue getSecondaryTilesetPalette(int paletteIndex); + Q_INVOKABLE QJSValue getSecondaryTilesetPalettes(); + void refreshAfterPalettePreviewChange(); void setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors); Q_INVOKABLE void setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors); + Q_INVOKABLE void setPrimaryTilesetPalettesPreview(QList>> palettes); Q_INVOKABLE void setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors); + Q_INVOKABLE void setSecondaryTilesetPalettesPreview(QList>> palettes); + Q_INVOKABLE QJSValue getPrimaryTilesetPalettePreview(int paletteIndex); + Q_INVOKABLE QJSValue getPrimaryTilesetPalettesPreview(); + Q_INVOKABLE QJSValue getSecondaryTilesetPalettePreview(int paletteIndex); + Q_INVOKABLE QJSValue getSecondaryTilesetPalettesPreview(); + Q_INVOKABLE QString getPrimaryTileset(); + Q_INVOKABLE QString getSecondaryTileset(); + Q_INVOKABLE void setPrimaryTileset(QString tileset); + Q_INVOKABLE void setSecondaryTileset(QString tileset); Q_INVOKABLE void registerAction(QString functionName, QString actionName); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3ba56dde..27e29645 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2754,6 +2754,18 @@ void MainWindow::addImage(int x, int y, QString filepath) { this->ui->graphicsView_Map->overlay.addImage(x, y, filepath); } +void MainWindow::refreshAfterPaletteChange(Tileset *tileset) { + if (this->tilesetEditor) { + this->tilesetEditor->setTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); + } + this->editor->metatile_selector_item->draw(); + this->editor->selected_border_metatiles_item->draw(); + this->editor->map_item->draw(true); + this->editor->updateMapBorder(); + this->editor->updateMapConnections(); + this->editor->project->saveTilesetPalettes(tileset); +} + void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors) { if (!this->editor || !this->editor->map || !this->editor->map->layout) return; @@ -2768,51 +2780,93 @@ void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QListpalettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); (*tileset->palettePreviews)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); } - - if (this->tilesetEditor) { - this->tilesetEditor->setTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); - } - this->editor->metatile_selector_item->draw(); - this->editor->selected_border_metatiles_item->draw(); - this->editor->map_item->draw(true); - this->editor->updateMapBorder(); - this->editor->updateMapConnections(); - this->editor->project->saveTilesetPalettes(tileset); } void MainWindow::setPrimaryTilesetPalette(int paletteIndex, QList> colors) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) return; this->setTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex, colors); + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); +} + +void MainWindow::setPrimaryTilesetPalettes(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalette(this->editor->map->layout->tileset_primary, i, palettes[i]); + } + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); } void MainWindow::setSecondaryTilesetPalette(int paletteIndex, QList> colors) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) return; this->setTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex, colors); + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); } -QJSValue MainWindow::getTilesetPalette(Tileset *tileset, int paletteIndex) { - if (paletteIndex >= tileset->palettes->size()) +void MainWindow::setSecondaryTilesetPalettes(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalette(this->editor->map->layout->tileset_secondary, i, palettes[i]); + } + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); +} + +QJSValue MainWindow::getTilesetPalette(QList> *palettes, int paletteIndex) { + if (paletteIndex >= palettes->size()) return QJSValue(); QList> palette; - for (auto color : tileset->palettes->value(paletteIndex)) { + for (auto color : palettes->value(paletteIndex)) { palette.append(QList({qRed(color), qGreen(color), qBlue(color)})); } return Scripting::getEngine()->toScriptValue(palette); } +QJSValue MainWindow::getTilesetPalettes(QList> *palettes) { + QList>> outPalettes; + for (int i = 0; i < palettes->size(); i++) { + QList> colors; + for (auto color : palettes->value(i)) { + colors.append(QList({qRed(color), qGreen(color), qBlue(color)})); + } + outPalettes.append(colors); + } + return Scripting::getEngine()->toScriptValue(outPalettes); +} + QJSValue MainWindow::getPrimaryTilesetPalette(int paletteIndex) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex); + return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettes, paletteIndex); +} + +QJSValue MainWindow::getPrimaryTilesetPalettes() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettes); } QJSValue MainWindow::getSecondaryTilesetPalette(int paletteIndex) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex); + return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettes, paletteIndex); +} + +QJSValue MainWindow::getSecondaryTilesetPalettes() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettes); +} + +void MainWindow::refreshAfterPalettePreviewChange() { + this->editor->metatile_selector_item->draw(); + this->editor->selected_border_metatiles_item->draw(); + this->editor->map_item->draw(true); + this->editor->updateMapBorder(); + this->editor->updateMapConnections(); } void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors) { @@ -2829,24 +2883,82 @@ void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QL auto palettes = tileset->palettePreviews; (*palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); } - - this->editor->metatile_selector_item->draw(); - this->editor->selected_border_metatiles_item->draw(); - this->editor->map_item->draw(true); - this->editor->updateMapBorder(); - this->editor->updateMapConnections(); } void MainWindow::setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) return; this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, paletteIndex, colors); + this->refreshAfterPalettePreviewChange(); +} + +void MainWindow::setPrimaryTilesetPalettesPreview(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, i, palettes[i]); + } + this->refreshAfterPalettePreviewChange(); } void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) return; this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, paletteIndex, colors); + this->refreshAfterPalettePreviewChange(); +} + +void MainWindow::setSecondaryTilesetPalettesPreview(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, i, palettes[i]); + } + this->refreshAfterPalettePreviewChange(); +} + +QJSValue MainWindow::getPrimaryTilesetPalettePreview(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettePreviews, paletteIndex); +} + +QJSValue MainWindow::getPrimaryTilesetPalettesPreview() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettePreviews); +} + +QJSValue MainWindow::getSecondaryTilesetPalettePreview(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettePreviews, paletteIndex); +} + +QJSValue MainWindow::getSecondaryTilesetPalettesPreview() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettePreviews); +} + +QString MainWindow::getPrimaryTileset() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QString(); + return this->editor->map->layout->tileset_primary->name; +} + +QString MainWindow::getSecondaryTileset() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QString(); + return this->editor->map->layout->tileset_secondary->name; +} + +void MainWindow::setPrimaryTileset(QString tileset) { + this->on_comboBox_PrimaryTileset_currentTextChanged(tileset); +} + +void MainWindow::setSecondaryTileset(QString tileset) { + this->on_comboBox_SecondaryTileset_currentTextChanged(tileset); } void MainWindow::registerAction(QString functionName, QString actionName) { @@ -2855,7 +2967,7 @@ void MainWindow::registerAction(QString functionName, QString actionName) { Scripting::registerAction(functionName, actionName); if (Scripting::numRegisteredActions() == 1) { - this->ui->menuTools->addSeparator(); + this->ui->menuTools->addSection("Custom Actions"); } this->ui->menuTools->addAction(actionName, [actionName](){ Scripting::invokeAction(actionName); diff --git a/test_script.js b/test_script.js index 85b3e72c..c807898a 100644 --- a/test_script.js +++ b/test_script.js @@ -13,14 +13,17 @@ function applyTint(palette, tint) { function applyTintToPalettes(tint) { try { - for (let i = 0; i < 13; i++) { - const primaryPalette = map.getPrimaryTilesetPalette(i) - applyTint(primaryPalette, tint) - map.setPrimaryTilesetPalettePreview(i, primaryPalette) - const secondaryPalette = map.getSecondaryTilesetPalette(i) - applyTint(secondaryPalette, tint) - map.setSecondaryTilesetPalettePreview(i, secondaryPalette) - } + // const primaryPalettes = map.getPrimaryTilesetPalettes() + // for (let i = 0; i < primaryPalettes.length; i++) + // applyTint(primaryPalettes[i], tint) + + // const secondaryPalettes = map.getSecondaryTilesetPalettes() + // for (let i = 0; i < secondaryPalettes.length; i++) + // applyTint(secondaryPalettes[i], tint) + + // map.setPrimaryTilesetPalettesPreview(primaryPalettes) + // map.setSecondaryTilesetPalettesPreview(secondaryPalettes) + map.setSecondaryTileset("gTileset_Rustboro") } catch(err) { console.log(err) } From 7072c001afeaf7c0ac94536218147ac0a30742f0 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Thu, 7 May 2020 20:13:56 -0500 Subject: [PATCH 10/17] Implement api for checkboxes --- include/mainwindow.h | 6 ++++++ src/mainwindow.cpp | 24 ++++++++++++++++++++++++ test_script.js | 5 ++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/mainwindow.h b/include/mainwindow.h index bd7f4408..bb85e62a 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -89,6 +89,12 @@ public: Q_INVOKABLE QString getSecondaryTileset(); Q_INVOKABLE void setPrimaryTileset(QString tileset); Q_INVOKABLE void setSecondaryTileset(QString tileset); + Q_INVOKABLE void setGridVisibility(bool visible); + Q_INVOKABLE bool getGridVisibility(); + Q_INVOKABLE void setBorderVisibility(bool visible); + Q_INVOKABLE bool getBorderVisibility(); + Q_INVOKABLE void setSmartPathsEnabled(bool visible); + Q_INVOKABLE bool getSmartPathsEnabled(); Q_INVOKABLE void registerAction(QString functionName, QString actionName); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 27e29645..310fe8b1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2961,6 +2961,30 @@ void MainWindow::setSecondaryTileset(QString tileset) { this->on_comboBox_SecondaryTileset_currentTextChanged(tileset); } +void MainWindow::setGridVisibility(bool visible) { + this->ui->checkBox_ToggleGrid->setChecked(visible); +} + +bool MainWindow::getGridVisibility() { + return this->ui->checkBox_ToggleGrid->isChecked(); +} + +void MainWindow::setBorderVisibility(bool visible) { + this->editor->toggleBorderVisibility(visible); +} + +bool MainWindow::getBorderVisibility() { + return this->ui->checkBox_ToggleBorder->isChecked(); +} + +void MainWindow::setSmartPathsEnabled(bool visible) { + this->ui->checkBox_smartPaths->setChecked(visible); +} + +bool MainWindow::getSmartPathsEnabled() { + return this->ui->checkBox_smartPaths->isChecked(); +} + void MainWindow::registerAction(QString functionName, QString actionName) { if (!this->ui || !this->ui->menuTools) return; diff --git a/test_script.js b/test_script.js index c807898a..31965ce5 100644 --- a/test_script.js +++ b/test_script.js @@ -11,6 +11,8 @@ function applyTint(palette, tint) { } } +let visible = true; + function applyTintToPalettes(tint) { try { // const primaryPalettes = map.getPrimaryTilesetPalettes() @@ -23,7 +25,8 @@ function applyTintToPalettes(tint) { // map.setPrimaryTilesetPalettesPreview(primaryPalettes) // map.setSecondaryTilesetPalettesPreview(secondaryPalettes) - map.setSecondaryTileset("gTileset_Rustboro") + console.log(map.setSmartPathsEnabled(visible)) + visible = !visible } catch(err) { console.log(err) } From 71242e5714b5dad4a618a06b57467d6b52908732 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Fri, 8 May 2020 08:49:29 -0500 Subject: [PATCH 11/17] Add ability to set keyboard shortcuts for custom actions --- include/mainwindow.h | 2 +- src/mainwindow.cpp | 7 +++++-- test_script.js | 26 +++++++++++--------------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/include/mainwindow.h b/include/mainwindow.h index bb85e62a..b2b651a8 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -95,7 +95,7 @@ public: Q_INVOKABLE bool getBorderVisibility(); Q_INVOKABLE void setSmartPathsEnabled(bool visible); Q_INVOKABLE bool getSmartPathsEnabled(); - Q_INVOKABLE void registerAction(QString functionName, QString actionName); + Q_INVOKABLE void registerAction(QString functionName, QString actionName, QString shortcut = ""); public slots: diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 310fe8b1..e562ae64 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2985,7 +2985,7 @@ bool MainWindow::getSmartPathsEnabled() { return this->ui->checkBox_smartPaths->isChecked(); } -void MainWindow::registerAction(QString functionName, QString actionName) { +void MainWindow::registerAction(QString functionName, QString actionName, QString shortcut) { if (!this->ui || !this->ui->menuTools) return; @@ -2993,7 +2993,10 @@ void MainWindow::registerAction(QString functionName, QString actionName) { if (Scripting::numRegisteredActions() == 1) { this->ui->menuTools->addSection("Custom Actions"); } - this->ui->menuTools->addAction(actionName, [actionName](){ + QAction *action = this->ui->menuTools->addAction(actionName, [actionName](){ Scripting::invokeAction(actionName); }); + if (!shortcut.isEmpty()) { + action->setShortcut(QKeySequence(shortcut)); + } } diff --git a/test_script.js b/test_script.js index 31965ce5..c10f9b39 100644 --- a/test_script.js +++ b/test_script.js @@ -11,22 +11,18 @@ function applyTint(palette, tint) { } } -let visible = true; - function applyTintToPalettes(tint) { try { - // const primaryPalettes = map.getPrimaryTilesetPalettes() - // for (let i = 0; i < primaryPalettes.length; i++) - // applyTint(primaryPalettes[i], tint) + const primaryPalettes = map.getPrimaryTilesetPalettes() + for (let i = 0; i < primaryPalettes.length; i++) + applyTint(primaryPalettes[i], tint) - // const secondaryPalettes = map.getSecondaryTilesetPalettes() - // for (let i = 0; i < secondaryPalettes.length; i++) - // applyTint(secondaryPalettes[i], tint) + const secondaryPalettes = map.getSecondaryTilesetPalettes() + for (let i = 0; i < secondaryPalettes.length; i++) + applyTint(secondaryPalettes[i], tint) - // map.setPrimaryTilesetPalettesPreview(primaryPalettes) - // map.setSecondaryTilesetPalettesPreview(secondaryPalettes) - console.log(map.setSmartPathsEnabled(visible)) - visible = !visible + map.setPrimaryTilesetPalettesPreview(primaryPalettes) + map.setSecondaryTilesetPalettesPreview(secondaryPalettes) } catch(err) { console.log(err) } @@ -36,9 +32,9 @@ function applyTintToPalettes(tint) { export function on_project_opened(projectPath) { try { console.log(`opened ${projectPath}`) - map.registerAction("resetTint", "View Day Tint") - map.registerAction("applyMorningTint", "View Morning Tint") - map.registerAction("applyNightTint", "View Night Tint") + map.registerAction("resetTint", "View Day Tint", "K") + map.registerAction("applyMorningTint", "View Morning Tint", "N") + map.registerAction("applyNightTint", "View Night Tint", "Ctrl+J") } catch(err) { console.log(err) } From 3c1549cc93be4db1eeb36ea727d7cc8652ce9b9e Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Fri, 8 May 2020 09:46:26 -0500 Subject: [PATCH 12/17] Add setTimeout to script api, and properly refresh scene when overlay is changed --- include/mainwindow.h | 2 ++ src/mainwindow.cpp | 22 ++++++++++++++++++++ src/ui/overlay.cpp | 1 - test_script.js | 48 +++++++++++++++++++++++++++++++++----------- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/include/mainwindow.h b/include/mainwindow.h index b2b651a8..eed46769 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -96,6 +96,8 @@ public: Q_INVOKABLE void setSmartPathsEnabled(bool visible); Q_INVOKABLE bool getSmartPathsEnabled(); Q_INVOKABLE void registerAction(QString functionName, QString actionName, QString shortcut = ""); + Q_INVOKABLE void setTimeout(QJSValue callback, int milliseconds); + void invokeCallback(QJSValue callback); public slots: diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e562ae64..8fc9e8d9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2728,30 +2728,35 @@ void MainWindow::clearOverlay() { if (!this->ui || !this->ui->graphicsView_Map) return; this->ui->graphicsView_Map->overlay.clearItems(); + this->ui->graphicsView_Map->scene()->update(); } void MainWindow::addText(QString text, int x, int y, QString color, int fontSize) { if (!this->ui || !this->ui->graphicsView_Map) return; this->ui->graphicsView_Map->overlay.addText(text, x, y, color, fontSize); + this->ui->graphicsView_Map->scene()->update(); } void MainWindow::addRect(int x, int y, int width, int height, QString color) { if (!this->ui || !this->ui->graphicsView_Map) return; this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, false); + this->ui->graphicsView_Map->scene()->update(); } void MainWindow::addFilledRect(int x, int y, int width, int height, QString color) { if (!this->ui || !this->ui->graphicsView_Map) return; this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, true); + this->ui->graphicsView_Map->scene()->update(); } void MainWindow::addImage(int x, int y, QString filepath) { if (!this->ui || !this->ui->graphicsView_Map) return; this->ui->graphicsView_Map->overlay.addImage(x, y, filepath); + this->ui->graphicsView_Map->scene()->update(); } void MainWindow::refreshAfterPaletteChange(Tileset *tileset) { @@ -3000,3 +3005,20 @@ void MainWindow::registerAction(QString functionName, QString actionName, QStrin action->setShortcut(QKeySequence(shortcut)); } } + +void MainWindow::setTimeout(QJSValue callback, int milliseconds) { + if (!callback.isCallable() || milliseconds < 0) + return; + + QTimer *timer = new QTimer(0); + connect(timer, &QTimer::timeout, [=](){ + this->invokeCallback(callback); + }); + connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater())); + timer->setSingleShot(true); + timer->start(milliseconds); +} + +void MainWindow::invokeCallback(QJSValue callback) { + callback.call(); +} diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index f1be1a94..004a1fed 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -43,4 +43,3 @@ void Overlay::addRect(int x, int y, int width, int height, QString color, bool f void Overlay::addImage(int x, int y, QString filepath) { this->items.append(new OverlayImage(x, y, QImage(filepath))); } - diff --git a/test_script.js b/test_script.js index c10f9b39..1e7913b8 100644 --- a/test_script.js +++ b/test_script.js @@ -28,18 +28,6 @@ function applyTintToPalettes(tint) { } } -// Porymap callback when project is opened. -export function on_project_opened(projectPath) { - try { - console.log(`opened ${projectPath}`) - map.registerAction("resetTint", "View Day Tint", "K") - map.registerAction("applyMorningTint", "View Morning Tint", "N") - map.registerAction("applyNightTint", "View Night Tint", "Ctrl+J") - } catch(err) { - console.log(err) - } -} - export function resetTint() { applyTintToPalettes(normalTint) } @@ -51,3 +39,39 @@ export function applyMorningTint() { export function applyNightTint() { applyTintToPalettes(nightTint) } + +let curTint = 0 + +function nextTint() { + map.clearOverlay() + map.addRect(curTint * 40, 40, 40, 40, "#FFFF0000") + curTint = (curTint + 1) % 4 + map.setTimeout(nextTint, 100) +} + +export function animated() { + try { + map.setTimeout(nextTint, 1000) + } catch(err) { + console.log(err) + } +} + +let registered = false + +// Porymap callback when project is opened. +export function on_project_opened(projectPath) { + if (registered) { + return; + } + + try { + // map.registerAction("resetTint", "View Day Tint", "K") + // map.registerAction("applyMorningTint", "View Morning Tint", "N") + // map.registerAction("applyNightTint", "View Night Tint", "Ctrl+J") + map.registerAction("animated", "Animate") + registered = true; + } catch(err) { + console.log(err) + } +} From b8d0268d5d49676a8d153db9bfad95506ccd2ace Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Fri, 8 May 2020 09:59:01 -0500 Subject: [PATCH 13/17] Move script api into separate file --- include/mainwindow.h | 1 + porymap.pro | 1 + src/mainwindow.cpp | 459 ---------------------------------- src/mainwindow_scriptapi.cpp | 462 +++++++++++++++++++++++++++++++++++ 4 files changed, 464 insertions(+), 459 deletions(-) create mode 100644 src/mainwindow_scriptapi.cpp diff --git a/include/mainwindow.h b/include/mainwindow.h index eed46769..764a0a9e 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -38,6 +38,7 @@ public: MainWindow(const MainWindow &) = delete; MainWindow & operator = (const MainWindow &) = delete; + // Scripting API 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); diff --git a/porymap.pro b/porymap.pro index 2a138e30..907ee041 100644 --- a/porymap.pro +++ b/porymap.pro @@ -31,6 +31,7 @@ SOURCES += src/core/block.cpp \ src/core/regionmap.cpp \ src/core/wildmoninfo.cpp \ src/lib/orderedjson.cpp \ + src/mainwindow_scriptapi.cpp \ src/ui/aboutporymap.cpp \ src/ui/bordermetatilespixmapitem.cpp \ src/ui/collisionpixmapitem.cpp \ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8fc9e8d9..f4328f1a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2563,462 +2563,3 @@ void MainWindow::closeEvent(QCloseEvent *event) { QMainWindow::closeEvent(event); } - -QJSValue MainWindow::getBlock(int x, int y) { - if (!this->editor || !this->editor->map) - return QJSValue(); - Block *block = this->editor->map->getBlock(x, y); - if (!block) { - return Scripting::fromBlock(Block()); - } - return Scripting::fromBlock(*block); -} - -void MainWindow::setBlock(int x, int y, int tile, int collision, int elevation) { - if (!this->editor || !this->editor->map) - return; - this->editor->map->setBlock(x, y, Block(tile, collision, elevation)); -} - -void MainWindow::setBlocksFromSelection(int x, int y) { - if (this->editor && this->editor->map_item) { - this->editor->map_item->paintNormal(x, y, true); - } -} - -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(); -} - -void MainWindow::clearOverlay() { - if (!this->ui || !this->ui->graphicsView_Map) - return; - this->ui->graphicsView_Map->overlay.clearItems(); - this->ui->graphicsView_Map->scene()->update(); -} - -void MainWindow::addText(QString text, int x, int y, QString color, int fontSize) { - if (!this->ui || !this->ui->graphicsView_Map) - return; - this->ui->graphicsView_Map->overlay.addText(text, x, y, color, fontSize); - this->ui->graphicsView_Map->scene()->update(); -} - -void MainWindow::addRect(int x, int y, int width, int height, QString color) { - if (!this->ui || !this->ui->graphicsView_Map) - return; - this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, false); - this->ui->graphicsView_Map->scene()->update(); -} - -void MainWindow::addFilledRect(int x, int y, int width, int height, QString color) { - if (!this->ui || !this->ui->graphicsView_Map) - return; - this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, true); - this->ui->graphicsView_Map->scene()->update(); -} - -void MainWindow::addImage(int x, int y, QString filepath) { - if (!this->ui || !this->ui->graphicsView_Map) - return; - this->ui->graphicsView_Map->overlay.addImage(x, y, filepath); - this->ui->graphicsView_Map->scene()->update(); -} - -void MainWindow::refreshAfterPaletteChange(Tileset *tileset) { - if (this->tilesetEditor) { - this->tilesetEditor->setTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); - } - this->editor->metatile_selector_item->draw(); - this->editor->selected_border_metatiles_item->draw(); - this->editor->map_item->draw(true); - this->editor->updateMapBorder(); - this->editor->updateMapConnections(); - this->editor->project->saveTilesetPalettes(tileset); -} - -void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout) - return; - if (paletteIndex >= tileset->palettes->size()) - return; - if (colors.size() != 16) - return; - - for (int i = 0; i < 16; i++) { - if (colors[i].size() != 3) - continue; - (*tileset->palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); - (*tileset->palettePreviews)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); - } -} - -void MainWindow::setPrimaryTilesetPalette(int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return; - this->setTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex, colors); - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); -} - -void MainWindow::setPrimaryTilesetPalettes(QList>> palettes) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return; - for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalette(this->editor->map->layout->tileset_primary, i, palettes[i]); - } - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); -} - -void MainWindow::setSecondaryTilesetPalette(int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return; - this->setTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex, colors); - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); -} - -void MainWindow::setSecondaryTilesetPalettes(QList>> palettes) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return; - for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalette(this->editor->map->layout->tileset_secondary, i, palettes[i]); - } - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); -} - -QJSValue MainWindow::getTilesetPalette(QList> *palettes, int paletteIndex) { - if (paletteIndex >= palettes->size()) - return QJSValue(); - - QList> palette; - for (auto color : palettes->value(paletteIndex)) { - palette.append(QList({qRed(color), qGreen(color), qBlue(color)})); - } - return Scripting::getEngine()->toScriptValue(palette); -} - -QJSValue MainWindow::getTilesetPalettes(QList> *palettes) { - QList>> outPalettes; - for (int i = 0; i < palettes->size(); i++) { - QList> colors; - for (auto color : palettes->value(i)) { - colors.append(QList({qRed(color), qGreen(color), qBlue(color)})); - } - outPalettes.append(colors); - } - return Scripting::getEngine()->toScriptValue(outPalettes); -} - -QJSValue MainWindow::getPrimaryTilesetPalette(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettes, paletteIndex); -} - -QJSValue MainWindow::getPrimaryTilesetPalettes() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettes); -} - -QJSValue MainWindow::getSecondaryTilesetPalette(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettes, paletteIndex); -} - -QJSValue MainWindow::getSecondaryTilesetPalettes() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettes); -} - -void MainWindow::refreshAfterPalettePreviewChange() { - this->editor->metatile_selector_item->draw(); - this->editor->selected_border_metatiles_item->draw(); - this->editor->map_item->draw(true); - this->editor->updateMapBorder(); - this->editor->updateMapConnections(); -} - -void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout) - return; - if (paletteIndex >= tileset->palettePreviews->size()) - return; - if (colors.size() != 16) - return; - - for (int i = 0; i < 16; i++) { - if (colors[i].size() != 3) - continue; - auto palettes = tileset->palettePreviews; - (*palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); - } -} - -void MainWindow::setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return; - this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, paletteIndex, colors); - this->refreshAfterPalettePreviewChange(); -} - -void MainWindow::setPrimaryTilesetPalettesPreview(QList>> palettes) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return; - for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, i, palettes[i]); - } - this->refreshAfterPalettePreviewChange(); -} - -void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return; - this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, paletteIndex, colors); - this->refreshAfterPalettePreviewChange(); -} - -void MainWindow::setSecondaryTilesetPalettesPreview(QList>> palettes) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return; - for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, i, palettes[i]); - } - this->refreshAfterPalettePreviewChange(); -} - -QJSValue MainWindow::getPrimaryTilesetPalettePreview(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettePreviews, paletteIndex); -} - -QJSValue MainWindow::getPrimaryTilesetPalettesPreview() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettePreviews); -} - -QJSValue MainWindow::getSecondaryTilesetPalettePreview(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettePreviews, paletteIndex); -} - -QJSValue MainWindow::getSecondaryTilesetPalettesPreview() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettePreviews); -} - -QString MainWindow::getPrimaryTileset() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) - return QString(); - return this->editor->map->layout->tileset_primary->name; -} - -QString MainWindow::getSecondaryTileset() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) - return QString(); - return this->editor->map->layout->tileset_secondary->name; -} - -void MainWindow::setPrimaryTileset(QString tileset) { - this->on_comboBox_PrimaryTileset_currentTextChanged(tileset); -} - -void MainWindow::setSecondaryTileset(QString tileset) { - this->on_comboBox_SecondaryTileset_currentTextChanged(tileset); -} - -void MainWindow::setGridVisibility(bool visible) { - this->ui->checkBox_ToggleGrid->setChecked(visible); -} - -bool MainWindow::getGridVisibility() { - return this->ui->checkBox_ToggleGrid->isChecked(); -} - -void MainWindow::setBorderVisibility(bool visible) { - this->editor->toggleBorderVisibility(visible); -} - -bool MainWindow::getBorderVisibility() { - return this->ui->checkBox_ToggleBorder->isChecked(); -} - -void MainWindow::setSmartPathsEnabled(bool visible) { - this->ui->checkBox_smartPaths->setChecked(visible); -} - -bool MainWindow::getSmartPathsEnabled() { - return this->ui->checkBox_smartPaths->isChecked(); -} - -void MainWindow::registerAction(QString functionName, QString actionName, QString shortcut) { - if (!this->ui || !this->ui->menuTools) - return; - - Scripting::registerAction(functionName, actionName); - if (Scripting::numRegisteredActions() == 1) { - this->ui->menuTools->addSection("Custom Actions"); - } - QAction *action = this->ui->menuTools->addAction(actionName, [actionName](){ - Scripting::invokeAction(actionName); - }); - if (!shortcut.isEmpty()) { - action->setShortcut(QKeySequence(shortcut)); - } -} - -void MainWindow::setTimeout(QJSValue callback, int milliseconds) { - if (!callback.isCallable() || milliseconds < 0) - return; - - QTimer *timer = new QTimer(0); - connect(timer, &QTimer::timeout, [=](){ - this->invokeCallback(callback); - }); - connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater())); - timer->setSingleShot(true); - timer->start(milliseconds); -} - -void MainWindow::invokeCallback(QJSValue callback) { - callback.call(); -} diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp new file mode 100644 index 00000000..f321595e --- /dev/null +++ b/src/mainwindow_scriptapi.cpp @@ -0,0 +1,462 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "scripting.h" + +QJSValue MainWindow::getBlock(int x, int y) { + if (!this->editor || !this->editor->map) + return QJSValue(); + Block *block = this->editor->map->getBlock(x, y); + if (!block) { + return Scripting::fromBlock(Block()); + } + return Scripting::fromBlock(*block); +} + +void MainWindow::setBlock(int x, int y, int tile, int collision, int elevation) { + if (!this->editor || !this->editor->map) + return; + this->editor->map->setBlock(x, y, Block(tile, collision, elevation)); +} + +void MainWindow::setBlocksFromSelection(int x, int y) { + if (this->editor && this->editor->map_item) { + this->editor->map_item->paintNormal(x, y, true); + } +} + +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(); +} + +void MainWindow::clearOverlay() { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.clearItems(); + this->ui->graphicsView_Map->scene()->update(); +} + +void MainWindow::addText(QString text, int x, int y, QString color, int fontSize) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addText(text, x, y, color, fontSize); + this->ui->graphicsView_Map->scene()->update(); +} + +void MainWindow::addRect(int x, int y, int width, int height, QString color) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, false); + this->ui->graphicsView_Map->scene()->update(); +} + +void MainWindow::addFilledRect(int x, int y, int width, int height, QString color) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addRect(x, y, width, height, color, true); + this->ui->graphicsView_Map->scene()->update(); +} + +void MainWindow::addImage(int x, int y, QString filepath) { + if (!this->ui || !this->ui->graphicsView_Map) + return; + this->ui->graphicsView_Map->overlay.addImage(x, y, filepath); + this->ui->graphicsView_Map->scene()->update(); +} + +void MainWindow::refreshAfterPaletteChange(Tileset *tileset) { + if (this->tilesetEditor) { + this->tilesetEditor->setTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); + } + this->editor->metatile_selector_item->draw(); + this->editor->selected_border_metatiles_item->draw(); + this->editor->map_item->draw(true); + this->editor->updateMapBorder(); + this->editor->updateMapConnections(); + this->editor->project->saveTilesetPalettes(tileset); +} + +void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout) + return; + if (paletteIndex >= tileset->palettes->size()) + return; + if (colors.size() != 16) + return; + + for (int i = 0; i < 16; i++) { + if (colors[i].size() != 3) + continue; + (*tileset->palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); + (*tileset->palettePreviews)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); + } +} + +void MainWindow::setPrimaryTilesetPalette(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + this->setTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex, colors); + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); +} + +void MainWindow::setPrimaryTilesetPalettes(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalette(this->editor->map->layout->tileset_primary, i, palettes[i]); + } + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); +} + +void MainWindow::setSecondaryTilesetPalette(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + this->setTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex, colors); + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); +} + +void MainWindow::setSecondaryTilesetPalettes(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalette(this->editor->map->layout->tileset_secondary, i, palettes[i]); + } + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); +} + +QJSValue MainWindow::getTilesetPalette(QList> *palettes, int paletteIndex) { + if (paletteIndex >= palettes->size()) + return QJSValue(); + + QList> palette; + for (auto color : palettes->value(paletteIndex)) { + palette.append(QList({qRed(color), qGreen(color), qBlue(color)})); + } + return Scripting::getEngine()->toScriptValue(palette); +} + +QJSValue MainWindow::getTilesetPalettes(QList> *palettes) { + QList>> outPalettes; + for (int i = 0; i < palettes->size(); i++) { + QList> colors; + for (auto color : palettes->value(i)) { + colors.append(QList({qRed(color), qGreen(color), qBlue(color)})); + } + outPalettes.append(colors); + } + return Scripting::getEngine()->toScriptValue(outPalettes); +} + +QJSValue MainWindow::getPrimaryTilesetPalette(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettes, paletteIndex); +} + +QJSValue MainWindow::getPrimaryTilesetPalettes() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettes); +} + +QJSValue MainWindow::getSecondaryTilesetPalette(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettes, paletteIndex); +} + +QJSValue MainWindow::getSecondaryTilesetPalettes() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettes); +} + +void MainWindow::refreshAfterPalettePreviewChange() { + this->editor->metatile_selector_item->draw(); + this->editor->selected_border_metatiles_item->draw(); + this->editor->map_item->draw(true); + this->editor->updateMapBorder(); + this->editor->updateMapConnections(); +} + +void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout) + return; + if (paletteIndex >= tileset->palettePreviews->size()) + return; + if (colors.size() != 16) + return; + + for (int i = 0; i < 16; i++) { + if (colors[i].size() != 3) + continue; + auto palettes = tileset->palettePreviews; + (*palettes)[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]); + } +} + +void MainWindow::setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, paletteIndex, colors); + this->refreshAfterPalettePreviewChange(); +} + +void MainWindow::setPrimaryTilesetPalettesPreview(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, i, palettes[i]); + } + this->refreshAfterPalettePreviewChange(); +} + +void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, paletteIndex, colors); + this->refreshAfterPalettePreviewChange(); +} + +void MainWindow::setSecondaryTilesetPalettesPreview(QList>> palettes) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return; + for (int i = 0; i < palettes.size(); i++) { + this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, i, palettes[i]); + } + this->refreshAfterPalettePreviewChange(); +} + +QJSValue MainWindow::getPrimaryTilesetPalettePreview(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettePreviews, paletteIndex); +} + +QJSValue MainWindow::getPrimaryTilesetPalettesPreview() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettePreviews); +} + +QJSValue MainWindow::getSecondaryTilesetPalettePreview(int paletteIndex) { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettePreviews, paletteIndex); +} + +QJSValue MainWindow::getSecondaryTilesetPalettesPreview() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QJSValue(); + return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettePreviews); +} + +QString MainWindow::getPrimaryTileset() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + return QString(); + return this->editor->map->layout->tileset_primary->name; +} + +QString MainWindow::getSecondaryTileset() { + if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + return QString(); + return this->editor->map->layout->tileset_secondary->name; +} + +void MainWindow::setPrimaryTileset(QString tileset) { + this->on_comboBox_PrimaryTileset_currentTextChanged(tileset); +} + +void MainWindow::setSecondaryTileset(QString tileset) { + this->on_comboBox_SecondaryTileset_currentTextChanged(tileset); +} + +void MainWindow::setGridVisibility(bool visible) { + this->ui->checkBox_ToggleGrid->setChecked(visible); +} + +bool MainWindow::getGridVisibility() { + return this->ui->checkBox_ToggleGrid->isChecked(); +} + +void MainWindow::setBorderVisibility(bool visible) { + this->editor->toggleBorderVisibility(visible); +} + +bool MainWindow::getBorderVisibility() { + return this->ui->checkBox_ToggleBorder->isChecked(); +} + +void MainWindow::setSmartPathsEnabled(bool visible) { + this->ui->checkBox_smartPaths->setChecked(visible); +} + +bool MainWindow::getSmartPathsEnabled() { + return this->ui->checkBox_smartPaths->isChecked(); +} + +void MainWindow::registerAction(QString functionName, QString actionName, QString shortcut) { + if (!this->ui || !this->ui->menuTools) + return; + + Scripting::registerAction(functionName, actionName); + if (Scripting::numRegisteredActions() == 1) { + this->ui->menuTools->addSection("Custom Actions"); + } + QAction *action = this->ui->menuTools->addAction(actionName, [actionName](){ + Scripting::invokeAction(actionName); + }); + if (!shortcut.isEmpty()) { + action->setShortcut(QKeySequence(shortcut)); + } +} + +void MainWindow::setTimeout(QJSValue callback, int milliseconds) { + if (!callback.isCallable() || milliseconds < 0) + return; + + QTimer *timer = new QTimer(0); + connect(timer, &QTimer::timeout, [=](){ + this->invokeCallback(callback); + }); + connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater())); + timer->setSingleShot(true); + timer->start(milliseconds); +} + +void MainWindow::invokeCallback(QJSValue callback) { + callback.call(); +} From 998ea1fcc89ba91da60137f05cf71ac27a2f27c3 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Fri, 8 May 2020 11:26:23 -0500 Subject: [PATCH 14/17] Add custom_scripts project config, and cleanup scripts when switching projects --- include/config.h | 5 ++++ include/mainwindow.h | 2 ++ include/scripting.h | 2 ++ src/config.cpp | 26 ++++++++++++++++++++- src/mainwindow.cpp | 6 ++++- src/mainwindow_scriptapi.cpp | 4 +++- src/scripting.cpp | 45 ++++++++++++++++++++++++++---------- 7 files changed, 75 insertions(+), 15 deletions(-) diff --git a/include/config.h b/include/config.h index f1b1d91b..19c64850 100644 --- a/include/config.h +++ b/include/config.h @@ -113,6 +113,7 @@ public: this->baseGameVersion = BaseGameVersion::pokeemerald; this->useEncounterJson = true; this->useCustomBorderSize = false; + this->customScripts.clear(); } void setBaseGameVersion(BaseGameVersion baseGameVersion); BaseGameVersion getBaseGameVersion(); @@ -121,8 +122,11 @@ public: void setUsePoryScript(bool usePoryScript); bool getUsePoryScript(); void setProjectDir(QString projectDir); + QString getProjectDir(); void setUseCustomBorderSize(bool enable); bool getUseCustomBorderSize(); + void setCustomScripts(QList scripts); + QList getCustomScripts(); protected: virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; @@ -134,6 +138,7 @@ private: bool useEncounterJson; bool usePoryScript; bool useCustomBorderSize; + QList customScripts; }; extern ProjectConfig projectConfig; diff --git a/include/mainwindow.h b/include/mainwindow.h index 764a0a9e..09cc440c 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -253,6 +253,8 @@ private: DraggablePixmapItem *selectedBG; DraggablePixmapItem *selectedHealspot; + QList registeredActions; + bool isProgrammaticEventTabChange; bool projectHasUnsavedChanges; diff --git a/include/scripting.h b/include/scripting.h index e9fc9916..99daac89 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -9,6 +9,7 @@ enum CallbackType { OnProjectOpened, + OnProjectClosed, OnBlockChanged, OnMapOpened, }; @@ -25,6 +26,7 @@ public: static int numRegisteredActions(); static void invokeAction(QString actionName); static void cb_ProjectOpened(QString projectPath); + static void cb_ProjectClosed(QString projectPath); static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock); static void cb_MapOpened(QString mapName); diff --git a/src/config.cpp b/src/config.cpp index 80fcaa83..0c102cf6 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -36,7 +36,7 @@ void KeyValueConfigBase::load() { QTextStream in(&file); in.setCodec("UTF-8"); QList configLines; - QRegularExpression re("^(?.+)=(?.+)$"); + QRegularExpression re("^(?.+)=(?.*)$"); while (!in.atEnd()) { QString line = in.readLine().trimmed(); int commentIndex = line.indexOf("#"); @@ -380,6 +380,15 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { if (!ok) { logWarn(QString("Invalid config value for use_custom_border_size: '%1'. Must be 0 or 1.").arg(value)); } + } else if (key == "custom_scripts") { + this->customScripts.clear(); + QList paths = value.split(","); + paths.removeDuplicates(); + for (QString script : paths) { + if (!script.isEmpty()) { + this->customScripts.append(script); + } + } } else { logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); } @@ -391,6 +400,7 @@ QMap ProjectConfig::getKeyValueMap() { map.insert("use_encounter_json", QString::number(this->useEncounterJson)); map.insert("use_poryscript", QString::number(this->usePoryScript)); map.insert("use_custom_border_size", QString::number(this->useCustomBorderSize)); + map.insert("custom_scripts", this->customScripts.join(",")); return map; } @@ -423,12 +433,17 @@ void ProjectConfig::onNewConfigFileCreated() { this->useCustomBorderSize = this->baseGameVersion == BaseGameVersion::pokefirered; this->useEncounterJson = true; this->usePoryScript = false; + this->customScripts.clear(); } void ProjectConfig::setProjectDir(QString projectDir) { this->projectDir = projectDir; } +QString ProjectConfig::getProjectDir() { + return this->projectDir; +} + void ProjectConfig::setBaseGameVersion(BaseGameVersion baseGameVersion) { this->baseGameVersion = baseGameVersion; this->save(); @@ -464,3 +479,12 @@ void ProjectConfig::setUseCustomBorderSize(bool enable) { bool ProjectConfig::getUseCustomBorderSize() { return this->useCustomBorderSize; } + +void ProjectConfig::setCustomScripts(QList scripts) { + this->customScripts = scripts; + this->save(); +} + +QList ProjectConfig::getCustomScripts() { + return this->customScripts; +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f4328f1a..be796043 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -46,7 +46,6 @@ MainWindow::MainWindow(QWidget *parent) : QCoreApplication::setApplicationName("porymap"); QApplication::setApplicationDisplayName("porymap"); QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-2.ico")); - Scripting::init(this); ui->setupUi(this); this->initWindow(); @@ -349,6 +348,10 @@ bool MainWindow::openProject(QString dir) { } if (success) { + for (auto action : this->registeredActions) { + this->ui->menuTools->removeAction(action); + } + Scripting::init(this); Scripting::cb_ProjectOpened(dir); } @@ -395,6 +398,7 @@ void MainWindow::on_action_Open_Project_triggered() } QString dir = getExistingDirectory(recent); if (!dir.isEmpty()) { + Scripting::cb_ProjectClosed(this->editor->project->root); porymapConfig.setRecentProject(dir); if (!openProject(dir)) { this->initWindow(); diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp index f321595e..6dc84151 100644 --- a/src/mainwindow_scriptapi.cpp +++ b/src/mainwindow_scriptapi.cpp @@ -434,7 +434,8 @@ void MainWindow::registerAction(QString functionName, QString actionName, QStrin Scripting::registerAction(functionName, actionName); if (Scripting::numRegisteredActions() == 1) { - this->ui->menuTools->addSection("Custom Actions"); + QAction *section = this->ui->menuTools->addSection("Custom Actions"); + this->registeredActions.append(section); } QAction *action = this->ui->menuTools->addAction(actionName, [actionName](){ Scripting::invokeAction(actionName); @@ -442,6 +443,7 @@ void MainWindow::registerAction(QString functionName, QString actionName, QStrin if (!shortcut.isEmpty()) { action->setShortcut(QKeySequence(shortcut)); } + this->registeredActions.append(action); } void MainWindow::setTimeout(QJSValue callback, int milliseconds) { diff --git a/src/scripting.cpp b/src/scripting.cpp index ecd34d51..2df636f8 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -2,14 +2,19 @@ #include "log.h" QMap callbackFunctions = { - {OnProjectOpened, "on_project_opened"}, - {OnBlockChanged, "on_block_changed"}, - {OnMapOpened, "on_map_opened"}, + {OnProjectOpened, "onProjectOpened"}, + {OnProjectClosed, "onProjectClosed"}, + {OnBlockChanged, "onBlockChanged"}, + {OnMapOpened, "onMapOpened"}, }; Scripting *instance = nullptr; void Scripting::init(MainWindow *mainWindow) { + if (instance) { + instance->engine->setInterrupted(true); + delete instance; + } instance = new Scripting(mainWindow); } @@ -17,7 +22,9 @@ Scripting::Scripting(MainWindow *mainWindow) { this->engine = new QJSEngine(mainWindow); this->engine->installExtensions(QJSEngine::ConsoleExtension); this->engine->globalObject().setProperty("map", this->engine->newQObject(mainWindow)); - this->filepaths.append("D:\\devkitProOld\\msys\\home\\huder\\pretmap\\test_script.js"); + for (QString script : projectConfig.getCustomScripts()) { + this->filepaths.append(script); + } this->loadModules(this->filepaths); } @@ -25,16 +32,21 @@ void Scripting::loadModules(QStringList moduleFiles) { for (QString filepath : moduleFiles) { QJSValue module = this->engine->importModule(filepath); if (module.isError()) { - logError(QString("Failed to load custom script file '%1'\nName: %2\nMessage: %3\nFile: %4\nLine Number: %5\nStack: %6") - .arg(filepath) - .arg(module.property("name").toString()) - .arg(module.property("message").toString()) - .arg(module.property("fileName").toString()) - .arg(module.property("lineNumber").toString()) - .arg(module.property("stack").toString())); - continue; + QString relativePath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath); + module = this->engine->importModule(relativePath); + if (module.isError()) { + logError(QString("Failed to load custom script file '%1'\nName: %2\nMessage: %3\nFile: %4\nLine Number: %5\nStack: %6") + .arg(filepath) + .arg(module.property("name").toString()) + .arg(module.property("message").toString()) + .arg(module.property("fileName").toString()) + .arg(module.property("lineNumber").toString()) + .arg(module.property("stack").toString())); + continue; + } } + logInfo(QString("Successfully loaded custom script file '%1'").arg(filepath)); this->modules.append(module); } } @@ -93,6 +105,15 @@ void Scripting::cb_ProjectOpened(QString projectPath) { instance->invokeCallback(OnProjectOpened, args); } +void Scripting::cb_ProjectClosed(QString projectPath) { + if (!instance) return; + + QJSValueList args { + projectPath, + }; + instance->invokeCallback(OnProjectClosed, args); +} + void Scripting::cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock) { if (!instance) return; From f139365faf68670a299fcc2318fd00ade6c11b31 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Fri, 8 May 2020 11:29:39 -0500 Subject: [PATCH 15/17] Add log() to api --- include/mainwindow.h | 1 + src/mainwindow_scriptapi.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/mainwindow.h b/include/mainwindow.h index 09cc440c..76f8e920 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -99,6 +99,7 @@ public: Q_INVOKABLE void registerAction(QString functionName, QString actionName, QString shortcut = ""); Q_INVOKABLE void setTimeout(QJSValue callback, int milliseconds); void invokeCallback(QJSValue callback); + Q_INVOKABLE void log(QString message); public slots: diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp index 6dc84151..846cdce3 100644 --- a/src/mainwindow_scriptapi.cpp +++ b/src/mainwindow_scriptapi.cpp @@ -462,3 +462,7 @@ void MainWindow::setTimeout(QJSValue callback, int milliseconds) { void MainWindow::invokeCallback(QJSValue callback) { callback.call(); } + +void MainWindow::log(QString message) { + logInfo(message); +} From 8013921d43cd806e699685f30d98c96087983b46 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Fri, 8 May 2020 11:42:36 -0500 Subject: [PATCH 16/17] Remove test script --- test_script.js | 77 -------------------------------------------------- 1 file changed, 77 deletions(-) delete mode 100644 test_script.js diff --git a/test_script.js b/test_script.js deleted file mode 100644 index 1e7913b8..00000000 --- a/test_script.js +++ /dev/null @@ -1,77 +0,0 @@ -const normalTint = [1, 1, 1] -const morningTint = [0.8, 0.7, 0.9]; -const nightTint = [0.6, 0.55, 1.0]; - -function applyTint(palette, tint) { - for (let i = 0; i < palette.length; i++) { - const color = palette[i]; - for (let j = 0; j < tint.length; j++) { - color[j] = Math.floor(color[j] * tint[j]); - } - } -} - -function applyTintToPalettes(tint) { - try { - const primaryPalettes = map.getPrimaryTilesetPalettes() - for (let i = 0; i < primaryPalettes.length; i++) - applyTint(primaryPalettes[i], tint) - - const secondaryPalettes = map.getSecondaryTilesetPalettes() - for (let i = 0; i < secondaryPalettes.length; i++) - applyTint(secondaryPalettes[i], tint) - - map.setPrimaryTilesetPalettesPreview(primaryPalettes) - map.setSecondaryTilesetPalettesPreview(secondaryPalettes) - } catch(err) { - console.log(err) - } -} - -export function resetTint() { - applyTintToPalettes(normalTint) -} - -export function applyMorningTint() { - applyTintToPalettes(morningTint) -} - -export function applyNightTint() { - applyTintToPalettes(nightTint) -} - -let curTint = 0 - -function nextTint() { - map.clearOverlay() - map.addRect(curTint * 40, 40, 40, 40, "#FFFF0000") - curTint = (curTint + 1) % 4 - map.setTimeout(nextTint, 100) -} - -export function animated() { - try { - map.setTimeout(nextTint, 1000) - } catch(err) { - console.log(err) - } -} - -let registered = false - -// Porymap callback when project is opened. -export function on_project_opened(projectPath) { - if (registered) { - return; - } - - try { - // map.registerAction("resetTint", "View Day Tint", "K") - // map.registerAction("applyMorningTint", "View Morning Tint", "N") - // map.registerAction("applyNightTint", "View Night Tint", "Ctrl+J") - map.registerAction("animated", "Animate") - registered = true; - } catch(err) { - console.log(err) - } -} From 21613121222974d8bd1e4e74905fd964f6762a10 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sat, 9 May 2020 11:31:14 -0500 Subject: [PATCH 17/17] Add optional args to dictate map refresh and commit --- include/mainwindow.h | 24 ++++++++------ src/mainwindow_scriptapi.cpp | 61 ++++++++++++++++++++++++++++++------ 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/include/mainwindow.h b/include/mainwindow.h index 76f8e920..c4490965 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -40,19 +40,23 @@ public: // Scripting API 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); + void tryRedrawMapArea(bool forceRedraw); + void tryCommitMapChanges(bool commitChanges); + Q_INVOKABLE void setBlock(int x, int y, int tile, int collision, int elevation, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE void setBlocksFromSelection(int x, int y, bool forceRedraw = true, bool commitChanges = true); Q_INVOKABLE int getMetatileId(int x, int y); - Q_INVOKABLE void setMetatileId(int x, int y, int metatileId); + Q_INVOKABLE void setMetatileId(int x, int y, int metatileId, bool forceRedraw = true, bool commitChanges = true); Q_INVOKABLE int getCollision(int x, int y); - Q_INVOKABLE void setCollision(int x, int y, int collision); + Q_INVOKABLE void setCollision(int x, int y, int collision, bool forceRedraw = true, bool commitChanges = true); 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 void setElevation(int x, int y, int elevation, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE void bucketFill(int x, int y, int metatileId, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE void bucketFillFromSelection(int x, int y, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE void magicFill(int x, int y, int metatileId, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE void magicFillFromSelection(int x, int y, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE void shift(int xDelta, int yDelta, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE void redraw(); + Q_INVOKABLE void commit(); Q_INVOKABLE QJSValue getDimensions(); Q_INVOKABLE int getWidth(); Q_INVOKABLE int getHeight(); diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp index 846cdce3..7f6cc24c 100644 --- a/src/mainwindow_scriptapi.cpp +++ b/src/mainwindow_scriptapi.cpp @@ -12,15 +12,32 @@ QJSValue MainWindow::getBlock(int x, int y) { return Scripting::fromBlock(*block); } -void MainWindow::setBlock(int x, int y, int tile, int collision, int elevation) { +void MainWindow::tryRedrawMapArea(bool forceRedraw) { + if (forceRedraw) { + this->editor->map_item->draw(); + this->editor->collision_item->draw(); + } +} + +void MainWindow::tryCommitMapChanges(bool commitChanges) { + if (commitChanges) { + this->editor->map->commit(); + } +} + +void MainWindow::setBlock(int x, int y, int tile, int collision, int elevation, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; this->editor->map->setBlock(x, y, Block(tile, collision, elevation)); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } -void MainWindow::setBlocksFromSelection(int x, int y) { +void MainWindow::setBlocksFromSelection(int x, int y, bool forceRedraw, bool commitChanges) { if (this->editor && this->editor->map_item) { this->editor->map_item->paintNormal(x, y, true); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } } @@ -34,7 +51,7 @@ int MainWindow::getMetatileId(int x, int y) { return block->tile; } -void MainWindow::setMetatileId(int x, int y, int metatileId) { +void MainWindow::setMetatileId(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; Block *block = this->editor->map->getBlock(x, y); @@ -42,6 +59,8 @@ void MainWindow::setMetatileId(int x, int y, int metatileId) { return; } this->editor->map->setBlock(x, y, Block(metatileId, block->collision, block->elevation)); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } int MainWindow::getCollision(int x, int y) { @@ -54,7 +73,7 @@ int MainWindow::getCollision(int x, int y) { return block->collision; } -void MainWindow::setCollision(int x, int y, int collision) { +void MainWindow::setCollision(int x, int y, int collision, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; Block *block = this->editor->map->getBlock(x, y); @@ -62,6 +81,8 @@ void MainWindow::setCollision(int x, int y, int collision) { return; } this->editor->map->setBlock(x, y, Block(block->tile, collision, block->elevation)); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } int MainWindow::getElevation(int x, int y) { @@ -74,7 +95,7 @@ int MainWindow::getElevation(int x, int y) { return block->elevation; } -void MainWindow::setElevation(int x, int y, int elevation) { +void MainWindow::setElevation(int x, int y, int elevation, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; Block *block = this->editor->map->getBlock(x, y); @@ -82,36 +103,56 @@ void MainWindow::setElevation(int x, int y, int elevation) { return; } this->editor->map->setBlock(x, y, Block(block->tile, block->collision, elevation)); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } -void MainWindow::bucketFill(int x, int y, int metatileId) { +void MainWindow::bucketFill(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; this->editor->map_item->floodFill(x, y, metatileId, true); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } -void MainWindow::bucketFillFromSelection(int x, int y) { +void MainWindow::bucketFillFromSelection(int x, int y, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; this->editor->map_item->floodFill(x, y, true); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } -void MainWindow::magicFill(int x, int y, int metatileId) { +void MainWindow::magicFill(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; this->editor->map_item->magicFill(x, y, metatileId, true); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } -void MainWindow::magicFillFromSelection(int x, int y) { +void MainWindow::magicFillFromSelection(int x, int y, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; this->editor->map_item->magicFill(x, y, true); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); } -void MainWindow::shift(int xDelta, int yDelta) { +void MainWindow::shift(int xDelta, int yDelta, bool forceRedraw, bool commitChanges) { if (!this->editor || !this->editor->map) return; this->editor->map_item->shift(xDelta, yDelta); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); +} + +void MainWindow::redraw() { + this->tryRedrawMapArea(true); +} + +void MainWindow::commit() { + this->tryCommitMapChanges(true); } QJSValue MainWindow::getDimensions() {