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/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..a679a55b 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 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); @@ -86,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/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/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 1e5a65be..c4490965 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,6 +38,74 @@ public: MainWindow(const MainWindow &) = delete; MainWindow & operator = (const MainWindow &) = delete; + // Scripting API + Q_INVOKABLE QJSValue getBlock(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, bool forceRedraw = true, bool commitChanges = true); + Q_INVOKABLE int getCollision(int x, int y); + 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, 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(); + 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); + 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); + 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 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, QString shortcut = ""); + Q_INVOKABLE void setTimeout(QJSValue callback, int milliseconds); + void invokeCallback(QJSValue callback); + Q_INVOKABLE void log(QString message); + + public slots: void scaleMapView(int); @@ -189,6 +258,8 @@ private: DraggablePixmapItem *selectedBG; DraggablePixmapItem *selectedHealspot; + QList registeredActions; + bool isProgrammaticEventTabChange; bool projectHasUnsavedChanges; @@ -205,6 +276,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/project.h b/include/project.h index e6daffb0..45b73c4e 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 new file mode 100644 index 00000000..99daac89 --- /dev/null +++ b/include/scripting.h @@ -0,0 +1,43 @@ +#ifndef SCRIPTING_H +#define SCRIPTING_H + +#include "mainwindow.h" +#include "block.h" + +#include +#include + +enum CallbackType { + OnProjectOpened, + OnProjectClosed, + OnBlockChanged, + OnMapOpened, +}; + +class Scripting +{ +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 registerAction(QString functionName, QString actionName); + 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); + +private: + QJSEngine *engine; + QStringList filepaths; + QList modules; + QMap registeredActions; + + void loadModules(QStringList moduleFiles); + void invokeCallback(CallbackType type, QJSValueList args); +}; + +#endif // SCRIPTING_H 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/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/include/ui/mappixmapitem.h b/include/ui/mappixmapitem.h index e0111eb0..4ee10e56 100644 --- a/include/ui/mappixmapitem.h +++ b/include/ui/mappixmapitem.h @@ -38,17 +38,34 @@ 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, bool fromScriptCall = false); 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 fromScriptCall = false); private: - void paintNormal(int x, int y); - void paintSmartPath(int x, int y); + void paintSmartPath(int x, int y, bool fromScriptCall = false); static QList smartPathTable; signals: 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 b53d94ae..0f600695 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 @@ -31,11 +31,13 @@ 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 \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ + src/ui/overlay.cpp \ src/ui/regionmaplayoutpixmapitem.cpp \ src/ui/regionmapentriespixmapitem.cpp \ src/ui/cursortilerect.cpp \ @@ -70,6 +72,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 +97,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 \ @@ -133,9 +136,11 @@ 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 + include/ui/newtilesetdialog.h \ + include/ui/overlay.h FORMS += forms/mainwindow.ui \ forms/eventpropertiesframe.ui \ 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/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..2e777190 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 @@ -220,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(); @@ -238,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; } @@ -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 enableScriptCallback) { 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 (enableScriptCallback) { + 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, true); 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, true); } } } 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/editor.cpp b/src/editor.cpp index 985b2892..dcb32ee6 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1444,6 +1444,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 0adc83ab..7299ee13 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 "adjustingstackedwidget.h" #include @@ -346,6 +348,14 @@ bool MainWindow::openProject(QString dir) { } + if (success) { + for (auto action : this->registeredActions) { + this->ui->menuTools->removeAction(action); + } + Scripting::init(this); + Scripting::cb_ProjectOpened(dir); + } + return success; } @@ -389,6 +399,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(); @@ -444,6 +455,8 @@ bool MainWindow::setMap(QString map_name, bool scrollTreeView) { setRecentMap(map_name); updateMapList(); updateTilesetEditor(); + + Scripting::cb_MapOpened(map_name); return true; } @@ -1052,6 +1065,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(); @@ -1059,17 +1073,19 @@ 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); editor->project->saveTilesetMetatiles(newSet); editor->project->saveTilesetMetatileAttributes(newSet); - editor->project->saveTilesetPalettes(newSet, !createTilesetDialog->isSecondary); + editor->project->saveTilesetPalettes(newSet); //append to tileset specific files @@ -2303,7 +2319,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" @@ -2328,6 +2344,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; diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp new file mode 100644 index 00000000..7f6cc24c --- /dev/null +++ b/src/mainwindow_scriptapi.cpp @@ -0,0 +1,509 @@ +#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::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, 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); + } +} + +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, bool forceRedraw, bool commitChanges) { + 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)); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); +} + +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, bool forceRedraw, bool commitChanges) { + 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)); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); +} + +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, bool forceRedraw, bool commitChanges) { + 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)); + this->tryCommitMapChanges(commitChanges); + this->tryRedrawMapArea(forceRedraw); +} + +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, 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, 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, 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, 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() { + 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) { + QAction *section = this->ui->menuTools->addSection("Custom Actions"); + this->registeredActions.append(section); + } + QAction *action = this->ui->menuTools->addAction(actionName, [actionName](){ + Scripting::invokeAction(actionName); + }); + if (!shortcut.isEmpty()) { + action->setShortcut(QKeySequence(shortcut)); + } + this->registeredActions.append(action); +} + +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(); +} + +void MainWindow::log(QString message) { + logInfo(message); +} diff --git a/src/project.cpp b/src/project.cpp index 7fc42117..8f229863 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1013,8 +1013,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) { @@ -1151,7 +1151,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); @@ -1586,6 +1586,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); @@ -1620,8 +1621,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/scripting.cpp b/src/scripting.cpp new file mode 100644 index 00000000..2df636f8 --- /dev/null +++ b/src/scripting.cpp @@ -0,0 +1,156 @@ +#include "scripting.h" +#include "log.h" + +QMap callbackFunctions = { + {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); +} + +Scripting::Scripting(MainWindow *mainWindow) { + this->engine = new QJSEngine(mainWindow); + this->engine->installExtensions(QJSEngine::ConsoleExtension); + this->engine->globalObject().setProperty("map", this->engine->newQObject(mainWindow)); + for (QString script : projectConfig.getCustomScripts()) { + this->filepaths.append(script); + } + this->loadModules(this->filepaths); +} + +void Scripting::loadModules(QStringList moduleFiles) { + for (QString filepath : moduleFiles) { + QJSValue module = this->engine->importModule(filepath); + if (module.isError()) { + 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); + } +} + +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::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_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; + + QJSValueList args { + x, + y, + instance->fromBlock(prevBlock), + instance->fromBlock(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("metatileId", block.tile); + obj.setProperty("collision", block.collision); + obj.setProperty("elevation", block.elevation); + 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; +} + +QJSEngine *Scripting::getEngine() { + return instance->engine; +} diff --git a/src/ui/collisionpixmapitem.cpp b/src/ui/collisionpixmapitem.cpp index 94589a8d..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/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/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/mappixmapitem.cpp b/src/ui/mappixmapitem.cpp index cf67e8c9..d6f4b2c4 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,20 +52,43 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::paintNormal(int x, int y) { +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 = 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. - 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 +102,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, !fromScriptCall); } } } @@ -125,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(); @@ -159,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); } } @@ -203,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); } } @@ -269,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); } } @@ -287,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(); @@ -362,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)); @@ -385,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, bool fromScriptCall) { QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QList> *selectedCollisions = this->metatileSelector->getSelectedCollisions(); @@ -427,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)); } @@ -482,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/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/overlay.cpp b/src/ui/overlay.cpp new file mode 100644 index 00000000..004a1fed --- /dev/null +++ b/src/ui/overlay.cpp @@ -0,0 +1,45 @@ +#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/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; }