Allow rendering individual metatile layers

This commit is contained in:
GriffinR 2025-07-01 17:53:07 -04:00
parent 17e4cbfa30
commit b2798e77d4
24 changed files with 272 additions and 147 deletions

View File

@ -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

View File

@ -133,9 +133,6 @@ private:
QMap<Event::Group, QList<Event *>> m_events;
QSet<Event *> m_ownedEvents; // for memory management
QList<int> m_metatileLayerOrder;
QList<float> m_metatileLayerOpacity;
void trackConnection(MapConnection*);
// MapConnections in 'ownedConnections' but not 'connections' persist in the edit history.

View File

@ -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<int> metatileLayerOrder;
QList<float> metatileLayerOpacity;
void setMetatileLayerOrder(const QList<int> &layerOrder) { m_metatileLayerOrder = layerOrder; }
QList<int> metatileLayerOrder() const;
static void setDefaultMetatileLayerOrder(const QList<int> &layerOrder) { s_defaultMetatileLayerOrder = layerOrder; }
static QList<int> defaultMetatileLayerOrder();
void setMetatileLayerOpacity(const QList<float> &layerOpacity) { m_metatileLayerOpacity = layerOpacity; }
QList<float> metatileLayerOpacity() const;
static void setDefaultMetatileLayerOpacity(const QList<float> &layerOpacity) { s_defaultMetatileLayerOpacity = layerOpacity; }
static QList<float> 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<int> m_metatileLayerOrder;
QList<float> m_metatileLayerOpacity;
static QList<int> s_defaultMetatileLayerOrder;
static QList<float> s_defaultMetatileLayerOpacity;
signals:
void dimensionsChanged(const QSize &size);
void needsRedrawing();

View File

@ -284,7 +284,6 @@ signals:
void wildMonTableEdited();
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
void tilesetUpdated(QString);
void gridToggled(bool);
void editActionSet(EditAction newEditAction);
};

View File

@ -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<int> getMetatileLayerOrder() const;
Q_INVOKABLE void setMetatileLayerOrder(const QList<int> &order);
Q_INVOKABLE QList<float> getMetatileLayerOpacity() const;
Q_INVOKABLE void setMetatileLayerOpacity(const QList<float> &opacities);
Q_INVOKABLE QString getSong();
Q_INVOKABLE void setSong(QString song);
Q_INVOKABLE QString getLocation();

View File

@ -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);

View File

@ -38,9 +38,9 @@ public:
Q_INVOKABLE bool getSmartPathsEnabled();
Q_INVOKABLE QList<QString> getCustomScripts();
Q_INVOKABLE QList<int> getMetatileLayerOrder();
Q_INVOKABLE void setMetatileLayerOrder(QList<int> order);
Q_INVOKABLE void setMetatileLayerOrder(const QList<int> &order);
Q_INVOKABLE QList<float> getMetatileLayerOpacity();
Q_INVOKABLE void setMetatileLayerOpacity(QList<float> order);
Q_INVOKABLE void setMetatileLayerOpacity(const QList<float> &order);
Q_INVOKABLE QList<QString> getMapNames();
Q_INVOKABLE QList<QString> getMapConstants();
Q_INVOKABLE QList<QString> getLayoutNames();
@ -57,6 +57,8 @@ public:
Q_INVOKABLE bool isPrimaryTileset(QString tilesetName);
Q_INVOKABLE bool isSecondaryTileset(QString tilesetName);
static bool validateMetatileLayerOrder(const QList<int> &order);
private:
void callTimeoutFunction(QJSValue callback);
void runMessageBox(QString text, QString informativeText, QString detailedText, QMessageBox::Icon icon);

View File

@ -6,8 +6,12 @@
#include <QImage>
#include <QPixmap>
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<int>&, const QList<float>&, bool useTruePalettes = false);
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList<int>&, const QList<float>&, bool useTruePalettes = false);
QImage getTileImage(uint16_t, Tileset*, Tileset*);

View File

@ -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<uint16_t>, QList<QPair<uint16_t, uint16_t>>);
@ -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;

View File

@ -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) {
}

View File

