diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst index 0651f7d9..4e016f6d 100644 --- a/docsrc/manual/scripting-capabilities.rst +++ b/docsrc/manual/scripting-capabilities.rst @@ -1222,6 +1222,58 @@ All tileset functions are callable via the global ``map`` object. :returns: the pixel data :rtype: array +.. |describe-metatile-layer-order| + replace:: where ``0`` is the bottom layer, ``1`` is the middle layer, and ``2`` is the top layer. The default order is ``[0, 1, 2]`` + +.. |describe-metatile-layer-order-handling| + replace:: If no elements are provided the layer order will be reset to the default. Any layer not listed in the provided ``order`` will not be rendered. Any additional elements after the first 3 are ignored + +.. js:function:: map.getMetatileLayerOrder() + + Gets the order that metatile layers are rendered for the current layout, |describe-metatile-layer-order|. + + If you'd like to get the default metatile layer order for all layouts, see :js:func:`utility.getMetatileLayerOrder` instead. + + :returns: array of layers + :rtype: array + +.. js:function:: map.setMetatileLayerOrder(order) + + Sets the order that metatile layers are rendered for the current layout, |describe-metatile-layer-order|. + + |describe-metatile-layer-order-handling|. + + If you'd like to set the default metatile layer order for all layouts, see :js:func:`utility.setMetatileLayerOrder` instead. + + :param order: array of layers + :type order: array + +.. |describe-metatile-layer-opacity| + replace:: where the first element is the bottom layer, the second element is the middle layer, and the third element is the top layer. The default opacities are ``[1.0, 1.0, 1.0]`` + +.. |describe-metatile-layer-opacity-handling| + replace:: Any additional elements after the first 3 are ignored. Any elements not provided will be rendered with opacity ``1.0`` + +.. js:function:: map.getMetatileLayerOpacity() + + Gets the opacities that metatile layers are rendered with for the current layout, |describe-metatile-layer-opacity|. + + If you'd like to get the default metatile layer opacities for all layouts, see :js:func:`utility.getMetatileLayerOpacity` instead. + + :returns: array of opacities for each layer + :rtype: array + +.. js:function:: map.setMetatileLayerOpacity(opacities) + + Sets the opacities that metatile layers are rendered with for the current layout, |describe-metatile-layer-opacity|. + + |describe-metatile-layer-opacity-handling|. + + If you'd like to set the default metatile layer opacities for all layouts, see :js:func:`utility.setMetatileLayerOpacity` instead. + + :param opacities: array of opacities for each layer + :type opacities: array + Overlay Functions ^^^^^^^^^^^^^^^^^ @@ -1811,30 +1863,42 @@ All settings functions are callable via the global ``utility`` object. .. js:function:: utility.getMetatileLayerOrder() - Gets the order that metatile layers are rendered. + Gets the order that metatile layers are rendered by default, |describe-metatile-layer-order|. - :returns: array of layers. The bottom layer is represented as 0. + If you'd like to get the metatile layer order for only the current layout, see :js:func:`map.getMetatileLayerOrder` instead. + + :returns: array of layers :rtype: array .. js:function:: utility.setMetatileLayerOrder(order) - Sets the order that metatile layers are rendered. + Sets the order that metatile layers are rendered by default, |describe-metatile-layer-order|. - :param order: array of layers. The bottom layer is represented as 0. + |describe-metatile-layer-order-handling|. + + If you'd like to set the metatile layer order for only the current layout, see :js:func:`map.setMetatileLayerOrder` instead. + + :param order: array of layers :type order: array .. js:function:: utility.getMetatileLayerOpacity() - Gets the opacities that metatile layers are rendered with. + Gets the opacities that metatile layers are rendered with by default, |describe-metatile-layer-opacity|. - :returns: array of opacities for each layer. The bottom layer is the first element. + If you'd like to get the metatile layer opacities for only the current layout, see :js:func:`map.getMetatileLayerOpacity` instead. + + :returns: array of opacities for each layer :rtype: array .. js:function:: utility.setMetatileLayerOpacity(opacities) - Sets the opacities that metatile layers are rendered with. + Sets the opacities that metatile layers are rendered with by default, |describe-metatile-layer-opacity|. - :param opacities: array of opacities for each layer. The bottom layer is the first element. + |describe-metatile-layer-opacity-handling|. + + If you'd like to set the metatile layer opacities for only the current layout, see :js:func:`map.setMetatileLayerOpacity` instead. + + :param opacities: array of opacities for each layer :type opacities: array diff --git a/include/core/map.h b/include/core/map.h index d5e28075..f5cc1ef1 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -133,9 +133,6 @@ private: QMap> m_events; QSet m_ownedEvents; // for memory management - QList m_metatileLayerOrder; - QList m_metatileLayerOpacity; - void trackConnection(MapConnection*); // MapConnections in 'ownedConnections' but not 'connections' persist in the edit history. diff --git a/include/core/maplayout.h b/include/core/maplayout.h index c7790ad3..280f3006 100644 --- a/include/core/maplayout.h +++ b/include/core/maplayout.h @@ -17,7 +17,7 @@ class BorderMetatilesPixmapItem; class Layout : public QObject { Q_OBJECT public: - Layout() {} + Layout() {}; Layout(const Layout &other); static QString layoutConstantFromName(const QString &name); @@ -64,8 +64,15 @@ public: QSize borderDimensions; } lastCommitBlocks; // to track map changes - QList metatileLayerOrder; - QList metatileLayerOpacity; + void setMetatileLayerOrder(const QList &layerOrder) { m_metatileLayerOrder = layerOrder; } + QList metatileLayerOrder() const; + static void setDefaultMetatileLayerOrder(const QList &layerOrder) { s_defaultMetatileLayerOrder = layerOrder; } + static QList defaultMetatileLayerOrder(); + + void setMetatileLayerOpacity(const QList &layerOpacity) { m_metatileLayerOpacity = layerOpacity; } + QList metatileLayerOpacity() const; + static void setDefaultMetatileLayerOpacity(const QList &layerOpacity) { s_defaultMetatileLayerOpacity = layerOpacity; } + static QList defaultMetatileLayerOpacity(); LayoutPixmapItem *layoutItem = nullptr; CollisionPixmapItem *collisionItem = nullptr; @@ -147,6 +154,8 @@ public: void setCollisionItem(CollisionPixmapItem *item) { collisionItem = item; } void setBorderItem(BorderMetatilesPixmapItem *item) { borderItem = item; } + bool metatileIsValid(uint16_t metatileId) { return Tileset::metatileIsValid(metatileId, this->tileset_primary, this->tileset_secondary); } + private: void setNewDimensionsBlockdata(int newWidth, int newHeight); void setNewBorderDimensionsBlockdata(int newWidth, int newHeight); @@ -155,6 +164,11 @@ private: static int getBorderDrawDistance(int dimension, qreal minimum); + QList m_metatileLayerOrder; + QList m_metatileLayerOpacity; + static QList s_defaultMetatileLayerOrder; + static QList s_defaultMetatileLayerOpacity; + signals: void dimensionsChanged(const QSize &size); void needsRedrawing(); diff --git a/include/editor.h b/include/editor.h index 414356d1..a4620af6 100644 --- a/include/editor.h +++ b/include/editor.h @@ -284,7 +284,6 @@ signals: void wildMonTableEdited(); void currentMetatilesSelectionChanged(); void mapRulerStatusChanged(const QString &); - void tilesetUpdated(QString); void gridToggled(bool); void editActionSet(EditAction newEditAction); }; diff --git a/include/mainwindow.h b/include/mainwindow.h index 3b3003aa..8441babb 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -145,6 +145,10 @@ public: Q_INVOKABLE void setMetatileTiles(int metatileId, QJSValue tilesObj, int tileStart = 0, int tileEnd = -1, bool forceRedraw = true); Q_INVOKABLE void setMetatileTiles(int metatileId, int tileId, bool xflip, bool yflip, int palette, int tileStart = 0, int tileEnd = -1, bool forceRedraw = true); Q_INVOKABLE QJSValue getTilePixels(int tileId); + Q_INVOKABLE QList getMetatileLayerOrder() const; + Q_INVOKABLE void setMetatileLayerOrder(const QList &order); + Q_INVOKABLE QList getMetatileLayerOpacity() const; + Q_INVOKABLE void setMetatileLayerOpacity(const QList &opacities); Q_INVOKABLE QString getSong(); Q_INVOKABLE void setSong(QString song); Q_INVOKABLE QString getLocation(); diff --git a/include/scripting.h b/include/scripting.h index b85e9e26..aaa2bd02 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -23,7 +23,7 @@ enum CallbackType { OnMapResized, OnBorderResized, OnMapShifted, - OnTilesetUpdated, + OnTilesetsChanged, OnMainTabChanged, OnMapViewTabChanged, OnBorderVisibilityToggled, @@ -51,7 +51,7 @@ public: static void cb_MapResized(int oldWidth, int oldHeight, const QMargins &delta); static void cb_BorderResized(int oldWidth, int oldHeight, int newWidth, int newHeight); static void cb_MapShifted(int xDelta, int yDelta); - static void cb_TilesetUpdated(QString tilesetName); + static void cb_TilesetsChanged(const QString &primaryTilesetName, const QString &secondaryTilesetName); static void cb_MainTabChanged(int oldTab, int newTab); static void cb_MapViewTabChanged(int oldTab, int newTab); static void cb_BorderVisibilityToggled(bool visible); diff --git a/include/scriptutility.h b/include/scriptutility.h index 78272eec..09bfaae6 100644 --- a/include/scriptutility.h +++ b/include/scriptutility.h @@ -38,9 +38,9 @@ public: Q_INVOKABLE bool getSmartPathsEnabled(); Q_INVOKABLE QList getCustomScripts(); Q_INVOKABLE QList getMetatileLayerOrder(); - Q_INVOKABLE void setMetatileLayerOrder(QList order); + Q_INVOKABLE void setMetatileLayerOrder(const QList &order); Q_INVOKABLE QList getMetatileLayerOpacity(); - Q_INVOKABLE void setMetatileLayerOpacity(QList order); + Q_INVOKABLE void setMetatileLayerOpacity(const QList &order); Q_INVOKABLE QList getMapNames(); Q_INVOKABLE QList getMapConstants(); Q_INVOKABLE QList getLayoutNames(); @@ -57,6 +57,8 @@ public: Q_INVOKABLE bool isPrimaryTileset(QString tilesetName); Q_INVOKABLE bool isSecondaryTileset(QString tilesetName); + static bool validateMetatileLayerOrder(const QList &order); + private: void callTimeoutFunction(QJSValue callback); void runMessageBox(QString text, QString informativeText, QString detailedText, QMessageBox::Icon icon); diff --git a/include/ui/imageproviders.h b/include/ui/imageproviders.h index 806ffd5b..d3136580 100644 --- a/include/ui/imageproviders.h +++ b/include/ui/imageproviders.h @@ -6,8 +6,12 @@ #include #include +class Layout; + QImage getCollisionMetatileImage(Block); QImage getCollisionMetatileImage(int, int); +QImage getMetatileImage(uint16_t, Layout*, bool useTruePalettes = false); +QImage getMetatileImage(Metatile*, Layout*, bool useTruePalettes = false); QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList&, const QList&, bool useTruePalettes = false); QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList&, const QList&, bool useTruePalettes = false); QImage getTileImage(uint16_t, Tileset*, Tileset*); diff --git a/include/ui/metatileselector.h b/include/ui/metatileselector.h index 7bb3f131..f5faa1e0 100644 --- a/include/ui/metatileselector.h +++ b/include/ui/metatileselector.h @@ -36,8 +36,6 @@ public: this->prefabSelection = false; this->numMetatilesWide = numMetatilesWide; this->layout = layout; - this->primaryTileset = layout->tileset_primary; - this->secondaryTileset = layout->tileset_secondary; this->selection = MetatileSelection{}; this->cellPos = QPoint(-1, -1); setAcceptHoverEvents(true); @@ -45,10 +43,10 @@ public: QPoint getSelectionDimensions() override; void draw() override; + void refresh(); bool select(uint16_t metatile); void selectFromMap(uint16_t metatileId, uint16_t collision, uint16_t elevation); - void setTilesets(Tileset*, Tileset*); MetatileSelection getMetatileSelection(); void setPrefabSelection(MetatileSelection selection); void setExternalSelection(int, int, QList, QList>); @@ -56,8 +54,9 @@ public: void setLayout(Layout *layout); bool isInternalSelection() const { return (!this->externalSelection && !this->prefabSelection); } - Tileset *primaryTileset; - Tileset *secondaryTileset; + Tileset *primaryTileset() const { return this->layout->tileset_primary; } + Tileset *secondaryTileset() const { return this->layout->tileset_secondary; } + protected: void mousePressEvent(QGraphicsSceneMouseEvent*) override; void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; diff --git a/resources/text/script_template.txt b/resources/text/script_template.txt index bee6e56e..8135a853 100644 --- a/resources/text/script_template.txt +++ b/resources/text/script_template.txt @@ -53,8 +53,8 @@ export function onMapShifted(xDelta, yDelta) { } -// Called when the currently loaded tileset is changed by switching to a new one or by saving changes to it in the Tileset Editor. -export function onTilesetUpdated(tilesetName) { +// Called when a currently loaded tileset is changed by switching to a new one or by saving changes in the Tileset Editor. +export function onTilesetsChanged(primaryTilesetName, secondaryTilesetName) { } diff --git a/src/core/map.cpp b/src/core/map.cpp index 6110a26f..03fee13e 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -30,8 +30,6 @@ Map::Map(const Map &other, QObject *parent) : Map(parent) { *m_header = *other.m_header; m_layout = other.m_layout; m_isPersistedToFile = false; - m_metatileLayerOrder = other.m_metatileLayerOrder; - m_metatileLayerOpacity = other.m_metatileLayerOpacity; // Copy events for (auto i = other.m_events.constBegin(); i != other.m_events.constEnd(); i++) { diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp index 4ddb3b75..a9885042 100644 --- a/src/core/maplayout.cpp +++ b/src/core/maplayout.cpp @@ -380,8 +380,8 @@ QPixmap Layout::render(bool ignoreCache, Layout *fromLayout, QRect bounds) { metatileId, fromLayout ? fromLayout->tileset_primary : this->tileset_primary, fromLayout ? fromLayout->tileset_secondary : this->tileset_secondary, - metatileLayerOrder, - metatileLayerOpacity + metatileLayerOrder(), + metatileLayerOpacity() ); imageCache.insert(metatileId, metatileImage); } @@ -457,7 +457,7 @@ QPixmap Layout::renderBorder(bool ignoreCache) { changed_any = true; Block block = this->border.at(i); uint16_t metatileId = block.metatileId(); - QImage metatile_image = getMetatileImage(metatileId, this->tileset_primary, this->tileset_secondary, metatileLayerOrder, metatileLayerOpacity); + QImage metatile_image = getMetatileImage(metatileId, this); int map_y = width_ ? i / width_ : 0; int map_x = width_ ? i % width_ : 0; painter.drawImage(QPoint(map_x * 16, map_y * 16), metatile_image); @@ -612,3 +612,23 @@ Blockdata Layout::readBlockdata(const QString &path, QString *error) { return blockdata; } + +QList Layout::metatileLayerOrder() const { + return !m_metatileLayerOrder.isEmpty() ? m_metatileLayerOrder : Layout::defaultMetatileLayerOrder(); +} + +QList Layout::s_defaultMetatileLayerOrder; +QList Layout::defaultMetatileLayerOrder() { + static const QList initialDefault = {0, 1, 2}; + return !s_defaultMetatileLayerOrder.isEmpty() ? s_defaultMetatileLayerOrder : initialDefault; +} + +QList Layout::metatileLayerOpacity() const { + return !m_metatileLayerOpacity.isEmpty() ? m_metatileLayerOpacity : Layout::defaultMetatileLayerOpacity(); +} + +QList Layout::s_defaultMetatileLayerOpacity; +QList Layout::defaultMetatileLayerOpacity() { + static const QList initialDefault = {1.0, 1.0, 1.0}; + return !s_defaultMetatileLayerOpacity.isEmpty() ? s_defaultMetatileLayerOpacity : initialDefault; +} diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index cc7efb55..0ae8512f 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -220,14 +220,23 @@ bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tile QList> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) { QList> palettes; - auto primaryPalettes = useTruePalettes ? primaryTileset->palettes : primaryTileset->palettePreviews; + + QList> primaryPalettes; + if (primaryTileset) { + primaryPalettes = useTruePalettes ? primaryTileset->palettes : primaryTileset->palettePreviews; + } for (int i = 0; i < Project::getNumPalettesPrimary(); i++) { - palettes.append(primaryPalettes.at(i)); + palettes.append(primaryPalettes.value(i)); + } + + QList> secondaryPalettes; + if (secondaryTileset) { + secondaryPalettes = useTruePalettes ? secondaryTileset->palettes : secondaryTileset->palettePreviews; } - auto secondaryPalettes = useTruePalettes ? secondaryTileset->palettes : secondaryTileset->palettePreviews; for (int i = Project::getNumPalettesPrimary(); i < Project::getNumPalettesTotal(); i++) { - palettes.append(secondaryPalettes.at(i)); + palettes.append(secondaryPalettes.value(i)); } + return palettes; } diff --git a/src/editor.cpp b/src/editor.cpp index 5ad15a84..bd15f4c5 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1357,12 +1357,13 @@ bool Editor::setLayout(QString layoutId) { map_ruler->setMapDimensions(QSize(this->layout->getWidth(), this->layout->getHeight())); connect(this->layout, &Layout::dimensionsChanged, map_ruler, &MapRuler::setMapDimensions); - ui->comboBox_PrimaryTileset->blockSignals(true); - ui->comboBox_SecondaryTileset->blockSignals(true); + QString prevPrimaryTileset = ui->comboBox_PrimaryTileset->currentText(); + QString prevSecondaryTileset = ui->comboBox_SecondaryTileset->currentText(); + + const QSignalBlocker b_PrimaryTilest(ui->comboBox_PrimaryTileset); + const QSignalBlocker b_SecondaryTilest(ui->comboBox_SecondaryTileset); ui->comboBox_PrimaryTileset->setTextItem(this->layout->tileset_primary_label); ui->comboBox_SecondaryTileset->setTextItem(this->layout->tileset_secondary_label); - ui->comboBox_PrimaryTileset->blockSignals(false); - ui->comboBox_SecondaryTileset->blockSignals(false); const QSignalBlocker b0(this->ui->comboBox_LayoutSelector); int index = this->ui->comboBox_LayoutSelector->findText(layoutId); @@ -1371,6 +1372,8 @@ bool Editor::setLayout(QString layoutId) { if (this->layout->name != prevLayoutName) Scripting::cb_LayoutOpened(this->layout->name); + if (this->layout->tileset_primary_label != prevPrimaryTileset || this->layout->tileset_secondary_label != prevSecondaryTileset) + Scripting::cb_TilesetsChanged(this->layout->tileset_primary_label, this->layout->tileset_secondary_label); return true; } @@ -1651,13 +1654,6 @@ void Editor::displayMetatileSelector() { metatile_selector_item->select(0); } else { metatile_selector_item->setLayout(this->layout); - if (metatile_selector_item->primaryTileset - && metatile_selector_item->primaryTileset != this->layout->tileset_primary) - emit tilesetUpdated(this->layout->tileset_primary->name); - if (metatile_selector_item->secondaryTileset - && metatile_selector_item->secondaryTileset != this->layout->tileset_secondary) - emit tilesetUpdated(this->layout->tileset_secondary->name); - metatile_selector_item->setTilesets(this->layout->tileset_primary, this->layout->tileset_secondary); } scene_metatiles->addItem(metatile_selector_item); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 238d9706..8d44895e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -367,7 +367,6 @@ void MainWindow::initEditor() { connect(this->editor, &Editor::currentMetatilesSelectionChanged, this, &MainWindow::currentMetatilesSelectionChanged); connect(this->editor, &Editor::wildMonTableEdited, [this] { markMapEdited(this->editor->map); }); connect(this->editor, &Editor::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged); - connect(this->editor, &Editor::tilesetUpdated, this, &Scripting::cb_TilesetUpdated); connect(this->editor, &Editor::editActionSet, this, &MainWindow::setEditActionUi); connect(ui->newEventToolButton, &NewEventToolButton::newEventAdded, this->editor, &Editor::addNewEvent); connect(ui->toolButton_deleteEvent, &QAbstractButton::clicked, this->editor, &Editor::deleteSelectedEvents); @@ -2604,20 +2603,20 @@ void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryT bool updated = false; if (primaryTilesetLabel == this->editor->layout->tileset_primary_label) { this->editor->updatePrimaryTileset(primaryTilesetLabel, true); - Scripting::cb_TilesetUpdated(primaryTilesetLabel); updated = true; } else { this->editor->project->getTileset(primaryTilesetLabel, true); } if (secondaryTilesetLabel == this->editor->layout->tileset_secondary_label) { this->editor->updateSecondaryTileset(secondaryTilesetLabel, true); - Scripting::cb_TilesetUpdated(secondaryTilesetLabel); updated = true; } else { this->editor->project->getTileset(secondaryTilesetLabel, true); } - if (updated) + if (updated) { redrawMapScene(); + Scripting::cb_TilesetsChanged(primaryTilesetLabel, secondaryTilesetLabel); + } } void MainWindow::onMapRulerStatusChanged(const QString &status) { diff --git a/src/scriptapi/apimap.cpp b/src/scriptapi/apimap.cpp index 08bfc9c5..c7b6d386 100644 --- a/src/scriptapi/apimap.cpp +++ b/src/scriptapi/apimap.cpp @@ -4,6 +4,7 @@ #include "editcommands.h" #include "config.h" #include "imageproviders.h" +#include "scriptutility.h" // TODO: "tilesetNeedsRedraw" is used when redrawing the map after // changing a metatile's tiles via script. It is unnecessarily @@ -26,7 +27,7 @@ void MainWindow::tryRedrawMapArea(bool forceRedraw) { if (this->tilesetEditor) this->tilesetEditor->updateTilesets(this->editor->layout->tileset_primary_label, this->editor->layout->tileset_secondary_label); if (this->editor->metatile_selector_item) - this->editor->metatile_selector_item->draw(); + this->editor->metatile_selector_item->refresh(); if (this->editor->selected_border_metatiles_item) this->editor->selected_border_metatiles_item->draw(); if (this->editor->current_metatile_selection_item) @@ -332,7 +333,7 @@ void MainWindow::refreshAfterPaletteChange(Tileset *tileset) { if (this->tilesetEditor) { this->tilesetEditor->updateTilesets(this->editor->layout->tileset_primary_label, this->editor->layout->tileset_secondary_label); } - this->editor->metatile_selector_item->draw(); + this->editor->metatile_selector_item->refresh(); this->editor->selected_border_metatiles_item->draw(); this->editor->map_item->draw(true); this->editor->updateMapBorder(); @@ -444,7 +445,7 @@ QJSValue MainWindow::getSecondaryTilesetPalettes() { } void MainWindow::refreshAfterPalettePreviewChange() { - this->editor->metatile_selector_item->draw(); + this->editor->metatile_selector_item->refresh(); this->editor->selected_border_metatiles_item->draw(); this->editor->map_item->draw(true); this->editor->updateMapBorder(); @@ -807,6 +808,32 @@ QJSValue MainWindow::getTilePixels(int tileId) { return pixelArray; } +QList MainWindow::getMetatileLayerOrder() const { + if (!this->editor || !this->editor->layout) + return QList(); + return this->editor->layout->metatileLayerOrder(); +} + +void MainWindow::setMetatileLayerOrder(const QList &order) { + if (!this->editor || !this->editor->layout || !ScriptUtility::validateMetatileLayerOrder(order)) + return; + this->editor->layout->setMetatileLayerOrder(order); + this->refreshAfterPalettePreviewChange(); +} + +QList MainWindow::getMetatileLayerOpacity() const { + if (!this->editor || !this->editor->layout) + return QList(); + return this->editor->layout->metatileLayerOpacity(); +} + +void MainWindow::setMetatileLayerOpacity(const QList &opacities) { + if (!this->editor || !this->editor->layout) + return; + this->editor->layout->setMetatileLayerOpacity(opacities); + this->refreshAfterPalettePreviewChange(); +} + //===================== // Editing map header //===================== diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 4e183659..ffe7743a 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -294,11 +294,7 @@ void MapView::addTileImage(int x, int y, const Tile &tile, bool setTransparency, void MapView::addMetatileImage(int x, int y, int metatileId, bool setTransparency, int layer) { if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary || !this->editor->layout->tileset_secondary) return; - QImage image = getMetatileImage(static_cast(metatileId), - this->editor->layout->tileset_primary, - this->editor->layout->tileset_secondary, - this->editor->layout->metatileLayerOrder, - this->editor->layout->metatileLayerOpacity); + QImage image = getMetatileImage(static_cast(metatileId), this->editor->layout); if (setTransparency) image.setColor(0, qRgba(0, 0, 0, 0)); if (this->getOverlay(layer)->addImage(x, y, image)) diff --git a/src/scriptapi/apiutility.cpp b/src/scriptapi/apiutility.cpp index dfc65617..900c8701 100644 --- a/src/scriptapi/apiutility.cpp +++ b/src/scriptapi/apiutility.cpp @@ -201,46 +201,36 @@ QList ScriptUtility::getCustomScripts() { } QList ScriptUtility::getMetatileLayerOrder() { - if (!window || !window->editor || !window->editor->layout) - return QList(); - return window->editor->layout->metatileLayerOrder; + return Layout::defaultMetatileLayerOrder(); } -void ScriptUtility::setMetatileLayerOrder(QList order) { - if (!window || !window->editor || !window->editor->layout) - return; - +bool ScriptUtility::validateMetatileLayerOrder(const QList &order) { const int numLayers = 3; - int size = order.size(); - if (size < numLayers) { - logError(QString("Metatile layer order has insufficient elements (%1), needs at least %2.").arg(size).arg(numLayers)); - return; - } - bool invalid = false; - for (int i = 0; i < numLayers; i++) { + bool valid = true; + for (int i = 0; i < qMin(order.length(), numLayers); i++) { int layer = order.at(i); if (layer < 0 || layer >= numLayers) { logError(QString("'%1' is not a valid metatile layer order value, must be in range 0-%2.").arg(layer).arg(numLayers - 1)); - invalid = true; + valid = false; } } - if (invalid) return; + return valid; +} - window->editor->layout->metatileLayerOrder = order; - window->refreshAfterPalettePreviewChange(); +void ScriptUtility::setMetatileLayerOrder(const QList &order) { + if (!validateMetatileLayerOrder(order)) + return; + Layout::setDefaultMetatileLayerOrder(order); + if (window) window->refreshAfterPalettePreviewChange(); } QList ScriptUtility::getMetatileLayerOpacity() { - if (!window || !window->editor || !window->editor->layout) - return QList(); - return window->editor->layout->metatileLayerOpacity; + return Layout::defaultMetatileLayerOpacity(); } -void ScriptUtility::setMetatileLayerOpacity(QList order) { - if (!window || !window->editor || !window->editor->layout) - return; - window->editor->layout->metatileLayerOpacity = order; - window->refreshAfterPalettePreviewChange(); +void ScriptUtility::setMetatileLayerOpacity(const QList &opacities) { + Layout::setDefaultMetatileLayerOpacity(opacities); + if (window) window->refreshAfterPalettePreviewChange(); } QList ScriptUtility::getMapNames() { diff --git a/src/scriptapi/scripting.cpp b/src/scriptapi/scripting.cpp index 93823de2..5de63221 100644 --- a/src/scriptapi/scripting.cpp +++ b/src/scriptapi/scripting.cpp @@ -16,7 +16,7 @@ const QMap callbackFunctions = { {OnMapResized, "onMapResized"}, {OnBorderResized, "onBorderResized"}, {OnMapShifted, "onMapShifted"}, - {OnTilesetUpdated, "onTilesetUpdated"}, + {OnTilesetsChanged, "onTilesetsChanged"}, {OnMainTabChanged, "onMainTabChanged"}, {OnMapViewTabChanged, "onMapViewTabChanged"}, {OnBorderVisibilityToggled, "onBorderVisibilityToggled"}, @@ -301,13 +301,14 @@ void Scripting::cb_MapShifted(int xDelta, int yDelta) { instance->invokeCallback(OnMapShifted, args); } -void Scripting::cb_TilesetUpdated(QString tilesetName) { +void Scripting::cb_TilesetsChanged(const QString &primaryTilesetName, const QString &secondaryTilesetName) { if (!instance) return; QJSValueList args { - tilesetName, + primaryTilesetName, + secondaryTilesetName }; - instance->invokeCallback(OnTilesetUpdated, args); + instance->invokeCallback(OnTilesetsChanged, args); } void Scripting::cb_MainTabChanged(int oldTab, int newTab) { diff --git a/src/ui/bordermetatilespixmapitem.cpp b/src/ui/bordermetatilespixmapitem.cpp index e793e466..9d6ab07f 100644 --- a/src/ui/bordermetatilespixmapitem.cpp +++ b/src/ui/bordermetatilespixmapitem.cpp @@ -46,12 +46,7 @@ void BorderMetatilesPixmapItem::draw() { for (int j = 0; j < height; j++) { int x = i * 16; int y = j * 16; - QImage metatile_image = getMetatileImage( - layout->getBorderMetatileId(i, j), - layout->tileset_primary, - layout->tileset_secondary, - layout->metatileLayerOrder, - layout->metatileLayerOpacity); + QImage metatile_image = getMetatileImage(layout->getBorderMetatileId(i, j), layout); QPoint metatile_origin = QPoint(x, y); painter.drawImage(metatile_origin, metatile_image); } diff --git a/src/ui/currentselectedmetatilespixmapitem.cpp b/src/ui/currentselectedmetatilespixmapitem.cpp index e8b16f49..dcc3cb24 100644 --- a/src/ui/currentselectedmetatilespixmapitem.cpp +++ b/src/ui/currentselectedmetatilespixmapitem.cpp @@ -17,12 +17,7 @@ QPixmap drawMetatileSelection(MetatileSelection selection, Layout *layout) { int index = j * selection.dimensions.x() + i; MetatileSelectionItem item = selection.metatileItems.at(index); if (item.enabled) { - QImage metatile_image = getMetatileImage( - item.metatileId, - layout->tileset_primary, - layout->tileset_secondary, - layout->metatileLayerOrder, - layout->metatileLayerOpacity); + QImage metatile_image = getMetatileImage(item.metatileId, layout); painter.drawImage(metatile_origin, metatile_image); } } diff --git a/src/ui/imageproviders.cpp b/src/ui/imageproviders.cpp index 3f7b8fb8..4072baf8 100644 --- a/src/ui/imageproviders.cpp +++ b/src/ui/imageproviders.cpp @@ -13,6 +13,25 @@ QImage getCollisionMetatileImage(int collision, int elevation) { return image ? *image : QImage(); } +QImage getMetatileImage(uint16_t metatileId, Layout *layout, bool useTruePalettes) { + Metatile* metatile = Tileset::getMetatile(metatileId, + layout ? layout->tileset_primary : nullptr, + layout ? layout->tileset_secondary : nullptr); + return getMetatileImage(metatile, layout, useTruePalettes); +} + +QImage getMetatileImage(Metatile *metatile, Layout *layout, bool useTruePalettes) { + if (!layout) { + return getMetatileImage(metatile, nullptr, nullptr, {}, {}, useTruePalettes); + } + return getMetatileImage(metatile, + layout->tileset_primary, + layout->tileset_secondary, + layout->metatileLayerOrder(), + layout->metatileLayerOpacity(), + useTruePalettes); +} + QImage getMetatileImage( uint16_t metatileId, Tileset *primaryTileset, @@ -21,13 +40,12 @@ QImage getMetatileImage( const QList &layerOpacity, bool useTruePalettes) { - Metatile* metatile = Tileset::getMetatile(metatileId, primaryTileset, secondaryTileset); - if (!metatile) { - QImage metatile_image(16, 16, QImage::Format_RGBA8888); - metatile_image.fill(Qt::magenta); - return metatile_image; - } - return getMetatileImage(metatile, primaryTileset, secondaryTileset, layerOrder, layerOpacity, useTruePalettes); + return getMetatileImage(Tileset::getMetatile(metatileId, primaryTileset, secondaryTileset), + primaryTileset, + secondaryTileset, + layerOrder, + layerOpacity, + useTruePalettes); } QImage getMetatileImage( @@ -38,7 +56,9 @@ QImage getMetatileImage( const QList &layerOpacity, bool useTruePalettes) { - QImage metatile_image(16, 16, QImage::Format_RGBA8888); + const int numTilesWide = 2; + const int numTilesTall = 2; + QImage metatile_image(8 * numTilesWide, 8 * numTilesTall, QImage::Format_RGBA8888); if (!metatile) { metatile_image.fill(Qt::magenta); return metatile_image; @@ -54,18 +74,17 @@ QImage getMetatileImage( metatile_image.fill(projectConfig.setTransparentPixelsBlack ? QColor("black") : QColor(palettes.value(0).value(0))); QPainter metatile_painter(&metatile_image); - const int numLayers = 3; // When rendering, metatiles always have 3 layers - uint32_t layerType = metatile->layerType(); - for (int layer = 0; layer < numLayers; layer++) - for (int y = 0; y < 2; y++) - for (int x = 0; x < 2; x++) { - int l = layerOrder.size() >= numLayers ? layerOrder[layer] : layer; + uint32_t layerType = metatile->layerType(); + const int numTilesPerLayer = numTilesWide * numTilesTall; + for (const auto &layer : layerOrder) + for (int y = 0; y < numTilesTall; y++) + for (int x = 0; x < numTilesWide; x++) { // Get the tile to render next Tile tile; - int tileOffset = (y * 2) + x; + int tileOffset = (y * numTilesWide) + x; if (projectConfig.tripleLayerMetatilesEnabled) { - tile = metatile->tiles.value(tileOffset + (l * 4)); + tile = metatile->tiles.value(tileOffset + (layer * numTilesPerLayer)); } else { // "Vanilla" metatiles only have 8 tiles, but render 12. // The remaining 4 tiles are rendered using user-specified tiles depending on layer type. @@ -73,22 +92,22 @@ QImage getMetatileImage( { default: case Metatile::LayerType::Normal: - if (l == 0) + if (layer == 0) tile = Tile(projectConfig.unusedTileNormal); else // Tiles are on layers 1 and 2 - tile = metatile->tiles.value(tileOffset + ((l - 1) * 4)); + tile = metatile->tiles.value(tileOffset + ((layer - 1) * numTilesPerLayer)); break; case Metatile::LayerType::Covered: - if (l == 2) + if (layer == 2) tile = Tile(projectConfig.unusedTileCovered); else // Tiles are on layers 0 and 1 - tile = metatile->tiles.value(tileOffset + (l * 4)); + tile = metatile->tiles.value(tileOffset + (layer * numTilesPerLayer)); break; case Metatile::LayerType::Split: - if (l == 1) + if (layer == 1) tile = Tile(projectConfig.unusedTileSplit); else // Tiles are on layers 0 and 2 - tile = metatile->tiles.value(tileOffset + ((l == 0 ? 0 : 1) * 4)); + tile = metatile->tiles.value(tileOffset + ((layer == 0 ? 0 : 1) * numTilesPerLayer)); break; } } @@ -112,7 +131,7 @@ QImage getMetatileImage( } QPoint origin = QPoint(x*8, y*8); - float opacity = layerOpacity.size() >= numLayers ? layerOpacity[l] : 1.0; + float opacity = layerOpacity.value(layer, 1.0); if (opacity < 1.0) { int alpha = 255 * opacity; for (int c = 0; c < tile_image.colorCount(); c++) { diff --git a/src/ui/metatileselector.cpp b/src/ui/metatileselector.cpp index 828c73cd..321f2925 100644 --- a/src/ui/metatileselector.cpp +++ b/src/ui/metatileselector.cpp @@ -11,12 +11,12 @@ QPoint MetatileSelector::getSelectionDimensions() { int MetatileSelector::numPrimaryMetatilesRounded() const { // We round up the number of primary metatiles to keep the tilesets on separate rows. - return ceil((double)this->primaryTileset->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide; + return ceil((double)this->primaryTileset()->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide; } void MetatileSelector::updateBasePixmap() { int primaryLength = this->numPrimaryMetatilesRounded(); - int length_ = primaryLength + this->secondaryTileset->numMetatiles(); + int length_ = primaryLength + this->secondaryTileset()->numMetatiles(); int height_ = length_ / this->numMetatilesWide; if (length_ % this->numMetatilesWide != 0) { height_++; @@ -25,11 +25,11 @@ void MetatileSelector::updateBasePixmap() { image.fill(Qt::magenta); QPainter painter(&image); for (int i = 0; i < length_; i++) { - int tile = i; + int metatileId = i; if (i >= primaryLength) { - tile += Project::getNumMetatilesPrimary() - primaryLength; + metatileId += Project::getNumMetatilesPrimary() - primaryLength; } - QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset, layout->metatileLayerOrder, layout->metatileLayerOpacity); + QImage metatile_image = getMetatileImage(metatileId, this->layout); int map_y = i / this->numMetatilesWide; int map_x = i % this->numMetatilesWide; QPoint metatile_origin = QPoint(map_x * this->cellWidth, map_y * this->cellHeight); @@ -53,7 +53,7 @@ void MetatileSelector::drawSelection() { } bool MetatileSelector::select(uint16_t metatileId) { - if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) return false; + if (!this->layout->metatileIsValid(metatileId)) return false; this->externalSelection = false; this->prefabSelection = false; this->selection = MetatileSelection{ @@ -73,9 +73,8 @@ void MetatileSelector::selectFromMap(uint16_t metatileId, uint16_t collision, ui this->setExternalSelection(1, 1, {metatileId}, {movePermissions}); } -void MetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { - this->primaryTileset = primaryTileset; - this->secondaryTileset = secondaryTileset; +void MetatileSelector::setLayout(Layout *layout) { + this->layout = layout; if (this->externalSelection) this->updateExternalSelectedMetatiles(); else @@ -85,6 +84,10 @@ void MetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTi draw(); } +void MetatileSelector::refresh() { + setLayout(this->layout); +} + MetatileSelection MetatileSelector::getMetatileSelection() { return selection; } @@ -104,7 +107,7 @@ void MetatileSelector::setExternalSelection(int width, int height, QListselection.collisionItems.append(CollisionSelectionItem{true, collision.first, collision.second}); uint16_t metatileId = metatiles.at(i); this->externalSelectedMetatiles.append(metatileId); - if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) + if (!this->layout->metatileIsValid(metatileId)) metatileId = 0; this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId}); } @@ -127,7 +130,7 @@ void MetatileSelector::setPrefabSelection(MetatileSelection selection) { } bool MetatileSelector::positionIsValid(const QPoint &pos) const { - return Tileset::metatileIsValid(getMetatileId(pos.x(), pos.y()), this->primaryTileset, this->secondaryTileset); + return this->layout->metatileIsValid(getMetatileId(pos.x(), pos.y())); } void MetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { @@ -188,7 +191,7 @@ void MetatileSelector::updateSelectedMetatiles() { for (int j = 0; j < this->selection.dimensions.y(); j++) { for (int i = 0; i < this->selection.dimensions.x(); i++) { uint16_t metatileId = this->getMetatileId(origin.x() + i, origin.y() + j); - if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) + if (!this->layout->metatileIsValid(metatileId)) metatileId = 0; this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId}); } @@ -201,7 +204,7 @@ void MetatileSelector::updateExternalSelectedMetatiles() { this->selection.dimensions = QPoint(this->externalSelectionWidth, this->externalSelectionHeight); for (int i = 0; i < this->externalSelectedMetatiles.count(); ++i) { uint16_t metatileId = this->externalSelectedMetatiles.at(i); - if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) + if (!this->layout->metatileIsValid(metatileId)) metatileId = 0; this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId}); } @@ -219,9 +222,7 @@ uint16_t MetatileSelector::getMetatileId(int x, int y) const { } QPoint MetatileSelector::getMetatileIdCoords(uint16_t metatileId) { - if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) - { - // Invalid metatile id. + if (!this->layout->metatileIsValid(metatileId)) { return QPoint(0, 0); } @@ -237,7 +238,3 @@ QPoint MetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) { pos.ry() = (pos.y() * this->cellHeight) + (this->cellHeight / 2); return pos; } - -void MetatileSelector::setLayout(Layout *layout) { - this->layout = layout; -} diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp index 5026a49b..79888681 100644 --- a/src/ui/tileseteditormetatileselector.cpp +++ b/src/ui/tileseteditormetatileselector.cpp @@ -60,8 +60,8 @@ QImage TilesetEditorMetatileSelector::buildImage(int metatileIdStart, int numMet metatileId, this->primaryTileset, this->secondaryTileset, - this->layout->metatileLayerOrder, - this->layout->metatileLayerOpacity, + this->layout->metatileLayerOrder(), + this->layout->metatileLayerOpacity(), true) .scaled(this->cellWidth, this->cellHeight); int map_y = i / this->numMetatilesWide; @@ -81,8 +81,8 @@ void TilesetEditorMetatileSelector::drawMetatile(uint16_t metatileId) { metatileId, this->primaryTileset, this->secondaryTileset, - this->layout->metatileLayerOrder, - this->layout->metatileLayerOpacity, + this->layout->metatileLayerOrder(), + this->layout->metatileLayerOpacity(), true) .scaled(this->cellWidth, this->cellHeight); painter.drawImage(QPoint(pos.x() * this->cellWidth, pos.y() * this->cellHeight), metatile_image);