Clean up tile/metatile image changes

This commit is contained in:
GriffinR 2025-07-19 23:10:49 -04:00
parent c5117e458b
commit 15b300f864
31 changed files with 315 additions and 242 deletions

View File

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

View File

@ -76,13 +76,17 @@ public:
void addMetatile(Metatile* metatile);
const QList<Metatile*> &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;

View File

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

View File

@ -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<QRgb> &palette);
QImage getGreyscaleTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset);
void flattenTo4bppImage(QImage * image);

View File

@ -89,8 +89,6 @@ private:
void syncMetatileWidth();
void validateMetatileStart();
void validateMetatileEnd();
uint16_t getExpectedMetatileStart();
uint16_t getExpectedMetatileEnd();
void updateMetatileRange();
void copyRenderSettings();
void restoreRenderSettings();

View File

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

View File

@ -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<uint16_t>, QList<QPair<uint16_t, uint16_t>>);
QPoint getMetatileIdCoordsOnWidget(uint16_t);
void setExternalSelection(int, int, const QList<uint16_t>&, const QList<QPair<uint16_t, uint16_t>>&);
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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -131,7 +131,7 @@ QList<Metatile*> 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<unsigned char>(in.at(0)) |
(static_cast<unsigned char>(in.at(1)) << 8) |
(static_cast<unsigned char>(in.at(2)) << 16) |

View File

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

View File

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

View File

@ -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<QList<QRgb>> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) {
@ -252,10 +252,12 @@ QList<QRgb> 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,5 @@
#include "config.h"
#include "imageproviders.h"
#include "log.h"
#include "editor.h"
#include <QPainter>
@ -62,10 +61,10 @@ QImage getMetatileImage(
const QList<float> &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<QList<QRgb>> 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<QRgb> 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<QRgb> &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;
}

View File

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

View File

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

View File

@ -3,15 +3,16 @@
#include "project.h"
#include <QPainter>
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>({MetatileSelectionItem{true, metatileId}}),
QList<CollisionSelectionItem>(),
};
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<uint16_t> metatiles, QList<QPair<uint16_t, uint16_t>> collisions) {
void MetatileSelector::setExternalSelection(int width, int height, const QList<uint16_t> &metatiles, const QList<QPair<uint16_t, uint16_t>> &collisions) {
this->prefabSelection = false;
this->externalSelection = true;
this->externalSelectionWidth = width;
@ -81,18 +81,18 @@ void MetatileSelector::setExternalSelection(int width, int height, QList<uint16_
this->selection.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<uint16_t>(index);
} else {
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimary);
}
}
QPoint MetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
if (!this->layout->metatileIsValid(metatileId)) {
return QPoint(0, 0);
uint16_t metatileId = static_cast<uint16_t>(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<uint16_t>(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;

View File

@ -232,7 +232,7 @@ QList<uint16_t> 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<uint16_t>(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

View File

@ -1,7 +1,7 @@
#include "selectablepixmapitem.h"
#include <QPainter>
QPoint SelectablePixmapItem::getSelectionDimensions()
QPoint SelectablePixmapItem::getSelectionDimensions() const
{
return QPoint(abs(this->selectionOffsetX) + 1, abs(this->selectionOffsetY) + 1);
}

View File

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

View File

@ -3,6 +3,9 @@
#include "project.h"
#include <QPainter>
// 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<uint16_t>(index);
} else {
return static_cast<uint16_t>(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<uint16_t>(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<uint16_t>(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;

View File

@ -4,7 +4,7 @@
#include <QPainter>
#include <QVector>
QPoint TilesetEditorTileSelector::getSelectionDimensions() {
QPoint TilesetEditorTileSelector::getSelectionDimensions() const {
if (this->externalSelection) {
return QPoint(this->externalSelectionWidth, this->externalSelectionHeight);
} else {