@ -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++) {

View File

@ -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<int> Layout::metatileLayerOrder() const {
return !m_metatileLayerOrder.isEmpty() ? m_metatileLayerOrder : Layout::defaultMetatileLayerOrder();
}
QList<int> Layout::s_defaultMetatileLayerOrder;
QList<int> Layout::defaultMetatileLayerOrder() {
static const QList<int> initialDefault = {0, 1, 2};
return !s_defaultMetatileLayerOrder.isEmpty() ? s_defaultMetatileLayerOrder : initialDefault;
}
QList<float> Layout::metatileLayerOpacity() const {
return !m_metatileLayerOpacity.isEmpty() ? m_metatileLayerOpacity : Layout::defaultMetatileLayerOpacity();
}
QList<float> Layout::s_defaultMetatileLayerOpacity;
QList<float> Layout::defaultMetatileLayerOpacity() {
static const QList<float> initialDefault = {1.0, 1.0, 1.0};
return !s_defaultMetatileLayerOpacity.isEmpty() ? s_defaultMetatileLayerOpacity : initialDefault;
}

View File

@ -220,14 +220,23 @@ bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tile
QList<QList<QRgb>> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) {
QList<QList<QRgb>> palettes;
auto primaryPalettes = useTruePalettes ? primaryTileset->palettes : primaryTileset->palettePreviews;
QList<QList<QRgb>> 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<QList<QRgb>> 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;
}

View File

@ -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);

View File

@ -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) {

View File

@ -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<int> MainWindow::getMetatileLayerOrder() const {
if (!this->editor || !this->editor->layout)
return QList<int>();
return this->editor->layout->metatileLayerOrder();
}
void MainWindow::setMetatileLayerOrder(const QList<int> &order) {
if (!this->editor || !this->editor->layout || !ScriptUtility::validateMetatileLayerOrder(order))
return;
this->editor->layout->setMetatileLayerOrder(order);
this->refreshAfterPalettePreviewChange();
}
QList<float> MainWindow::getMetatileLayerOpacity() const {
if (!this->editor || !this->editor->layout)
return QList<float>();
return this->editor->layout->metatileLayerOpacity();
}
void MainWindow::setMetatileLayerOpacity(const QList<float> &opacities) {
if (!this->editor || !this->editor->layout)
return;
this->editor->layout->setMetatileLayerOpacity(opacities);
this->refreshAfterPalettePreviewChange();
}
//=====================
// Editing map header
//=====================

View File

@ -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<uint16_t>(metatileId),
this->editor->layout->tileset_primary,
this->editor->layout->tileset_secondary,
this->editor->layout->metatileLayerOrder,
this->editor->layout->metatileLayerOpacity);
QImage image = getMetatileImage(static_cast<uint16_t>(metatileId), this->editor->layout);
if (setTransparency)
image.setColor(0, qRgba(0, 0, 0, 0));
if (this->getOverlay(layer)->addImage(x, y, image))

View File

@ -201,46 +201,36 @@ QList<QString> ScriptUtility::getCustomScripts() {
}
QList<int> ScriptUtility::getMetatileLayerOrder() {
if (!window || !window->editor || !window->editor->layout)
return QList<int>();
return window->editor->layout->metatileLayerOrder;
return Layout::defaultMetatileLayerOrder();
}
void ScriptUtility::setMetatileLayerOrder(QList<int> order) {
if (!window || !window->editor || !window->editor->layout)
return;
bool ScriptUtility::validateMetatileLayerOrder(const QList<int> &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<int> &order) {
if (!validateMetatileLayerOrder(order))
return;
Layout::setDefaultMetatileLayerOrder(order);
if (window) window->refreshAfterPalettePreviewChange();
}
QList<float> ScriptUtility::getMetatileLayerOpacity() {
if (!window || !window->editor || !window->editor->layout)
return QList<float>();
return window->editor->layout->metatileLayerOpacity;
return Layout::defaultMetatileLayerOpacity();
}
void ScriptUtility::setMetatileLayerOpacity(QList<float> order) {
if (!window || !window->editor || !window->editor->layout)
return;
window->editor->layout->metatileLayerOpacity = order;
window->refreshAfterPalettePreviewChange();
void ScriptUtility::setMetatileLayerOpacity(const QList<float> &opacities) {
Layout::setDefaultMetatileLayerOpacity(opacities);
if (window) window->refreshAfterPalettePreviewChange();
}
QList<QString> ScriptUtility::getMapNames() {

View File

@ -16,7 +16,7 @@ const QMap<CallbackType, QString> 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) {

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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<float> &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<float> &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++) {

View File

@ -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, QList<uint16_
this->selection.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;
}

View File

@ -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);