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:
|
public:
|
||||||
Block();
|
Block();
|
||||||
Block(uint16_t);
|
Block(uint16_t);
|
||||||
|
Block(uint16_t tile, uint16_t collision, uint16_t elevation);
|
||||||
Block(const Block&);
|
Block(const Block&);
|
||||||
bool operator ==(Block);
|
bool operator ==(Block);
|
||||||
bool operator !=(Block);
|
bool operator !=(Block);
|
||||||
|
|
|
@ -74,8 +74,7 @@ public:
|
||||||
void cacheBlockdata();
|
void cacheBlockdata();
|
||||||
void cacheCollision();
|
void cacheCollision();
|
||||||
Block *getBlock(int x, int y);
|
Block *getBlock(int x, int y);
|
||||||
void setBlock(int x, int y, Block block);
|
void setBlock(int x, int y, Block block, bool invokeCallback = false);
|
||||||
void _setBlock(int x, int y, Block block);
|
|
||||||
void floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation);
|
void floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation);
|
||||||
void _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);
|
void magicFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation);
|
||||||
|
|
|
@ -37,6 +37,8 @@ public:
|
||||||
MainWindow(const MainWindow &) = delete;
|
MainWindow(const MainWindow &) = delete;
|
||||||
MainWindow & operator = (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:
|
public slots:
|
||||||
void scaleMapView(int);
|
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
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ SOURCES += src/core/block.cpp \
|
||||||
src/main.cpp \
|
src/main.cpp \
|
||||||
src/mainwindow.cpp \
|
src/mainwindow.cpp \
|
||||||
src/project.cpp \
|
src/project.cpp \
|
||||||
|
src/scripting.cpp \
|
||||||
src/settings.cpp \
|
src/settings.cpp \
|
||||||
src/log.cpp \
|
src/log.cpp \
|
||||||
src/ui/newtilesetdialog.cpp
|
src/ui/newtilesetdialog.cpp
|
||||||
|
@ -132,6 +133,7 @@ HEADERS += include/core/block.h \
|
||||||
include/editor.h \
|
include/editor.h \
|
||||||
include/mainwindow.h \
|
include/mainwindow.h \
|
||||||
include/project.h \
|
include/project.h \
|
||||||
|
include/scripting.h \
|
||||||
include/settings.h \
|
include/settings.h \
|
||||||
include/log.h \
|
include/log.h \
|
||||||
include/ui/newtilesetdialog.h
|
include/ui/newtilesetdialog.h
|
||||||
|
|
|
@ -3,6 +3,12 @@
|
||||||
Block::Block() : tile(0), collision(0), elevation(0) {
|
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)
|
Block::Block(uint16_t word)
|
||||||
{
|
{
|
||||||
tile = word & 0x3ff;
|
tile = word & 0x3ff;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "historyitem.h"
|
#include "historyitem.h"
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
#include "imageproviders.h"
|
#include "imageproviders.h"
|
||||||
|
#include "scripting.h"
|
||||||
|
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
@ -363,10 +364,14 @@ Block* Map::getBlock(int x, int y) {
|
||||||
return nullptr;
|
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;
|
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);
|
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->collision = collision;
|
||||||
block->elevation = elevation;
|
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) {
|
if ((block = getBlock(x + 1, y)) && block->collision == old_coll && block->elevation == old_elev) {
|
||||||
todo.append(QPoint(x + 1, y));
|
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) {
|
void Map::floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) {
|
||||||
Block *block = getBlock(x, y);
|
Block *block = getBlock(x, y);
|
||||||
if (block && (block->collision != collision || block->elevation != elevation)) {
|
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) {
|
if (block && block->collision == old_coll && block->elevation == old_elev) {
|
||||||
block->collision = collision;
|
block->collision = collision;
|
||||||
block->elevation = elevation;
|
block->elevation = elevation;
|
||||||
_setBlock(x, y, *block);
|
setBlock(x, y, *block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include "ui_eventpropertiesframe.h"
|
#include "ui_eventpropertiesframe.h"
|
||||||
#include "bordermetatilespixmapitem.h"
|
#include "bordermetatilespixmapitem.h"
|
||||||
#include "currentselectedmetatilespixmapitem.h"
|
#include "currentselectedmetatilespixmapitem.h"
|
||||||
|
#include "customattributestable.h"
|
||||||
|
#include "scripting.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
|
@ -44,6 +46,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||||
QCoreApplication::setApplicationName("porymap");
|
QCoreApplication::setApplicationName("porymap");
|
||||||
QApplication::setApplicationDisplayName("porymap");
|
QApplication::setApplicationDisplayName("porymap");
|
||||||
QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-2.ico"));
|
QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-2.ico"));
|
||||||
|
Scripting::init(this);
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
this->initWindow();
|
this->initWindow();
|
||||||
|
@ -2540,3 +2543,7 @@ void MainWindow::closeEvent(QCloseEvent *event) {
|
||||||
|
|
||||||
QMainWindow::closeEvent(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) {
|
if (block) {
|
||||||
block->collision = this->movementPermissionsSelector->getSelectedCollision();
|
block->collision = this->movementPermissionsSelector->getSelectedCollision();
|
||||||
block->elevation = this->movementPermissionsSelector->getSelectedElevation();
|
block->elevation = this->movementPermissionsSelector->getSelectedElevation();
|
||||||
map->_setBlock(x, y, *block);
|
map->setBlock(x, y, *block);
|
||||||
}
|
}
|
||||||
if (event->type() == QEvent::GraphicsSceneMouseRelease) {
|
if (event->type() == QEvent::GraphicsSceneMouseRelease) {
|
||||||
map->commit();
|
map->commit();
|
||||||
|
|
|
@ -56,7 +56,7 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) {
|
||||||
|
|
||||||
int blockIndex = j * map->getWidth() + i;
|
int blockIndex = j * map->getWidth() + i;
|
||||||
Block srcBlock = backupBlockdata->blocks->at(blockIndex);
|
Block srcBlock = backupBlockdata->blocks->at(blockIndex);
|
||||||
map->_setBlock(destX, destY, srcBlock);
|
map->setBlock(destX, destY, srcBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete backupBlockdata;
|
delete backupBlockdata;
|
||||||
|
@ -96,7 +96,7 @@ void MapPixmapItem::paintNormal(int x, int y) {
|
||||||
block->collision = selectedCollisions->at(index).first;
|
block->collision = selectedCollisions->at(index).first;
|
||||||
block->elevation = selectedCollisions->at(index).second;
|
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->collision = openTileCollision;
|
||||||
block->elevation = openTileElevation;
|
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->collision = selectedCollisions->at(smartPathTable[id]).first;
|
||||||
block->elevation = selectedCollisions->at(smartPathTable[id]).second;
|
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->collision = selectedCollisions->at(index).first;
|
||||||
block->elevation = selectedCollisions->at(index).second;
|
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->collision = selectedCollisions->at(index).first;
|
||||||
block->elevation = selectedCollisions->at(index).second;
|
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) {
|
if (!visited[x + 1 + y * map->getWidth()] && (block = map->getBlock(x + 1, y)) && block->tile == old_tile) {
|
||||||
todo.append(QPoint(x + 1, y));
|
todo.append(QPoint(x + 1, y));
|
||||||
|
@ -427,7 +427,7 @@ void MapPixmapItem::_floodFillSmartPath(int initialX, int initialY) {
|
||||||
block->collision = openTileCollision;
|
block->collision = openTileCollision;
|
||||||
block->elevation = openTileElevation;
|
block->elevation = openTileElevation;
|
||||||
}
|
}
|
||||||
map->_setBlock(x, y, *block);
|
map->setBlock(x, y, *block);
|
||||||
if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) {
|
if ((block = map->getBlock(x + 1, y)) && block->tile == old_tile) {
|
||||||
todo.append(QPoint(x + 1, y));
|
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->collision = selectedCollisions->at(smartPathTable[id]).first;
|
||||||
block->elevation = selectedCollisions->at(smartPathTable[id]).second;
|
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.
|
// 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)) {
|
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