diff --git a/include/core/maplayout.h b/include/core/maplayout.h index 2dc3cc24..9f073d20 100644 --- a/include/core/maplayout.h +++ b/include/core/maplayout.h @@ -158,7 +158,6 @@ public: QPixmap render(bool ignoreCache = false, Layout *fromLayout = nullptr, const QRect &bounds = QRect(0, 0, -1, -1)); QPixmap renderCollision(bool ignoreCache); - // QPixmap renderConnection(MapConnection, Layout *); QPixmap renderBorder(bool ignoreCache = false); QPixmap getLayoutItemPixmap(); diff --git a/include/core/tileset.h b/include/core/tileset.h index d592a429..be5bdace 100644 --- a/include/core/tileset.h +++ b/include/core/tileset.h @@ -76,13 +76,17 @@ public: void addMetatile(Metatile* metatile); const QList &metatiles() const { return m_metatiles; } - Metatile* metatileAt(unsigned int i) const { return m_metatiles.at(i); } + const Metatile* metatileAt(unsigned int i) const { return m_metatiles.at(i); } void clearMetatiles(); void resizeMetatiles(int newNumMetatiles); int numMetatiles() const { return m_metatiles.length(); } int maxMetatiles() const; + uint16_t firstMetatileId() const; + uint16_t lastMetatileId() const; + bool contains(uint16_t metatileId) const { return metatileId >= firstMetatileId() && metatileId <= lastMetatileId(); } + int numTiles() const { return m_tiles.length(); } int maxTiles() const; diff --git a/include/scripting.h b/include/scripting.h index aaa2bd02..b2c43ed2 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -23,7 +23,7 @@ enum CallbackType { OnMapResized, OnBorderResized, OnMapShifted, - OnTilesetsChanged, + OnTilesetUpdated, 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_TilesetsChanged(const QString &primaryTilesetName, const QString &secondaryTilesetName); + static void cb_TilesetUpdated(const QString &tilesetName); 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/ui/imageproviders.h b/include/ui/imageproviders.h index 4bdfe801..17b823ce 100644 --- a/include/ui/imageproviders.h +++ b/include/ui/imageproviders.h @@ -37,7 +37,8 @@ QImage getMetatileSheetImage(Tileset *primaryTileset, QImage getTileImage(uint16_t, Tileset*, Tileset*); QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false); -QImage getGreyscaleTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset); +QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, const QList &palette); +QImage getGreyscaleTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset); void flattenTo4bppImage(QImage * image); diff --git a/include/ui/metatileimageexporter.h b/include/ui/metatileimageexporter.h index 9e9b0bf2..d155ad3f 100644 --- a/include/ui/metatileimageexporter.h +++ b/include/ui/metatileimageexporter.h @@ -89,8 +89,6 @@ private: void syncMetatileWidth(); void validateMetatileStart(); void validateMetatileEnd(); - uint16_t getExpectedMetatileStart(); - uint16_t getExpectedMetatileEnd(); void updateMetatileRange(); void copyRenderSettings(); void restoreRenderSettings(); diff --git a/include/ui/metatilelayersitem.h b/include/ui/metatilelayersitem.h index 1ca27c1c..ceea3ff8 100644 --- a/include/ui/metatilelayersitem.h +++ b/include/ui/metatilelayersitem.h @@ -26,7 +26,7 @@ private: signals: void tileChanged(int, int); void selectedTilesChanged(QPoint, int, int); - void hoveredTileChanged(uint16_t); + void hoveredTileChanged(const Tile &tile); void hoveredTileCleared(); protected: void mousePressEvent(QGraphicsSceneMouseEvent*); diff --git a/include/ui/metatileselector.h b/include/ui/metatileselector.h index d7c6a40b..8bc2ef35 100644 --- a/include/ui/metatileselector.h +++ b/include/ui/metatileselector.h @@ -31,7 +31,7 @@ struct MetatileSelection class MetatileSelector: public SelectablePixmapItem { Q_OBJECT public: - MetatileSelector(int numMetatilesWide, Layout *layout): SelectablePixmapItem(Metatile::pixelWidth(), Metatile::pixelHeight()) { + MetatileSelector(int numMetatilesWide, Layout *layout): SelectablePixmapItem(Metatile::pixelSize()) { this->externalSelection = false; this->prefabSelection = false; this->numMetatilesWide = numMetatilesWide; @@ -41,16 +41,16 @@ public: setAcceptHoverEvents(true); } - QPoint getSelectionDimensions() override; + QPoint getSelectionDimensions() const override; void draw() override; void refresh(); bool select(uint16_t metatile); void selectFromMap(uint16_t metatileId, uint16_t collision, uint16_t elevation); - MetatileSelection getMetatileSelection(); + MetatileSelection getMetatileSelection() const { return this->selection; } void setPrefabSelection(MetatileSelection selection); - void setExternalSelection(int, int, QList, QList>); - QPoint getMetatileIdCoordsOnWidget(uint16_t); + void setExternalSelection(int, int, const QList&, const QList>&); + QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId) const; void setLayout(Layout *layout); bool isInternalSelection() const { return (!this->externalSelection && !this->prefabSelection); } @@ -79,8 +79,9 @@ private: void updateBasePixmap(); void updateSelectedMetatiles(); void updateExternalSelectedMetatiles(); - uint16_t getMetatileId(int x, int y) const; - QPoint getMetatileIdCoords(uint16_t); + uint16_t posToMetatileId(int x, int y, bool *ok = nullptr) const; + uint16_t posToMetatileId(const QPoint &pos, bool *ok = nullptr) const; + QPoint metatileIdToPos(uint16_t metatileId, bool *ok = nullptr) const; bool positionIsValid(const QPoint &pos) const; bool selectionIsValid(); void hoverChanged(); diff --git a/include/ui/selectablepixmapitem.h b/include/ui/selectablepixmapitem.h index d497e00d..8323f7e1 100644 --- a/include/ui/selectablepixmapitem.h +++ b/include/ui/selectablepixmapitem.h @@ -19,7 +19,7 @@ public: selectionOffsetX(0), selectionOffsetY(0) {} - virtual QPoint getSelectionDimensions(); + virtual QPoint getSelectionDimensions() const; virtual void draw() = 0; protected: @@ -33,7 +33,8 @@ protected: int selectionOffsetY; QPoint getSelectionStart(); - void select(int, int, int, int); + void select(int x, int y, int width = 0, int height = 0); + void select(const QPoint &pos, const QSize &size = QSize(0,0)) { select(pos.x(), pos.y(), size.width(), size.height()); } void updateSelection(int, int); QPoint getCellPos(QPointF); virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override; diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h index b89709df..f9ccfee4 100644 --- a/include/ui/tileseteditor.h +++ b/include/ui/tileseteditor.h @@ -62,6 +62,7 @@ private slots: void onWindowActivated(); void onHoveredMetatileChanged(uint16_t); void onHoveredMetatileCleared(); + void onHoveredTileChanged(const Tile&); void onHoveredTileChanged(uint16_t); void onHoveredTileCleared(); void onSelectedTilesChanged(); diff --git a/include/ui/tileseteditormetatileselector.h b/include/ui/tileseteditormetatileselector.h index 77a5c4e4..47a70e4b 100644 --- a/include/ui/tileseteditormetatileselector.h +++ b/include/ui/tileseteditormetatileselector.h @@ -18,9 +18,9 @@ public: bool select(uint16_t metatileId); void setTilesets(Tileset*, Tileset*); - uint16_t getSelectedMetatileId(); + uint16_t getSelectedMetatileId() const { return this->selectedMetatileId; } void updateSelectedMetatile(); - QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId); + QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId) const; QVector usedMetatiles; bool selectorShowUnused = false; @@ -44,11 +44,12 @@ private: int numMetatilesWide; int numMetatilesHigh; void updateBasePixmap(); - uint16_t getMetatileId(int x, int y); - QPoint getMetatileIdCoords(uint16_t); + uint16_t posToMetatileId(int x, int y, bool *ok = nullptr) const; + uint16_t posToMetatileId(const QPoint &pos, bool *ok = nullptr) const; + QPoint metatileIdToPos(uint16_t metatileId, bool *ok = nullptr) const; bool shouldAcceptEvent(QGraphicsSceneMouseEvent*); - int numRows(int numMetatiles); - int numRows(); + int numRows(int numMetatiles) const; + int numRows() const; void drawGrid(); void drawDivider(); void drawFilters(); diff --git a/include/ui/tileseteditortileselector.h b/include/ui/tileseteditortileselector.h index 2b1982ba..0ad22b97 100644 --- a/include/ui/tileseteditortileselector.h +++ b/include/ui/tileseteditortileselector.h @@ -18,8 +18,8 @@ public: this->paletteChanged = false; setAcceptHoverEvents(true); } - QPoint getSelectionDimensions(); - void draw(); + QPoint getSelectionDimensions() const override; + void draw() override; void select(uint16_t metatileId); void highlight(uint16_t metatileId); void setTilesets(Tileset*, Tileset*); @@ -36,11 +36,11 @@ public: bool showDivider = false; protected: - void mousePressEvent(QGraphicsSceneMouseEvent*); - void mouseMoveEvent(QGraphicsSceneMouseEvent*); - void mouseReleaseEvent(QGraphicsSceneMouseEvent*); - void hoverMoveEvent(QGraphicsSceneHoverEvent*); - void hoverLeaveEvent(QGraphicsSceneHoverEvent*); + void mousePressEvent(QGraphicsSceneMouseEvent*) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent*) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; private: QPixmap basePixmap; diff --git a/resources/text/script_template.txt b/resources/text/script_template.txt index 8135a853..bee6e56e 100644 --- a/resources/text/script_template.txt +++ b/resources/text/script_template.txt @@ -53,8 +53,8 @@ export function onMapShifted(xDelta, yDelta) { } -// 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) { +// 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) { } diff --git a/src/config.cpp b/src/config.cpp index f3457e9e..6347e7e4 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -298,6 +298,9 @@ uint32_t KeyValueConfigBase::getConfigUint32(const QString &key, const QString & } QColor KeyValueConfigBase::getConfigColor(const QString &key, const QString &value, const QColor &defaultValue) { + if (value.isEmpty()) + return QColor(); + QColor color = QColor("#" + value); if (!color.isValid()) { logWarn(QString("Invalid config value for %1: '%2'. Must be a color in the format 'RRGGBB'. Using default value '%3'.").arg(key).arg(value).arg(defaultValue.name())); @@ -307,7 +310,7 @@ QColor KeyValueConfigBase::getConfigColor(const QString &key, const QString &val } QString KeyValueConfigBase::toConfigColor(const QColor &color) { - return color.name().remove("#"); // Our text config treats '#' as the start of a comment. + return color.isValid() ? color.name().remove("#") : QString(); // Our text config treats '#' as the start of a comment. } PorymapConfig porymapConfig; diff --git a/src/core/advancemapparser.cpp b/src/core/advancemapparser.cpp index 6a8428ec..2e4ef7cd 100644 --- a/src/core/advancemapparser.cpp +++ b/src/core/advancemapparser.cpp @@ -131,7 +131,7 @@ QList AdvanceMapParser::parseMetatiles(const QString &filepath, bool } int attrSize = Metatile::getDefaultAttributesSize(version); - int maxMetatiles = primaryTileset ? Project::getNumMetatilesPrimary() : Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary(); + int maxMetatiles = primaryTileset ? Project::getNumMetatilesPrimary() : Project::getNumMetatilesSecondary(); int numMetatiles = static_cast(in.at(0)) | (static_cast(in.at(1)) << 8) | (static_cast(in.at(2)) << 16) | diff --git a/src/core/mapconnection.cpp b/src/core/mapconnection.cpp index 3e5d82dd..e1478fad 100644 --- a/src/core/mapconnection.cpp +++ b/src/core/mapconnection.cpp @@ -76,15 +76,15 @@ QPoint MapConnection::relativePixelPos(bool clipped) const { int x = 0, y = 0; if (m_direction == "right") { if (m_parentMap) x = m_parentMap->pixelWidth(); - y = m_offset; + y = m_offset * Metatile::pixelHeight(); } else if (m_direction == "down") { - x = m_offset; + x = m_offset * Metatile::pixelWidth(); if (m_parentMap) y = m_parentMap->pixelHeight(); } else if (m_direction == "left") { if (targetMap()) x = !clipped ? -targetMap()->pixelWidth() : -targetMap()->getConnectionRect(m_direction).width(); - y = m_offset; + y = m_offset * Metatile::pixelHeight(); } else if (m_direction == "up") { - x = m_offset; + x = m_offset * Metatile::pixelWidth(); if (targetMap()) y = !clipped ? -targetMap()->pixelHeight() : -targetMap()->getConnectionRect(m_direction).height(); } return QPoint(x, y); diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp index a8d3934d..a965ddb8 100644 --- a/src/core/maplayout.cpp +++ b/src/core/maplayout.cpp @@ -530,6 +530,7 @@ bool Layout::loadBorder(const QString &root) { logError(QString("Failed to load border for %1 from '%2': %3").arg(this->name).arg(path).arg(error)); return false; } + this->border = blockdata; // 0 is an expected border width/height that should be handled, GF used it for the RS layouts in FRLG if (this->border_width <= 0) { @@ -539,10 +540,6 @@ bool Layout::loadBorder(const QString &root) { this->border_height = DEFAULT_BORDER_HEIGHT; } - this->border = blockdata; - this->lastCommitBlocks.border = blockdata; - this->lastCommitBlocks.borderDimensions = QSize(this->border_width, this->border_height); - int expectedSize = this->border_width * this->border_height; if (this->border.count() != expectedSize) { logWarn(QString("%1 border blockdata length %2 does not match dimensions %3x%4 (should be %5). Resizing border blockdata.") @@ -553,6 +550,10 @@ bool Layout::loadBorder(const QString &root) { .arg(expectedSize)); this->border.resize(expectedSize); } + + this->lastCommitBlocks.border = this->border; + this->lastCommitBlocks.borderDimensions = QSize(this->border_width, this->border_height); + return true; } @@ -569,10 +570,7 @@ bool Layout::loadBlockdata(const QString &root) { logError(QString("Failed to load blockdata for %1 from '%2': %3").arg(this->name).arg(path).arg(error)); return false; } - this->blockdata = blockdata; - this->lastCommitBlocks.blocks = blockdata; - this->lastCommitBlocks.layoutDimensions = QSize(this->width, this->height); int expectedSize = this->width * this->height; if (expectedSize <= 0) { @@ -588,6 +586,10 @@ bool Layout::loadBlockdata(const QString &root) { .arg(expectedSize)); this->blockdata.resize(expectedSize); } + + this->lastCommitBlocks.blocks = this->blockdata; + this->lastCommitBlocks.layoutDimensions = QSize(this->width, this->height); + return true; } diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index 729c3334..84a34548 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -94,6 +94,14 @@ void Tileset::resizeMetatiles(int newNumMetatiles) { } } +uint16_t Tileset::firstMetatileId() const { + return this->is_secondary ? Project::getNumMetatilesPrimary() : 0; +} + +uint16_t Tileset::lastMetatileId() const { + return firstMetatileId() + qMax(m_metatiles.length(), 1) - 1; +} + int Tileset::maxMetatiles() const { return this->is_secondary ? Project::getNumMetatilesSecondary() : Project::getNumMetatilesPrimary(); } @@ -213,16 +221,8 @@ QString Tileset::getMetatileLabelPrefix(const QString &name) } bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) { - if (metatileId >= Project::getNumMetatilesTotal()) - return false; - - if (metatileId < Project::getNumMetatilesPrimary() && metatileId >= primaryTileset->numMetatiles()) - return false; - - if (metatileId >= Project::getNumMetatilesPrimary() + secondaryTileset->numMetatiles()) - return false; - - return true; + return (primaryTileset && primaryTileset->contains(metatileId)) + || (secondaryTileset && secondaryTileset->contains(metatileId)); } QList> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) { @@ -252,10 +252,12 @@ QList Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset Tileset *tileset = paletteId < Project::getNumPalettesPrimary() ? primaryTileset : secondaryTileset; - auto palettes = useTruePalettes ? tileset->palettes : tileset->palettePreviews; + if (!tileset) { + return paletteTable; + } - if (paletteId < 0 || paletteId >= palettes.length()){ - logError(QString("Invalid tileset palette id '%1' requested.").arg(paletteId)); + auto palettes = useTruePalettes ? tileset->palettes : tileset->palettePreviews; + if (paletteId < 0 || paletteId >= palettes.length()) { return paletteTable; } @@ -636,20 +638,20 @@ bool Tileset::savePalettes() { bool Tileset::load() { bool success = true; + if (!loadPalettes()) success = false; + if (!loadTilesImage()) success = false; if (!loadMetatiles()) success = false; if (!loadMetatileAttributes()) success = false; - if (!loadTilesImage()) success = false; - if (!loadPalettes()) success = false; return success; } // Because metatile labels are global (and handled by the project) we don't save them here. bool Tileset::save() { bool success = true; + if (!savePalettes()) success = false; + if (!saveTilesImage()) success = false; if (!saveMetatiles()) success = false; if (!saveMetatileAttributes()) success = false; - if (!saveTilesImage()) success = false; - if (!savePalettes()) success = false; return success; } diff --git a/src/editor.cpp b/src/editor.cpp index b043c942..daa490e4 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1372,8 +1372,10 @@ 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); + if (this->layout->tileset_primary_label != prevPrimaryTileset) + Scripting::cb_TilesetUpdated(this->layout->tileset_primary_label); + if (this->layout->tileset_secondary_label != prevSecondaryTileset) + Scripting::cb_TilesetUpdated(this->layout->tileset_secondary_label); return true; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 697ce835..2e2f9440 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1820,7 +1820,9 @@ void MainWindow::currentMetatilesSelectionChanged() { redrawMetatileSelection(); if (this->tilesetEditor) { MetatileSelection selection = editor->metatile_selector_item->getMetatileSelection(); - this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId); + if (!selection.metatileItems.isEmpty()) { + this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId); + } } // Don't scroll to internal selections here, it will disrupt the user while they make their selection. @@ -2603,20 +2605,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/project.cpp b/src/project.cpp index 2957ca2b..e1f074b7 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1558,9 +1558,8 @@ Tileset *Project::createNewTileset(QString name, bool secondary, bool checkerboa tileset->loadTilesImage(&tilesImage); // Create default metatiles - const int numMetatiles = tileset->is_secondary ? (Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()) : Project::getNumMetatilesPrimary(); const int tilesPerMetatile = projectConfig.getNumTilesInMetatile(); - for (int i = 0; i < numMetatiles; ++i) { + for (int i = 0; i < tileset->maxMetatiles(); ++i) { auto metatile = new Metatile(); for(int j = 0; j < tilesPerMetatile; ++j){ Tile tile = Tile(); diff --git a/src/scriptapi/scripting.cpp b/src/scriptapi/scripting.cpp index 5de63221..148a75fe 100644 --- a/src/scriptapi/scripting.cpp +++ b/src/scriptapi/scripting.cpp @@ -16,7 +16,7 @@ const QMap callbackFunctions = { {OnMapResized, "onMapResized"}, {OnBorderResized, "onBorderResized"}, {OnMapShifted, "onMapShifted"}, - {OnTilesetsChanged, "onTilesetsChanged"}, + {OnTilesetUpdated, "onTilesetUpdated"}, {OnMainTabChanged, "onMainTabChanged"}, {OnMapViewTabChanged, "onMapViewTabChanged"}, {OnBorderVisibilityToggled, "onBorderVisibilityToggled"}, @@ -99,15 +99,12 @@ void Scripting::populateGlobalObject(MainWindow *mainWindow) { constants.setProperty("version", version); // Get basic tileset information - int numTilesPrimary = Project::getNumTilesPrimary(); - int numMetatilesPrimary = Project::getNumMetatilesPrimary(); - int numPalettesPrimary = Project::getNumPalettesPrimary(); - constants.setProperty("max_primary_tiles", numTilesPrimary); - constants.setProperty("max_secondary_tiles", Project::getNumTilesTotal() - numTilesPrimary); - constants.setProperty("max_primary_metatiles", numMetatilesPrimary); - constants.setProperty("max_secondary_metatiles", Project::getNumMetatilesTotal() - numMetatilesPrimary); - constants.setProperty("num_primary_palettes", numPalettesPrimary); - constants.setProperty("num_secondary_palettes", Project::getNumPalettesTotal() - numPalettesPrimary); + constants.setProperty("max_primary_tiles", Project::getNumTilesPrimary()); + constants.setProperty("max_secondary_tiles", Project::getNumTilesSecondary()); + constants.setProperty("max_primary_metatiles", Project::getNumMetatilesPrimary()); + constants.setProperty("max_secondary_metatiles", Project::getNumMetatilesSecondary()); + constants.setProperty("num_primary_palettes", Project::getNumPalettesPrimary()); + constants.setProperty("num_secondary_palettes", Project::getNumPalettesSecondary()); constants.setProperty("layers_per_metatile", projectConfig.getNumLayersInMetatile()); constants.setProperty("tiles_per_metatile", projectConfig.getNumTilesInMetatile()); @@ -301,14 +298,13 @@ void Scripting::cb_MapShifted(int xDelta, int yDelta) { instance->invokeCallback(OnMapShifted, args); } -void Scripting::cb_TilesetsChanged(const QString &primaryTilesetName, const QString &secondaryTilesetName) { +void Scripting::cb_TilesetUpdated(const QString &tilesetName) { if (!instance) return; QJSValueList args { - primaryTilesetName, - secondaryTilesetName + tilesetName, }; - instance->invokeCallback(OnTilesetsChanged, args); + instance->invokeCallback(OnTilesetUpdated, args); } void Scripting::cb_MainTabChanged(int oldTab, int newTab) { diff --git a/src/ui/bordermetatilespixmapitem.cpp b/src/ui/bordermetatilespixmapitem.cpp index 4e847b1b..ef3c33db 100644 --- a/src/ui/bordermetatilespixmapitem.cpp +++ b/src/ui/bordermetatilespixmapitem.cpp @@ -44,11 +44,10 @@ void BorderMetatilesPixmapItem::draw() { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { + QImage metatile_image = getMetatileImage(layout->getBorderMetatileId(i, j), layout); int x = i * Metatile::pixelWidth(); int y = j * Metatile::pixelHeight(); - QImage metatile_image = getMetatileImage(layout->getBorderMetatileId(i, j), layout); - QPoint metatile_origin = QPoint(x, y); - painter.drawImage(metatile_origin, metatile_image); + painter.drawImage(x, y, metatile_image); } } diff --git a/src/ui/imageproviders.cpp b/src/ui/imageproviders.cpp index 188398bb..f2e9b26c 100644 --- a/src/ui/imageproviders.cpp +++ b/src/ui/imageproviders.cpp @@ -1,6 +1,5 @@ #include "config.h" #include "imageproviders.h" -#include "log.h" #include "editor.h" #include @@ -62,10 +61,10 @@ QImage getMetatileImage( const QList &layerOpacity, bool useTruePalettes) { - QImage metatile_image(Metatile::pixelWidth(), Metatile::pixelHeight(), QImage::Format_RGBA8888); + QImage metatileImage(Metatile::pixelSize(), QImage::Format_RGBA8888); if (!metatile) { - metatile_image.fill(getInvalidImageColor()); - return metatile_image; + metatileImage.fill(getInvalidImageColor()); + return metatileImage; } QList> palettes = Tileset::getBlockPalettes(primaryTileset, secondaryTileset, useTruePalettes); @@ -75,9 +74,9 @@ QImage getMetatileImage( // The GBA renders transparent pixels using palette 0 color 0. We have this color, // but all 3 games actually overwrite it with black when loading the tileset palettes, // so we have a setting to specify an override transparency color. - metatile_image.fill(projectConfig.transparencyColor.isValid() ? projectConfig.transparencyColor : QColor(palettes.value(0).value(0))); + metatileImage.fill(projectConfig.transparencyColor.isValid() ? projectConfig.transparencyColor : QColor(palettes.value(0).value(0))); - QPainter metatile_painter(&metatile_image); + QPainter painter(&metatileImage); uint32_t layerType = metatile->layerType(); for (const auto &layer : layerOrder) @@ -115,45 +114,31 @@ QImage getMetatileImage( } } - QImage tile_image = getTileImage(tile.tileId, primaryTileset, secondaryTileset); - if (tile_image.isNull()) { - // Some metatiles specify tiles that are outside the valid range. - // The way the GBA will render these depends on what's in memory (which Porymap can't know) - // so we treat them as if they were transparent. - continue; - } + QImage tileImage = getColoredTileImage(tile.tileId, primaryTileset, secondaryTileset, palettes.value(tile.palette)); - // Colorize the metatile tiles with its palette. - if (tile.palette < palettes.length()) { - const QList palette = palettes.value(tile.palette); - for (int j = 0; j < palette.length(); j++) { - tile_image.setColor(j, palette.value(j)); - } - } else { - logWarn(QString("Tile '%1' is referring to invalid palette number: '%2'").arg(tile.tileId).arg(tile.palette)); - } - - QPoint origin = QPoint(x * Tile::pixelWidth(), y * Tile::pixelHeight()); float opacity = layerOpacity.value(layer, 1.0); if (opacity < 1.0) { int alpha = 255 * opacity; - for (int c = 0; c < tile_image.colorCount(); c++) { - QColor color(tile_image.color(c)); + for (int c = 0; c < tileImage.colorCount(); c++) { + QColor color(tileImage.color(c)); color.setAlpha(alpha); - tile_image.setColor(c, color.rgba()); + tileImage.setColor(c, color.rgba()); } } // Color 0 is displayed as transparent. - QColor color(tile_image.color(0)); - color.setAlpha(0); - tile_image.setColor(0, color.rgba()); - tile.flip(&tile_image); - metatile_painter.drawImage(origin, tile_image); - } - metatile_painter.end(); + if (tileImage.colorCount()) { + QColor color(tileImage.color(0)); + color.setAlpha(0); + tileImage.setColor(0, color.rgba()); + } - return metatile_image; + tile.flip(&tileImage); + painter.drawImage(x * Tile::pixelWidth(), y * Tile::pixelHeight(), tileImage); + } + painter.end(); + + return metatileImage; } QImage getTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset) { @@ -164,15 +149,16 @@ QImage getTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondary QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, const QList &palette) { QImage tileImage = getTileImage(tileId, primaryTileset, secondaryTileset); if (tileImage.isNull()) { - tileImage = QImage(Tile::pixelWidth(), Tile::pixelHeight(), QImage::Format_RGBA8888); - QPainter painter(&tileImage); - painter.fillRect(0, 0, tileImage.width(), tileImage.height(), palette.value(0)); + // Some tiles specify tile IDs or palette IDs that are outside the valid range. + // The way the GBA will render these depends on what's in memory (which Porymap can't know) + // so we render them using the invalid color + tileImage = QImage(Tile::pixelSize(), QImage::Format_RGBA8888); + tileImage.fill(getInvalidImageColor()); } else { for (int i = 0; i < 16; i++) { - tileImage.setColor(i, palette.value(i)); + tileImage.setColor(i, palette.value(i, getInvalidImageColor().rgb())); } } - return tileImage; } diff --git a/src/ui/metatileimageexporter.cpp b/src/ui/metatileimageexporter.cpp index 5ffbf8d7..4c3b6b03 100644 --- a/src/ui/metatileimageexporter.cpp +++ b/src/ui/metatileimageexporter.cpp @@ -255,23 +255,20 @@ void MetatileImageExporter::validateMetatileEnd() { ui->spinBox_MetatileEnd->value())); } -uint16_t MetatileImageExporter::getExpectedMetatileStart() { - if (ui->checkBox_PrimaryTileset->isChecked()) return 0; - if (ui->checkBox_SecondaryTileset->isChecked()) return Project::getNumMetatilesPrimary(); - return ui->spinBox_MetatileStart->value(); -} - -uint16_t MetatileImageExporter::getExpectedMetatileEnd() { - if (ui->checkBox_SecondaryTileset->isChecked()) return Project::getNumMetatilesPrimary() + (m_secondaryTileset ? (m_secondaryTileset->numMetatiles() - 1) : 0); - if (ui->checkBox_PrimaryTileset->isChecked()) return m_primaryTileset ? (m_primaryTileset->numMetatiles() - 1) : 0; - return ui->spinBox_MetatileEnd->value(); -} - void MetatileImageExporter::updateMetatileRange() { + Tileset *tileset = nullptr; + if (ui->checkBox_PrimaryTileset->isChecked()) { + tileset = m_primaryTileset; + } else if (ui->checkBox_PrimaryTileset->isChecked()) { + tileset = m_secondaryTileset; + } + if (!tileset) + return; + const QSignalBlocker b_MetatileStart(ui->spinBox_MetatileStart); const QSignalBlocker b_MetatileEnd(ui->spinBox_MetatileEnd); - ui->spinBox_MetatileStart->setValue(getExpectedMetatileStart()); - ui->spinBox_MetatileEnd->setValue(getExpectedMetatileEnd()); + ui->spinBox_MetatileStart->setValue(tileset->firstMetatileId()); + ui->spinBox_MetatileEnd->setValue(tileset->lastMetatileId()); } void MetatileImageExporter::updateTilesetUI() { diff --git a/src/ui/metatilelayersitem.cpp b/src/ui/metatilelayersitem.cpp index 5ce6e68c..bae09ee6 100644 --- a/src/ui/metatilelayersitem.cpp +++ b/src/ui/metatilelayersitem.cpp @@ -129,7 +129,7 @@ void MetatileLayersItem::hoverMoveEvent(QGraphicsSceneHoverEvent * event) { if (tileIndex < 0 || tileIndex >= this->metatile->tiles.length()) return; - emit this->hoveredTileChanged(this->metatile->tiles.at(tileIndex).tileId); + emit this->hoveredTileChanged(this->metatile->tiles.at(tileIndex)); } void MetatileLayersItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { diff --git a/src/ui/metatileselector.cpp b/src/ui/metatileselector.cpp index d8f50410..4fe8060a 100644 --- a/src/ui/metatileselector.cpp +++ b/src/ui/metatileselector.cpp @@ -3,15 +3,16 @@ #include "project.h" #include -QPoint MetatileSelector::getSelectionDimensions() { +QPoint MetatileSelector::getSelectionDimensions() const { if (this->prefabSelection || this->externalSelection) return selection.dimensions; return SelectablePixmapItem::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; + if (!primaryTileset()) + return 0; + return Util::roundUpToMultiple(primaryTileset()->numMetatiles(), this->numMetatilesWide); } void MetatileSelector::updateBasePixmap() { @@ -32,7 +33,11 @@ void MetatileSelector::drawSelection() { } bool MetatileSelector::select(uint16_t metatileId) { - if (!this->layout->metatileIsValid(metatileId)) return false; + bool ok; + QPoint pos = metatileIdToPos(metatileId, &ok); + if (!ok) { + return false; + } this->externalSelection = false; this->prefabSelection = false; this->selection = MetatileSelection{ @@ -41,8 +46,7 @@ bool MetatileSelector::select(uint16_t metatileId) { QList({MetatileSelectionItem{true, metatileId}}), QList(), }; - QPoint coords = this->getMetatileIdCoords(metatileId); - SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0); + SelectablePixmapItem::select(pos); this->updateSelectedMetatiles(); return true; } @@ -67,11 +71,7 @@ void MetatileSelector::refresh() { setLayout(this->layout); } -MetatileSelection MetatileSelector::getMetatileSelection() { - return selection; -} - -void MetatileSelector::setExternalSelection(int width, int height, QList metatiles, QList> collisions) { +void MetatileSelector::setExternalSelection(int width, int height, const QList &metatiles, const QList> &collisions) { this->prefabSelection = false; this->externalSelection = true; this->externalSelectionWidth = width; @@ -81,18 +81,18 @@ void MetatileSelector::setExternalSelection(int width, int height, QListselection.collisionItems.clear(); this->selection.hasCollision = true; this->selection.dimensions = QPoint(width, height); - for (int i = 0; i < metatiles.length(); i++) { - auto collision = collisions.at(i); - this->selection.collisionItems.append(CollisionSelectionItem{true, collision.first, collision.second}); + for (int i = 0; i < qMin(metatiles.length(), collisions.length()); i++) { uint16_t metatileId = metatiles.at(i); + uint16_t collision = collisions.at(i).first; + uint16_t elevation = collisions.at(i).second; + this->selection.collisionItems.append(CollisionSelectionItem{true, collision, elevation}); this->externalSelectedMetatiles.append(metatileId); if (!this->layout->metatileIsValid(metatileId)) metatileId = 0; this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId}); } if (this->selection.metatileItems.length() == 1) { - QPoint coords = this->getMetatileIdCoords(this->selection.metatileItems.first().metatileId); - SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0); + SelectablePixmapItem::select(metatileIdToPos(this->selection.metatileItems.first().metatileId)); } this->draw(); @@ -109,7 +109,9 @@ void MetatileSelector::setPrefabSelection(MetatileSelection selection) { } bool MetatileSelector::positionIsValid(const QPoint &pos) const { - return this->layout->metatileIsValid(getMetatileId(pos.x(), pos.y())); + bool ok; + posToMetatileId(pos, &ok); + return ok; } void MetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { @@ -150,8 +152,14 @@ void MetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { } void MetatileSelector::hoverChanged() { - uint16_t metatileId = this->getMetatileId(this->cellPos.x(), this->cellPos.y()); - emit this->hoveredMetatileSelectionChanged(metatileId); + bool ok; + uint16_t metatileId = posToMetatileId(this->cellPos, &ok); + if (ok) { + emit this->hoveredMetatileSelectionChanged(metatileId); + } else { + emit this->hoveredMetatileSelectionCleared(); + this->cellPos = QPoint(-1, -1); + } } void MetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { @@ -169,9 +177,7 @@ void MetatileSelector::updateSelectedMetatiles() { QPoint origin = this->getSelectionStart(); 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 (!this->layout->metatileIsValid(metatileId)) - metatileId = 0; + uint16_t metatileId = posToMetatileId(origin.x() + i, origin.y() + j); this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId}); } } @@ -190,29 +196,56 @@ void MetatileSelector::updateExternalSelectedMetatiles() { emit selectedMetatilesChanged(); } -uint16_t MetatileSelector::getMetatileId(int x, int y) const { +uint16_t MetatileSelector::posToMetatileId(const QPoint &pos, bool *ok) const { + return posToMetatileId(pos.x(), pos.y(), ok); +} + +uint16_t MetatileSelector::posToMetatileId(int x, int y, bool *ok) const { + if (ok) *ok = true; int index = y * this->numMetatilesWide + x; - int numPrimary = this->numPrimaryMetatilesRounded(); - if (index < numPrimary) { - return static_cast(index); - } else { - return static_cast(Project::getNumMetatilesPrimary() + index - numPrimary); - } -} - -QPoint MetatileSelector::getMetatileIdCoords(uint16_t metatileId) { - if (!this->layout->metatileIsValid(metatileId)) { - return QPoint(0, 0); + uint16_t metatileId = static_cast(index); + if (primaryTileset() && primaryTileset()->contains(metatileId)) { + return metatileId; } - int index = metatileId < Project::getNumMetatilesPrimary() - ? metatileId - : metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded(); - return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); + // There's some extra handling here because we round the tilesets to keep them on separate rows. + // This means if the maximum number of primary metatiles is not divisible by the metatile width + // then the metatiles we used to round the primary tileset would have the index of valid secondary metatiles. + // These need to be ignored, or they'll appear to be duplicates of the subseqeunt secondary metatiles. + int numPrimaryRounded = numPrimaryMetatilesRounded(); + int firstSecondaryRow = numPrimaryRounded / qMax(this->numMetatilesWide, 1); + metatileId = static_cast(Project::getNumMetatilesPrimary() + index - numPrimaryRounded); + if (secondaryTileset() && secondaryTileset()->contains(metatileId) && y >= firstSecondaryRow) { + return metatileId; + } + + if (ok) *ok = false; + return 0; } -QPoint MetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) { - QPoint pos = getMetatileIdCoords(metatileId); +QPoint MetatileSelector::metatileIdToPos(uint16_t metatileId, bool *ok) const { + if (this->numMetatilesWide == 0) { + if (ok) *ok = false; + return QPoint(0,0); + } + + if (primaryTileset() && primaryTileset()->contains(metatileId)) { + if (ok) *ok = true; + int index = metatileId; + return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); + } + if (secondaryTileset() && secondaryTileset()->contains(metatileId)) { + if (ok) *ok = true; + int index = metatileId - Project::getNumMetatilesPrimary() + numPrimaryMetatilesRounded(); + return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); + } + + if (ok) *ok = false; + return QPoint(0,0); +} + +QPoint MetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) const { + QPoint pos = metatileIdToPos(metatileId); pos.rx() = (pos.x() * this->cellWidth) + (this->cellWidth / 2); pos.ry() = (pos.y() * this->cellHeight) + (this->cellHeight / 2); return pos; diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index f9f820ab..07a503ec 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -232,7 +232,7 @@ QList ProjectSettingsEditor::getBorderMetatileIds(bool customSize) { // Custom border size, read metatiles from line edit for (auto s : ui->lineEdit_BorderMetatiles->text().split(",")) { uint16_t metatileId = s.toUInt(nullptr, 0); - metatileIds.append(qMin(metatileId, static_cast(Project::getNumMetatilesTotal() - 1))); + metatileIds.append(qMin(metatileId, Block::getMaxMetatileId())); } } else { // Default border size, read metatiles from spin boxes @@ -475,11 +475,10 @@ void ProjectSettingsEditor::refresh() { ui->checkBox_PreserveMatchingOnlyData->setChecked(projectConfig.preserveMatchingOnlyData); // Radio buttons - // TODO: Replace - /*if (projectConfig.setTransparentPixelsBlack) + if (projectConfig.transparencyColor == QColor(Qt::black)) ui->radioButton_RenderBlack->setChecked(true); else - ui->radioButton_RenderFirstPalColor->setChecked(true);*/ + ui->radioButton_RenderFirstPalColor->setChecked(true); // Set spin box values ui->spinBox_Elevation->setValue(projectConfig.defaultElevation); @@ -575,7 +574,7 @@ void ProjectSettingsEditor::save() { projectConfig.tilesetsHaveCallback = ui->checkBox_OutputCallback->isChecked(); projectConfig.tilesetsHaveIsCompressed = ui->checkBox_OutputIsCompressed->isChecked(); porymapConfig.warpBehaviorWarningDisabled = ui->checkBox_DisableWarning->isChecked(); - //projectConfig.setTransparentPixelsBlack = ui->radioButton_RenderBlack->isChecked(); // TODO + projectConfig.transparencyColor = ui->radioButton_RenderBlack->isChecked() ? QColor(Qt::black) : QColor(); projectConfig.preserveMatchingOnlyData = ui->checkBox_PreserveMatchingOnlyData->isChecked(); // Save spin box settings diff --git a/src/ui/selectablepixmapitem.cpp b/src/ui/selectablepixmapitem.cpp index 3b212579..bd001e59 100644 --- a/src/ui/selectablepixmapitem.cpp +++ b/src/ui/selectablepixmapitem.cpp @@ -1,7 +1,7 @@ #include "selectablepixmapitem.h" #include -QPoint SelectablePixmapItem::getSelectionDimensions() +QPoint SelectablePixmapItem::getSelectionDimensions() const { return QPoint(abs(this->selectionOffsetX) + 1, abs(this->selectionOffsetY) + 1); } diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index e64a6add..3fc80774 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -241,8 +241,9 @@ void TilesetEditor::initMetatileLayersItem() { this, &TilesetEditor::onMetatileLayerTileChanged); connect(this->metatileLayersItem, &MetatileLayersItem::selectedTilesChanged, this, &TilesetEditor::onMetatileLayerSelectionChanged); - connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileChanged, - this, &TilesetEditor::onHoveredTileChanged); + connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileChanged, [this](const Tile &tile) { + onHoveredTileChanged(tile); + }); connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileCleared, this, &TilesetEditor::onHoveredTileCleared); @@ -258,8 +259,9 @@ void TilesetEditor::initMetatileLayersItem() { void TilesetEditor::initTileSelector() { this->tileSelector = new TilesetEditorTileSelector(this->primaryTileset, this->secondaryTileset, projectConfig.getNumLayersInMetatile()); - connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileChanged, - this, &TilesetEditor::onHoveredTileChanged); + connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileChanged, [this](uint16_t tileId) { + onHoveredTileChanged(tileId); + }); connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileCleared, this, &TilesetEditor::onHoveredTileCleared); connect(this->tileSelector, &TilesetEditorTileSelector::selectedTilesChanged, @@ -453,8 +455,17 @@ void TilesetEditor::queueMetatileReload(uint16_t metatileId) { this->metatileReloadQueue.insert(metatileId); } -void TilesetEditor::onHoveredTileChanged(uint16_t tile) { - this->ui->statusbar->showMessage(QString("Tile: %1").arg(Util::toHexString(tile, 3))); +void TilesetEditor::onHoveredTileChanged(const Tile &tile) { + this->ui->statusbar->showMessage(QString("Tile: %1, Palette: %2%3%4") + .arg(Util::toHexString(tile.tileId, 3)) + .arg(QString::number(tile.palette)) + .arg(tile.xflip ? ", X-flipped" : "") + .arg(tile.yflip ? ", Y-flipped" : "") + ); +} + +void TilesetEditor::onHoveredTileChanged(uint16_t tileId) { + this->ui->statusbar->showMessage(QString("Tile: %1").arg(Util::toHexString(tileId, 3))); } void TilesetEditor::onHoveredTileCleared() { @@ -834,7 +845,7 @@ void TilesetEditor::on_actionChange_Metatiles_Count_triggered() primarySpinBox->setMinimum(1); secondarySpinBox->setMinimum(1); primarySpinBox->setMaximum(Project::getNumMetatilesPrimary()); - secondarySpinBox->setMaximum(Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()); + secondarySpinBox->setMaximum(Project::getNumMetatilesSecondary()); primarySpinBox->setValue(this->primaryTileset->numMetatiles()); secondarySpinBox->setValue(this->secondaryTileset->numMetatiles()); form.addRow(new QLabel("Primary Tileset"), primarySpinBox); @@ -1018,7 +1029,7 @@ void TilesetEditor::importAdvanceMapMetatiles(Tileset *tileset) { // TODO: This is crude because it makes a history entry for every newly-imported metatile. // Revisit this when tiles and num metatiles are added to tileset editory history. - int metatileIdBase = primary ? 0 : Project::getNumMetatilesPrimary(); + uint16_t metatileIdBase = tileset->firstMetatileId(); for (int i = 0; i < metatiles.length(); i++) { if (i >= tileset->numMetatiles()) { break; diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp index 2d589195..6c9cc74c 100644 --- a/src/ui/tileseteditormetatileselector.cpp +++ b/src/ui/tileseteditormetatileselector.cpp @@ -3,6 +3,9 @@ #include "project.h" #include +// TODO: This class has a decent bit of overlap with the MetatileSelector class. +// They should be refactored to inherit from a single parent class. + TilesetEditorMetatileSelector::TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Layout *layout) : SelectablePixmapItem(32, 32, 1, 1) { this->primaryTileset = primaryTileset; @@ -13,7 +16,7 @@ TilesetEditorMetatileSelector::TilesetEditorMetatileSelector(Tileset *primaryTil this->usedMetatiles.resize(Project::getNumMetatilesTotal()); } -int TilesetEditorMetatileSelector::numRows(int numMetatiles) { +int TilesetEditorMetatileSelector::numRows(int numMetatiles) const { int numMetatilesHigh = numMetatiles / this->numMetatilesWide; if (numMetatiles % this->numMetatilesWide != 0) { // Round up height for incomplete last row @@ -22,17 +25,21 @@ int TilesetEditorMetatileSelector::numRows(int numMetatiles) { return numMetatilesHigh; } -int TilesetEditorMetatileSelector::numRows() { +int TilesetEditorMetatileSelector::numRows() const { return this->numRows(this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles()); } int TilesetEditorMetatileSelector::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; + if (!this->primaryTileset) + return 0; + return Util::roundUpToMultiple(this->primaryTileset->numMetatiles(), this->numMetatilesWide); } void TilesetEditorMetatileSelector::drawMetatile(uint16_t metatileId) { - QPoint pos = getMetatileIdCoords(metatileId); + bool ok; + QPoint pos = metatileIdToPos(metatileId, &ok); + if (!ok) + return; QPainter painter(&this->baseImage); QImage metatile_image = getMetatileImage( @@ -78,9 +85,11 @@ void TilesetEditorMetatileSelector::draw() { } bool TilesetEditorMetatileSelector::select(uint16_t metatileId) { - if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) return false; - QPoint coords = this->getMetatileIdCoords(metatileId); - SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0); + bool ok; + QPoint pos = metatileIdToPos(metatileId, &ok); + if (!ok) + return false; + SelectablePixmapItem::select(pos); this->selectedMetatileId = metatileId; emit selectedMetatileChanged(metatileId); return true; @@ -95,32 +104,19 @@ void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset } void TilesetEditorMetatileSelector::updateSelectedMetatile() { - QPoint origin = this->getSelectionStart(); - uint16_t metatileId = this->getMetatileId(origin.x(), origin.y()); - if (Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) - this->selectedMetatileId = metatileId; - else - this->selectedMetatileId = Project::getNumMetatilesPrimary() + this->secondaryTileset->numMetatiles() - 1; + bool ok; + uint16_t metatileId = posToMetatileId(getSelectionStart(), &ok); + if (!ok) + return; + + this->selectedMetatileId = metatileId; emit selectedMetatileChanged(this->selectedMetatileId); } -uint16_t TilesetEditorMetatileSelector::getSelectedMetatileId() { - return this->selectedMetatileId; -} - -uint16_t TilesetEditorMetatileSelector::getMetatileId(int x, int y) { - int index = y * this->numMetatilesWide + x; - int numPrimary = numPrimaryMetatilesRounded(); - if (index < numPrimary) { - return static_cast(index); - } else { - return static_cast(Project::getNumMetatilesPrimary() + index - numPrimary); - } -} - bool TilesetEditorMetatileSelector::shouldAcceptEvent(QGraphicsSceneMouseEvent *event) { - QPoint pos = this->getCellPos(event->pos()); - return Tileset::metatileIsValid(getMetatileId(pos.x(), pos.y()), this->primaryTileset, this->secondaryTileset); + bool ok; + posToMetatileId(getCellPos(event->pos()), &ok); + return ok; } void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { @@ -143,29 +139,69 @@ void TilesetEditorMetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent * } void TilesetEditorMetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { - QPoint pos = this->getCellPos(event->pos()); - uint16_t metatileId = this->getMetatileId(pos.x(), pos.y()); - emit this->hoveredMetatileChanged(metatileId); + bool ok; + uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok); + if (ok) { + emit this->hoveredMetatileChanged(metatileId); + } else { + emit this->hoveredMetatileCleared(); + } } void TilesetEditorMetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { emit this->hoveredMetatileCleared(); } -QPoint TilesetEditorMetatileSelector::getMetatileIdCoords(uint16_t metatileId) { - if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) - { - // Invalid metatile id. - return QPoint(0, 0); - } - int index = metatileId < Project::getNumMetatilesPrimary() - ? metatileId - : metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded(); - return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); +uint16_t TilesetEditorMetatileSelector::posToMetatileId(const QPoint &pos, bool *ok) const { + return posToMetatileId(pos.x(), pos.y(), ok); } -QPoint TilesetEditorMetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) { - QPoint pos = getMetatileIdCoords(metatileId); +uint16_t TilesetEditorMetatileSelector::posToMetatileId(int x, int y, bool *ok) const { + if (ok) *ok = true; + int index = y * this->numMetatilesWide + x; + uint16_t metatileId = static_cast(index); + if (this->primaryTileset && this->primaryTileset->contains(metatileId)) { + return metatileId; + } + + // There's some extra handling here because we round the tilesets to keep them on separate rows. + // This means if the maximum number of primary metatiles is not divisible by the metatile width + // then the metatiles we used to round the primary tileset would have the index of valid secondary metatiles. + // These need to be ignored, or they'll appear to be duplicates of the subseqeunt secondary metatiles. + int numPrimaryRounded = numPrimaryMetatilesRounded(); + int firstSecondaryRow = numPrimaryRounded / qMax(this->numMetatilesWide, 1); + metatileId = static_cast(Project::getNumMetatilesPrimary() + index - numPrimaryRounded); + if (this->secondaryTileset && this->secondaryTileset->contains(metatileId) && y >= firstSecondaryRow) { + return metatileId; + } + + if (ok) *ok = false; + return 0; +} + +QPoint TilesetEditorMetatileSelector::metatileIdToPos(uint16_t metatileId, bool *ok) const { + if (this->numMetatilesWide == 0) { + if (ok) *ok = false; + return QPoint(0,0); + } + + if (this->primaryTileset && this->primaryTileset->contains(metatileId)) { + if (ok) *ok = true; + int index = metatileId; + return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); + } + if (this->secondaryTileset && this->secondaryTileset->contains(metatileId)) { + if (ok) *ok = true; + int index = metatileId - Project::getNumMetatilesPrimary() + numPrimaryMetatilesRounded(); + return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide); + } + + if (ok) *ok = false; + return QPoint(0,0); +} + +QPoint TilesetEditorMetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) const { + QPoint pos = metatileIdToPos(metatileId); pos.rx() = (pos.x() * this->cellWidth) + (this->cellWidth / 2); pos.ry() = (pos.y() * this->cellHeight) + (this->cellHeight / 2); return pos; diff --git a/src/ui/tileseteditortileselector.cpp b/src/ui/tileseteditortileselector.cpp index 31ab088e..95d5be6c 100644 --- a/src/ui/tileseteditortileselector.cpp +++ b/src/ui/tileseteditortileselector.cpp @@ -4,7 +4,7 @@ #include #include -QPoint TilesetEditorTileSelector::getSelectionDimensions() { +QPoint TilesetEditorTileSelector::getSelectionDimensions() const { if (this->externalSelection) { return QPoint(this->externalSelectionWidth, this->externalSelectionHeight); } else {