Implement proof of concept for scripting capabilities
This commit is contained in:
parent
37ab54019b
commit
267cd5e2cb
12 changed files with 154 additions and 25 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
31
include/scripting.h
Normal file
31
include/scripting.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef SCRIPTING_H
|
||||
#define SCRIPTING_H
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "block.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QJSEngine>
|
||||
|
||||
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<QJSValue> modules;
|
||||
|
||||
void loadModules(QStringList moduleFiles);
|
||||
void invokeCallback(CallbackType type, QJSValueList args);
|
||||
};
|
||||
|
||||
#endif // SCRIPTING_H
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "historyitem.h"
|
||||
#include "map.h"
|
||||
#include "imageproviders.h"
|
||||
#include "scripting.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QPainter>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "ui_eventpropertiesframe.h"
|
||||
#include "bordermetatilespixmapitem.h"
|
||||
#include "currentselectedmetatilespixmapitem.h"
|
||||
#include "customattributestable.h"
|
||||
#include "scripting.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QDirIterator>
|
||||
|
@ -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));
|
||||
}
|
||||
|
|
69
src/scripting.cpp
Normal file
69
src/scripting.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "scripting.h"
|
||||
#include "log.h"
|
||||
|
||||
QMap<CallbackType, QString> 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;
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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)) {
|
||||
|
|
15
test_script.js
Normal file
15
test_script.js
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue