mirror of
https://github.com/huderlem/porymap.git
synced 2026-03-21 17:45:44 -05:00
Add metatile swap
This commit is contained in:
parent
f6f07ca5fc
commit
d0337a7ae3
|
|
@ -173,6 +173,9 @@
|
|||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionUndo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Undo</string>
|
||||
</property>
|
||||
|
|
@ -187,6 +190,9 @@
|
|||
</property>
|
||||
</action>
|
||||
<action name="actionRedo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Redo</string>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -681,6 +681,7 @@
|
|||
<addaction name="actionPaste"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionChange_Metatiles_Count"/>
|
||||
<addaction name="actionSwap_Metatiles"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
|
|
@ -752,6 +753,9 @@
|
|||
</property>
|
||||
</action>
|
||||
<action name="actionUndo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Undo</string>
|
||||
</property>
|
||||
|
|
@ -760,6 +764,9 @@
|
|||
</property>
|
||||
</action>
|
||||
<action name="actionRedo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Redo</string>
|
||||
</property>
|
||||
|
|
@ -784,6 +791,9 @@
|
|||
</property>
|
||||
</action>
|
||||
<action name="actionPaste">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Paste</string>
|
||||
</property>
|
||||
|
|
@ -887,6 +897,17 @@
|
|||
<string>Vertical</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSwap_Metatiles">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Swap Metatiles</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>X</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public:
|
|||
if (head > 0) {
|
||||
return history.at(--head);
|
||||
}
|
||||
head = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -37,9 +38,7 @@ public:
|
|||
|
||||
void push(T commit) {
|
||||
while (head + 1 < history.length()) {
|
||||
T item = history.last();
|
||||
history.removeLast();
|
||||
delete item;
|
||||
delete history.takeLast();
|
||||
}
|
||||
if (saved > head) {
|
||||
saved = -1;
|
||||
|
|
@ -48,7 +47,7 @@ public:
|
|||
head++;
|
||||
}
|
||||
|
||||
T current() {
|
||||
T current() const {
|
||||
if (head < 0 || history.length() == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -59,10 +58,30 @@ public:
|
|||
saved = head;
|
||||
}
|
||||
|
||||
bool isSaved() {
|
||||
bool isSaved() const {
|
||||
return saved == head;
|
||||
}
|
||||
|
||||
int length() const {
|
||||
return history.length();
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return history.isEmpty();
|
||||
}
|
||||
|
||||
int index() const {
|
||||
return head;
|
||||
}
|
||||
|
||||
bool canUndo() const {
|
||||
return head >= 0;
|
||||
}
|
||||
|
||||
bool canRedo() const {
|
||||
return (head + 1) < history.length();
|
||||
}
|
||||
|
||||
private:
|
||||
QList<T> history;
|
||||
int head = -1;
|
||||
|
|
|
|||
|
|
@ -124,10 +124,13 @@ public:
|
|||
bool isWithinBounds(const QRect &rect) const;
|
||||
bool isWithinBorderBounds(int x, int y) const;
|
||||
|
||||
bool getBlock(int x, int y, Block *out);
|
||||
bool getBlock(int x, int y, Block *out) const;
|
||||
void setBlock(int x, int y, Block block, bool enableScriptCallback = false);
|
||||
void setBlockdata(Blockdata blockdata, bool enableScriptCallback = false);
|
||||
|
||||
uint16_t getMetatileId(int x, int y) const;
|
||||
bool setMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback = false);
|
||||
|
||||
void adjustDimensions(const QMargins &margins, bool setNewBlockdata = true);
|
||||
void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true);
|
||||
void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ public:
|
|||
QStringList secondaryTilesetLabels;
|
||||
QStringList tilesetLabelsOrdered;
|
||||
QSet<QString> getPairedTilesetLabels(const Tileset *tileset) const;
|
||||
QSet<QString> getTilesetLayoutIds(const Tileset *priamryTileset, const Tileset *secondaryTileset) const;
|
||||
|
||||
bool readMapGroups();
|
||||
void addNewMapGroup(const QString &groupName);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ private:
|
|||
Tileset *secondaryTileset;
|
||||
|
||||
QList<ColorInputWidget*> colorInputs;
|
||||
QList<History<PaletteHistoryItem*>> palettesHistory;
|
||||
QMap<int, History<PaletteHistoryItem*>> palettesHistory;
|
||||
QMap<int,QSet<int>> unusedColorCache;
|
||||
QPointer<PaletteColorSearch> colorSearchWindow;
|
||||
|
||||
|
|
@ -53,6 +53,7 @@ private:
|
|||
void refreshPaletteId();
|
||||
void commitEditHistory();
|
||||
void commitEditHistory(int paletteId);
|
||||
void updateEditHistoryActions();
|
||||
void restoreWindowState();
|
||||
void invalidateCache();
|
||||
void closeEvent(QCloseEvent*);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ public:
|
|||
virtual void setMaxSelectionSize(int width, int height);
|
||||
QSize maxSelectionSize() { return QSize(this->maxSelectionWidth, this->maxSelectionHeight); }
|
||||
|
||||
void setSelectionStyle(Qt::PenStyle style);
|
||||
|
||||
protected:
|
||||
int cellWidth;
|
||||
int cellHeight;
|
||||
|
|
@ -40,10 +42,13 @@ protected:
|
|||
void select(const QPoint &pos, const QSize &size = QSize(1,1));
|
||||
void select(int x, int y, int width = 1, int height = 1) { select(QPoint(x, y), QSize(width, height)); }
|
||||
void updateSelection(const QPoint &pos);
|
||||
QPoint getCellPos(QPointF);
|
||||
QPoint getCellPos(const QPointF &itemPos);
|
||||
int getBoundedWidth(int width) const { return qBound(1, width, this->maxSelectionWidth); }
|
||||
int getBoundedHeight(int height) const { return qBound(1, height, this->maxSelectionHeight); }
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override;
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
|
||||
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
|
||||
virtual void drawSelectionRect(const QPoint &, const QSize &, Qt::PenStyle style = Qt::SolidLine);
|
||||
virtual void drawSelection();
|
||||
virtual int cellsWide() const { return this->cellWidth ? (pixmap().width() / this->cellWidth) : 0; }
|
||||
virtual int cellsTall() const { return this->cellHeight ? (pixmap().height() / this->cellHeight) : 0; }
|
||||
|
|
@ -53,6 +58,9 @@ signals:
|
|||
|
||||
private:
|
||||
QPoint prevCellPos = QPoint(-1,-1);
|
||||
Qt::PenStyle selectionStyle = Qt::SolidLine;
|
||||
|
||||
void setSelection(const QPoint &pos, const QSize &size);
|
||||
};
|
||||
|
||||
#endif // SELECTABLEPIXMAPITEM_H
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class TilesetEditor;
|
|||
|
||||
class MetatileHistoryItem {
|
||||
public:
|
||||
MetatileHistoryItem() {};
|
||||
MetatileHistoryItem(uint16_t metatileId, Metatile *prevMetatile, Metatile *newMetatile, QString prevLabel, QString newLabel) {
|
||||
this->metatileId = metatileId;
|
||||
this->prevMetatile = prevMetatile;
|
||||
|
|
@ -27,15 +28,24 @@ public:
|
|||
this->prevLabel = prevLabel;
|
||||
this->newLabel = newLabel;
|
||||
}
|
||||
MetatileHistoryItem(uint16_t metatileIdA, uint16_t metatileIdB) {
|
||||
this->metatileId = metatileIdA;
|
||||
this->swapMetatileId = metatileIdB;
|
||||
this->isSwap = true;
|
||||
}
|
||||
~MetatileHistoryItem() {
|
||||
delete this->prevMetatile;
|
||||
delete this->newMetatile;
|
||||
}
|
||||
uint16_t metatileId;
|
||||
Metatile *prevMetatile;
|
||||
Metatile *newMetatile;
|
||||
|
||||
uint16_t metatileId = 0;
|
||||
Metatile *prevMetatile = nullptr;
|
||||
Metatile *newMetatile = nullptr;
|
||||
QString prevLabel;
|
||||
QString newLabel;
|
||||
|
||||
uint16_t swapMetatileId = 0;
|
||||
bool isSwap = false;
|
||||
};
|
||||
|
||||
class TilesetEditor : public QMainWindow
|
||||
|
|
@ -122,8 +132,8 @@ private:
|
|||
void countMetatileUsage();
|
||||
void countTileUsage();
|
||||
void copyMetatile(bool cut);
|
||||
void pasteMetatile(const Metatile * toPaste, QString label);
|
||||
bool replaceMetatile(uint16_t metatileId, const Metatile * src, QString label);
|
||||
void pasteMetatile(const Metatile &toPaste, QString label);
|
||||
bool replaceMetatile(uint16_t metatileId, const Metatile &src, QString label);
|
||||
void commitMetatileChange(Metatile * prevMetatile);
|
||||
void commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel);
|
||||
uint32_t attributeNameToValue(Metatile::Attr attribute, const QString &text, bool *ok);
|
||||
|
|
@ -134,11 +144,17 @@ private:
|
|||
void commitEncounterType();
|
||||
void commitTerrainType();
|
||||
void commitLayerType();
|
||||
void commit(MetatileHistoryItem *item);
|
||||
void updateEditHistoryActions();
|
||||
void setRawAttributesVisible(bool visible);
|
||||
void refreshTileFlips();
|
||||
void refreshPaletteId();
|
||||
void paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly = false);
|
||||
void setMetatileLayerOrientation(Qt::Orientation orientation);
|
||||
void commitMetatileSwap(uint16_t metatileIdA, uint16_t metatileIdB);
|
||||
bool swapMetatiles(uint16_t metatileIdA, uint16_t metatileIdB);
|
||||
void applyMetatileSwapToLayouts(uint16_t metatileIdA, uint16_t metatileIdB);
|
||||
void applyMetatileSwapsToLayouts();
|
||||
|
||||
Ui::TilesetEditor *ui;
|
||||
History<MetatileHistoryItem*> metatileHistory;
|
||||
|
|
@ -162,6 +178,7 @@ private:
|
|||
bool lockSelection = false;
|
||||
QSet<uint16_t> metatileReloadQueue;
|
||||
MetatileImageExporter::Settings *metatileImageExportSettings = nullptr;
|
||||
QList<QPair<uint16_t,uint16_t>> metatileIdSwaps;
|
||||
|
||||
bool save();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,13 @@ public:
|
|||
bool select(uint16_t metatileId);
|
||||
void setTilesets(Tileset*, Tileset*);
|
||||
uint16_t getSelectedMetatileId() const { return this->selectedMetatileId; }
|
||||
void updateSelectedMetatile();
|
||||
QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId) const;
|
||||
|
||||
void setSwapMode(bool enabeled);
|
||||
void addToSwapSelection(uint16_t metatileId);
|
||||
void removeFromSwapSelection(uint16_t metatileId);
|
||||
void clearSwapSelection();
|
||||
|
||||
QVector<uint16_t> usedMetatiles;
|
||||
bool selectorShowUnused = false;
|
||||
bool selectorShowCounts = false;
|
||||
|
|
@ -42,11 +46,15 @@ private:
|
|||
Tileset *primaryTileset = nullptr;
|
||||
Tileset *secondaryTileset = nullptr;
|
||||
uint16_t selectedMetatileId;
|
||||
|
||||
QList<uint16_t> swapMetatileIds;
|
||||
bool inSwapMode = false;
|
||||
|
||||
void updateBasePixmap();
|
||||
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*);
|
||||
bool isValidMetatileId(uint16_t metatileId) const;
|
||||
int numRows(int numMetatiles) const;
|
||||
int numRows() const;
|
||||
void drawGrid();
|
||||
|
|
@ -60,6 +68,7 @@ signals:
|
|||
void hoveredMetatileChanged(uint16_t);
|
||||
void hoveredMetatileCleared();
|
||||
void selectedMetatileChanged(uint16_t);
|
||||
void swapRequested(uint16_t, uint16_t);
|
||||
};
|
||||
|
||||
#endif // TILESETEDITORMETATILESELECTOR_H
|
||||
|
|
|
|||
BIN
resources/icons/swap_cursor.ico
Normal file
BIN
resources/icons/swap_cursor.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -44,6 +44,7 @@
|
|||
<file>icons/refresh.ico</file>
|
||||
<file>icons/shift_cursor.ico</file>
|
||||
<file>icons/shift.ico</file>
|
||||
<file>icons/swap_cursor.ico</file>
|
||||
<file>icons/tall_grass.ico</file>
|
||||
<file>icons/warning.ico</file>
|
||||
<file>icons/minimap.ico</file>
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ QRect Layout::getVisibleRect() const {
|
|||
return area += Project::getPixelViewDistance();
|
||||
}
|
||||
|
||||
bool Layout::getBlock(int x, int y, Block *out) {
|
||||
bool Layout::getBlock(int x, int y, Block *out) const {
|
||||
if (isWithinBounds(x, y)) {
|
||||
int i = y * getWidth() + x;
|
||||
*out = this->blockdata.value(i);
|
||||
|
|
@ -134,6 +134,20 @@ void Layout::setBlockdata(Blockdata newBlockdata, bool enableScriptCallback) {
|
|||
}
|
||||
}
|
||||
|
||||
uint16_t Layout::getMetatileId(int x, int y) const {
|
||||
Block block;
|
||||
return getBlock(x, y, &block) ? block.metatileId() : 0;
|
||||
}
|
||||
|
||||
bool Layout::setMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback) {
|
||||
Block block;
|
||||
if (!getBlock(x, y, &block)) {
|
||||
return false;
|
||||
}
|
||||
setBlock(x, y, Block(metatileId, block.collision(), block.elevation()), enableScriptCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Layout::clearBorderCache() {
|
||||
this->cached_border.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3563,3 +3563,19 @@ QSet<QString> Project::getPairedTilesetLabels(const Tileset *tileset) const {
|
|||
}
|
||||
return pairedLabels;
|
||||
}
|
||||
|
||||
// Returns the set of IDs for the layouts that use the specified tilesets.
|
||||
// nullptr for either tileset is treated as a wildcard (so 'getTilesetLayouts(nullptr, nullptr)' returns all layout IDs).
|
||||
QSet<QString> Project::getTilesetLayoutIds(const Tileset *primaryTileset, const Tileset *secondaryTileset) const {
|
||||
// Note: We're intentioanlly just returning the layout IDs, and not the pointer to the layout.
|
||||
// The layout may not be loaded yet (which isn't obvious), and we should leave it up to the caller to request that.
|
||||
QSet<QString> layoutIds;
|
||||
for (const auto &layout : this->mapLayouts) {
|
||||
if (primaryTileset && primaryTileset->name != layout->tileset_primary_label)
|
||||
continue;
|
||||
if (secondaryTileset && secondaryTileset->name != layout->tileset_secondary_label)
|
||||
continue;
|
||||
layoutIds.insert(layout->id);
|
||||
}
|
||||
return layoutIds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,21 +96,15 @@ void MainWindow::setBlocksFromSelection(int x, int y, bool forceRedraw, bool com
|
|||
int MainWindow::getMetatileId(int x, int y) {
|
||||
if (!this->editor || !this->editor->layout)
|
||||
return 0;
|
||||
Block block;
|
||||
if (!this->editor->layout->getBlock(x, y, &block)) {
|
||||
return 0;
|
||||
}
|
||||
return block.metatileId();
|
||||
return this->editor->layout->getMetatileId(x, y);
|
||||
}
|
||||
|
||||
void MainWindow::setMetatileId(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) {
|
||||
if (!this->editor || !this->editor->layout)
|
||||
return;
|
||||
Block block;
|
||||
if (!this->editor->layout->getBlock(x, y, &block)) {
|
||||
if (!this->editor->layout->setMetatileId(x, y, metatileId)) {
|
||||
return;
|
||||
}
|
||||
this->editor->layout->setBlock(x, y, Block(metatileId, block.collision(), block.elevation()));
|
||||
this->tryCommitMapChanges(commitChanges);
|
||||
this->tryRedrawMapArea(forceRedraw);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ void MetatileLayersItem::hover(const QPoint &pos) {
|
|||
this->prevHoveredPos = pos;
|
||||
|
||||
int tileIndex = posToTileIndex(pos);
|
||||
if (tileIndex < 0 || tileIndex >= this->metatile->tiles.length())
|
||||
if (tileIndex < 0 || !this->metatile || tileIndex >= this->metatile->tiles.length())
|
||||
return;
|
||||
|
||||
emit this->hoveredTileChanged(this->metatile->tiles.at(tileIndex));
|
||||
|
|
|
|||
|
|
@ -28,11 +28,6 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
|
|||
ui->layout_Colors->addWidget(colorInput, i / numColorsPerRow, i % numColorsPerRow);
|
||||
}
|
||||
|
||||
// Setup edit-undo history for each of the palettes.
|
||||
for (int i = 0; i < Project::getNumPalettesTotal(); i++) {
|
||||
this->palettesHistory.append(History<PaletteHistoryItem*>());
|
||||
}
|
||||
|
||||
int bitDepth = porymapConfig.paletteEditorBitDepth;
|
||||
if (bitDepth == 15) {
|
||||
this->ui->bit_depth_15->setChecked(true);
|
||||
|
|
@ -62,6 +57,8 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
|
|||
connect(this->ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteEditor::refreshPaletteId);
|
||||
connect(this->ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteEditor::changedPalette);
|
||||
|
||||
ui->actionRedo->setShortcuts({ui->actionRedo->shortcut(), QKeySequence("Ctrl+Shift+Z")});
|
||||
|
||||
refreshPaletteId();
|
||||
restoreWindowState();
|
||||
}
|
||||
|
|
@ -127,8 +124,12 @@ void PaletteEditor::refreshPaletteId() {
|
|||
refreshColorInputs();
|
||||
|
||||
int paletteId = currentPaletteId();
|
||||
|
||||
if (!this->palettesHistory[paletteId].current()) {
|
||||
// The original colors are saved as an initial commit.
|
||||
commitEditHistory(paletteId);
|
||||
} else {
|
||||
updateEditHistoryActions();
|
||||
}
|
||||
if (this->colorSearchWindow) {
|
||||
this->colorSearchWindow->setPaletteId(paletteId);
|
||||
|
|
@ -154,8 +155,8 @@ void PaletteEditor::commitEditHistory(int paletteId) {
|
|||
for (int i = 0; i < this->colorInputs.length(); i++) {
|
||||
colors.append(this->colorInputs.at(i)->color());
|
||||
}
|
||||
PaletteHistoryItem *commit = new PaletteHistoryItem(colors);
|
||||
this->palettesHistory[paletteId].push(commit);
|
||||
this->palettesHistory[paletteId].push(new PaletteHistoryItem(colors));
|
||||
updateEditHistoryActions();
|
||||
}
|
||||
|
||||
void PaletteEditor::restoreWindowState() {
|
||||
|
|
@ -165,18 +166,27 @@ void PaletteEditor::restoreWindowState() {
|
|||
restoreState(geometry.value("palette_editor_state"));
|
||||
}
|
||||
|
||||
void PaletteEditor::updateEditHistoryActions() {
|
||||
int paletteId = currentPaletteId();
|
||||
// We have an initial commit that shouldn't be available to Undo, so we ignore that.
|
||||
ui->actionUndo->setEnabled(this->palettesHistory[paletteId].index() > 0);
|
||||
ui->actionRedo->setEnabled(this->palettesHistory[paletteId].canRedo());
|
||||
}
|
||||
|
||||
void PaletteEditor::on_actionUndo_triggered() {
|
||||
int paletteId = currentPaletteId();
|
||||
PaletteHistoryItem *prev = this->palettesHistory[paletteId].back();
|
||||
if (prev)
|
||||
setPalette(paletteId, prev->colors);
|
||||
PaletteHistoryItem *commit = this->palettesHistory[paletteId].back();
|
||||
if (!commit) return;
|
||||
setPalette(paletteId, commit->colors);
|
||||
updateEditHistoryActions();
|
||||
}
|
||||
|
||||
void PaletteEditor::on_actionRedo_triggered() {
|
||||
int paletteId = currentPaletteId();
|
||||
PaletteHistoryItem *next = this->palettesHistory[paletteId].next();
|
||||
if (next)
|
||||
setPalette(paletteId, next->colors);
|
||||
PaletteHistoryItem *commit = this->palettesHistory[paletteId].next();
|
||||
if (!commit) return;
|
||||
setPalette(paletteId, commit->colors);
|
||||
updateEditHistoryActions();
|
||||
}
|
||||
|
||||
void PaletteEditor::on_actionImport_Palette_triggered() {
|
||||
|
|
|
|||
|
|
@ -10,12 +10,15 @@ QPoint SelectablePixmapItem::getSelectionStart()
|
|||
return QPoint(x, y);
|
||||
}
|
||||
|
||||
void SelectablePixmapItem::select(const QPoint &pos, const QSize &size)
|
||||
{
|
||||
void SelectablePixmapItem::setSelection(const QPoint &pos, const QSize &size) {
|
||||
this->selectionInitialX = pos.x();
|
||||
this->selectionInitialY = pos.y();
|
||||
this->selectionOffsetX = qBound(0, size.width(), this->maxSelectionWidth - 1);
|
||||
this->selectionOffsetY = qBound(0, size.height(), this->maxSelectionHeight - 1);
|
||||
this->selectionOffsetX = getBoundedWidth(size.width()) - 1;
|
||||
this->selectionOffsetY = getBoundedHeight(size.height()) - 1;
|
||||
}
|
||||
|
||||
void SelectablePixmapItem::select(const QPoint &pos, const QSize &size) {
|
||||
setSelection(pos, size);
|
||||
draw();
|
||||
emit selectionChanged(pos, getSelectionDimensions());
|
||||
}
|
||||
|
|
@ -23,10 +26,7 @@ void SelectablePixmapItem::select(const QPoint &pos, const QSize &size)
|
|||
void SelectablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
QPoint pos = getCellPos(event->pos());
|
||||
this->selectionInitialX = pos.x();
|
||||
this->selectionInitialY = pos.y();
|
||||
this->selectionOffsetX = 0;
|
||||
this->selectionOffsetY = 0;
|
||||
setSelection(pos, QSize(1,1));
|
||||
this->prevCellPos = pos;
|
||||
updateSelection(pos);
|
||||
}
|
||||
|
|
@ -52,12 +52,10 @@ void SelectablePixmapItem::setMaxSelectionSize(int width, int height) {
|
|||
// Update the selection if we shrank below the current selection size.
|
||||
QSize size = getSelectionDimensions();
|
||||
if (size.width() > this->maxSelectionWidth || size.height() > this->maxSelectionHeight) {
|
||||
QPoint origin = getSelectionStart();
|
||||
this->selectionInitialX = origin.x();
|
||||
this->selectionInitialY = origin.y();
|
||||
this->selectionOffsetX = qMin(size.width(), this->maxSelectionWidth) - 1;
|
||||
this->selectionOffsetY = qMin(size.height(), this->maxSelectionHeight) - 1;
|
||||
setSelection(getSelectionStart(), size);
|
||||
draw();
|
||||
// 'draw' is allowed to change the selection position/size,
|
||||
// so call these again rather than keep values from above.
|
||||
emit selectionChanged(getSelectionStart(), getSelectionDimensions());
|
||||
}
|
||||
}
|
||||
|
|
@ -90,18 +88,25 @@ void SelectablePixmapItem::updateSelection(const QPoint &pos) {
|
|||
emit selectionChanged(QPoint(x, y), getSelectionDimensions());
|
||||
}
|
||||
|
||||
QPoint SelectablePixmapItem::getCellPos(QPointF pos) {
|
||||
void SelectablePixmapItem::setSelectionStyle(Qt::PenStyle style) {
|
||||
this->selectionStyle = style;
|
||||
draw();
|
||||
}
|
||||
|
||||
QPoint SelectablePixmapItem::getCellPos(const QPointF &itemPos) {
|
||||
if (this->cellWidth == 0 || this->cellHeight == 0 || pixmap().isNull())
|
||||
return QPoint(0,0);
|
||||
|
||||
int x = qBound(0, static_cast<int>(pos.x()), pixmap().width() - 1);
|
||||
int y = qBound(0, static_cast<int>(pos.y()), pixmap().height() - 1);
|
||||
int x = qBound(0, static_cast<int>(itemPos.x()), pixmap().width() - 1);
|
||||
int y = qBound(0, static_cast<int>(itemPos.y()), pixmap().height() - 1);
|
||||
return QPoint(x / this->cellWidth, y / this->cellHeight);
|
||||
}
|
||||
|
||||
void SelectablePixmapItem::drawSelection() {
|
||||
QPoint origin = this->getSelectionStart();
|
||||
QSize dimensions = this->getSelectionDimensions();
|
||||
drawSelectionRect(getSelectionStart(), getSelectionDimensions(), this->selectionStyle);
|
||||
}
|
||||
|
||||
void SelectablePixmapItem::drawSelectionRect(const QPoint &origin, const QSize &dimensions, Qt::PenStyle style) {
|
||||
QRect selectionRect(origin.x() * this->cellWidth, origin.y() * this->cellHeight, dimensions.width() * this->cellWidth, dimensions.height() * this->cellHeight);
|
||||
|
||||
// If a selection is fully outside the bounds of the selectable area, don't draw anything.
|
||||
|
|
@ -110,12 +115,27 @@ void SelectablePixmapItem::drawSelection() {
|
|||
if (!selectionRect.intersects(pixmap.rect()))
|
||||
return;
|
||||
|
||||
auto fillPen = QPen(QColor(Qt::white));
|
||||
auto borderPen = QPen(QColor(Qt::black));
|
||||
borderPen.setStyle(style);
|
||||
|
||||
QPainter painter(&pixmap);
|
||||
painter.setPen(QColor(0xff, 0xff, 0xff));
|
||||
painter.drawRect(selectionRect.x(), selectionRect.y(), selectionRect.width() - 1, selectionRect.height() - 1);
|
||||
painter.setPen(QColor(0, 0, 0));
|
||||
painter.drawRect(selectionRect.x() - 1, selectionRect.y() - 1, selectionRect.width() + 1, selectionRect.height() + 1);
|
||||
painter.drawRect(selectionRect.x() + 1, selectionRect.y() + 1, selectionRect.width() - 3, selectionRect.height() - 3);
|
||||
if (style == Qt::SolidLine) {
|
||||
painter.setPen(fillPen);
|
||||
painter.drawRect(selectionRect - QMargins(1,1,1,1));
|
||||
painter.setPen(borderPen);
|
||||
painter.drawRect(selectionRect);
|
||||
painter.drawRect(selectionRect - QMargins(2,2,2,2));
|
||||
} else {
|
||||
// Having separately sized rectangles with anything but a
|
||||
// solid line looks a little wonky because the dashes wont align.
|
||||
// For non-solid styles we'll draw a base white rectangle, then draw
|
||||
// a styled black rectangle on top
|
||||
painter.setPen(fillPen);
|
||||
painter.drawRect(selectionRect);
|
||||
painter.setPen(borderPen);
|
||||
painter.drawRect(selectionRect);
|
||||
}
|
||||
|
||||
this->setPixmap(pixmap);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTi
|
|||
this->primaryTileset = new Tileset(*primaryTileset);
|
||||
this->secondaryTileset = new Tileset(*secondaryTileset);
|
||||
if (this->paletteEditor) this->paletteEditor->setTilesets(this->primaryTileset, this->secondaryTileset);
|
||||
this->initMetatileHistory();
|
||||
initMetatileHistory();
|
||||
}
|
||||
|
||||
void TilesetEditor::initAttributesUi() {
|
||||
|
|
@ -217,12 +217,11 @@ void TilesetEditor::setRawAttributesVisible(bool visible) {
|
|||
void TilesetEditor::initMetatileSelector()
|
||||
{
|
||||
this->metatileSelector = new TilesetEditorMetatileSelector(projectConfig.metatileSelectorWidth, this->primaryTileset, this->secondaryTileset, this->layout);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileChanged,
|
||||
this, &TilesetEditor::onHoveredMetatileChanged);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileCleared,
|
||||
this, &TilesetEditor::onHoveredMetatileCleared);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::selectedMetatileChanged,
|
||||
this, &TilesetEditor::onSelectedMetatileChanged);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileChanged, this, &TilesetEditor::onHoveredMetatileChanged);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileCleared, this, &TilesetEditor::onHoveredMetatileCleared);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::selectedMetatileChanged, this, &TilesetEditor::onSelectedMetatileChanged);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::swapRequested, this, &TilesetEditor::commitMetatileSwap);
|
||||
connect(ui->actionSwap_Metatiles, &QAction::triggered, this->metatileSelector, &TilesetEditorMetatileSelector::setSwapMode);
|
||||
|
||||
bool showGrid = porymapConfig.showTilesetEditorMetatileGrid;
|
||||
this->ui->actionMetatile_Grid->setChecked(showGrid);
|
||||
|
|
@ -389,13 +388,6 @@ void TilesetEditor::onWindowActivated() {
|
|||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::initMetatileHistory() {
|
||||
metatileHistory.clear();
|
||||
MetatileHistoryItem *commit = new MetatileHistoryItem(0, nullptr, new Metatile(), QString(), QString());
|
||||
metatileHistory.push(commit);
|
||||
this->hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
void TilesetEditor::reset() {
|
||||
this->setTilesets(this->primaryTileset->name, this->secondaryTileset->name);
|
||||
if (this->paletteEditor)
|
||||
|
|
@ -472,6 +464,7 @@ void TilesetEditor::onHoveredMetatileCleared() {
|
|||
|
||||
void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) {
|
||||
this->metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
if (!this->metatile) return;
|
||||
|
||||
// The scripting API allows users to change metatiles in the project, and these changes are saved to disk.
|
||||
// The Tileset Editor (if open) needs to reflect these changes when the metatile is next displayed.
|
||||
|
|
@ -513,6 +506,8 @@ void TilesetEditor::onHoveredTileCleared() {
|
|||
}
|
||||
|
||||
void TilesetEditor::paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly) {
|
||||
if (!this->metatile) return;
|
||||
|
||||
bool changed = false;
|
||||
Metatile *prevMetatile = new Metatile(*this->metatile);
|
||||
QSize dimensions = this->tileSelector->getSelectionDimensions();
|
||||
|
|
@ -563,7 +558,7 @@ void TilesetEditor::onMetatileLayerSelectionChanged(const QPoint &selectionOrigi
|
|||
for (int i = 0; i < size.width(); i++) {
|
||||
int tileIndex = this->metatileLayersItem->posToTileIndex(selectionOrigin.x() + i, selectionOrigin.y() + j);
|
||||
if (tileIndex < maxTileIndex) {
|
||||
tiles.append(this->metatile->tiles.value(tileIndex));
|
||||
tiles.append(this->metatile ? this->metatile->tiles.value(tileIndex) : Tile());
|
||||
tileIdxs.append(tileIndex);
|
||||
}
|
||||
}
|
||||
|
|
@ -612,8 +607,9 @@ void TilesetEditor::on_lineEdit_metatileLabel_editingFinished()
|
|||
commitMetatileLabel();
|
||||
}
|
||||
|
||||
void TilesetEditor::commitMetatileLabel()
|
||||
{
|
||||
void TilesetEditor::commitMetatileLabel() {
|
||||
if (!this->metatile) return;
|
||||
|
||||
// Only commit if the field has changed.
|
||||
uint16_t metatileId = this->getSelectedMetatileId();
|
||||
QString oldLabel = Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
|
|
@ -625,12 +621,12 @@ void TilesetEditor::commitMetatileLabel()
|
|||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel)
|
||||
{
|
||||
metatileHistory.push(new MetatileHistoryItem(this->getSelectedMetatileId(),
|
||||
prevMetatile, new Metatile(*this->metatile),
|
||||
prevLabel, this->ui->lineEdit_metatileLabel->text()));
|
||||
this->hasUnsavedChanges = true;
|
||||
void TilesetEditor::commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel) {
|
||||
if (!this->metatile) return;
|
||||
|
||||
commit(new MetatileHistoryItem(this->getSelectedMetatileId(),
|
||||
prevMetatile, new Metatile(*this->metatile),
|
||||
prevLabel, this->ui->lineEdit_metatileLabel->text()));
|
||||
}
|
||||
|
||||
void TilesetEditor::commitMetatileChange(Metatile * prevMetatile)
|
||||
|
|
@ -661,8 +657,7 @@ uint32_t TilesetEditor::attributeNameToValue(Metatile::Attr attribute, const QSt
|
|||
}
|
||||
|
||||
void TilesetEditor::commitAttributeFromComboBox(Metatile::Attr attribute, NoScrollComboBox *combo) {
|
||||
if (!this->metatile)
|
||||
return;
|
||||
if (!this->metatile) return;
|
||||
|
||||
bool ok;
|
||||
uint32_t newValue = this->attributeNameToValue(attribute, combo->currentText(), &ok);
|
||||
|
|
@ -683,6 +678,8 @@ void TilesetEditor::commitAttributeFromComboBox(Metatile::Attr attribute, NoScro
|
|||
}
|
||||
|
||||
void TilesetEditor::onRawAttributesEdited() {
|
||||
if (!this->metatile) return;
|
||||
|
||||
uint32_t newAttributes = ui->spinBox_rawAttributesValue->value();
|
||||
if (newAttributes != this->metatile->getAttributes()) {
|
||||
Metatile *prevMetatile = new Metatile(*this->metatile);
|
||||
|
|
@ -732,6 +729,7 @@ bool TilesetEditor::save() {
|
|||
this->lockSelection = true;
|
||||
|
||||
bool success = this->project->saveTilesets(this->primaryTileset, this->secondaryTileset);
|
||||
applyMetatileSwapsToLayouts();
|
||||
emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
|
||||
if (this->paletteEditor) {
|
||||
this->paletteEditor->setTilesets(this->primaryTileset, this->secondaryTileset);
|
||||
|
|
@ -883,8 +881,15 @@ void TilesetEditor::on_actionChange_Metatiles_Count_triggered()
|
|||
if (dialog.exec() == QDialog::Accepted) {
|
||||
this->primaryTileset->resizeMetatiles(primarySpinBox->value());
|
||||
this->secondaryTileset->resizeMetatiles(secondarySpinBox->value());
|
||||
this->metatileSelector->updateSelectedMetatile();
|
||||
this->refresh();
|
||||
|
||||
// Our selected metatile ID may have become invalid. Make sure it's in-bounds.
|
||||
uint16_t metatileId = this->metatileSelector->getSelectedMetatileId();
|
||||
Tileset *tileset = Tileset::getMetatileTileset(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
if (tileset && !tileset->contains(metatileId)) {
|
||||
this->metatileSelector->select(qBound(tileset->firstMetatileId(), metatileId, tileset->lastMetatileId()));
|
||||
}
|
||||
|
||||
refresh();
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -906,11 +911,10 @@ void TilesetEditor::onPaletteEditorChangedPaletteColor() {
|
|||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, QString newLabel)
|
||||
{
|
||||
bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile &src, QString newLabel) {
|
||||
Metatile * dest = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
QString oldLabel = Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
if (!dest || !src || (*dest == *src && oldLabel == newLabel))
|
||||
if (!dest || (*dest == src && oldLabel == newLabel))
|
||||
return false;
|
||||
|
||||
Tileset::setMetatileLabel(metatileId, newLabel, this->primaryTileset, this->secondaryTileset);
|
||||
|
|
@ -921,8 +925,8 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, Q
|
|||
if (this->tileSelector && this->tileSelector->showUnused) {
|
||||
int numTiles = projectConfig.getNumTilesInMetatile();
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
if (src->tiles[i].tileId != dest->tiles[i].tileId) {
|
||||
this->tileSelector->usedTiles[src->tiles[i].tileId] += 1;
|
||||
if (src.tiles[i].tileId != dest->tiles[i].tileId) {
|
||||
this->tileSelector->usedTiles[src.tiles[i].tileId] += 1;
|
||||
this->tileSelector->usedTiles[dest->tiles[i].tileId] -= 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -930,7 +934,7 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, Q
|
|||
}
|
||||
|
||||
this->metatile = dest;
|
||||
*this->metatile = *src;
|
||||
*this->metatile = src;
|
||||
this->metatileSelector->select(metatileId);
|
||||
this->metatileSelector->drawMetatile(metatileId);
|
||||
this->metatileLayersItem->draw();
|
||||
|
|
@ -939,29 +943,52 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, Q
|
|||
return true;
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionUndo_triggered()
|
||||
{
|
||||
MetatileHistoryItem *commit = this->metatileHistory.current();
|
||||
if (!commit) return;
|
||||
Metatile *prev = commit->prevMetatile;
|
||||
if (!prev) return;
|
||||
this->metatileHistory.back();
|
||||
this->replaceMetatile(commit->metatileId, prev, commit->prevLabel);
|
||||
void TilesetEditor::initMetatileHistory() {
|
||||
this->metatileHistory.clear();
|
||||
updateEditHistoryActions();
|
||||
this->hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionRedo_triggered()
|
||||
{
|
||||
void TilesetEditor::commit(MetatileHistoryItem *item) {
|
||||
this->metatileHistory.push(item);
|
||||
updateEditHistoryActions();
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
void TilesetEditor::updateEditHistoryActions() {
|
||||
ui->actionUndo->setEnabled(this->metatileHistory.canUndo());
|
||||
ui->actionRedo->setEnabled(this->metatileHistory.canRedo());
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionUndo_triggered() {
|
||||
MetatileHistoryItem *commit = this->metatileHistory.current();
|
||||
if (!commit) return;
|
||||
this->metatileHistory.back();
|
||||
|
||||
if (commit->isSwap) {
|
||||
swapMetatiles(commit->swapMetatileId, commit->metatileId);
|
||||
} else if (commit->prevMetatile) {
|
||||
replaceMetatile(commit->metatileId, *commit->prevMetatile, commit->prevLabel);
|
||||
};
|
||||
updateEditHistoryActions();
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionRedo_triggered() {
|
||||
MetatileHistoryItem *commit = this->metatileHistory.next();
|
||||
if (!commit) return;
|
||||
this->replaceMetatile(commit->metatileId, commit->newMetatile, commit->newLabel);
|
||||
|
||||
if (commit->isSwap) {
|
||||
swapMetatiles(commit->metatileId, commit->swapMetatileId);
|
||||
} else if (commit->newMetatile) {
|
||||
replaceMetatile(commit->metatileId, *commit->newMetatile, commit->newLabel);
|
||||
}
|
||||
updateEditHistoryActions();
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionCut_triggered()
|
||||
{
|
||||
Metatile * empty = new Metatile(projectConfig.getNumTilesInMetatile());
|
||||
this->copyMetatile(true);
|
||||
this->pasteMetatile(empty, "");
|
||||
delete empty;
|
||||
this->pasteMetatile(Metatile(projectConfig.getNumTilesInMetatile()), "");
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionCopy_triggered()
|
||||
|
|
@ -971,7 +998,9 @@ void TilesetEditor::on_actionCopy_triggered()
|
|||
|
||||
void TilesetEditor::on_actionPaste_triggered()
|
||||
{
|
||||
this->pasteMetatile(this->copiedMetatile, this->copiedMetatileLabel);
|
||||
if (this->copiedMetatile) {
|
||||
this->pasteMetatile(*this->copiedMetatile, this->copiedMetatileLabel);
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::copyMetatile(bool cut) {
|
||||
|
|
@ -984,12 +1013,15 @@ void TilesetEditor::copyMetatile(bool cut) {
|
|||
else
|
||||
*this->copiedMetatile = *toCopy;
|
||||
|
||||
ui->actionPaste->setEnabled(true);
|
||||
|
||||
// Don't try to copy the label unless it's a cut, these should be unique to each metatile.
|
||||
this->copiedMetatileLabel = cut ? Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset) : QString();
|
||||
}
|
||||
|
||||
void TilesetEditor::pasteMetatile(const Metatile * toPaste, QString newLabel)
|
||||
{
|
||||
void TilesetEditor::pasteMetatile(const Metatile &toPaste, QString newLabel) {
|
||||
if (!this->metatile) return;
|
||||
|
||||
Metatile *prevMetatile = new Metatile(*this->metatile);
|
||||
QString prevLabel = this->ui->lineEdit_metatileLabel->text();
|
||||
if (newLabel.isNull()) newLabel = prevLabel; // Don't change the label if one wasn't copied
|
||||
|
|
@ -1092,15 +1124,13 @@ void TilesetEditor::importAdvanceMapMetatiles(Tileset *tileset) {
|
|||
uint16_t metatileId = static_cast<uint16_t>(metatileIdBase + i);
|
||||
QString prevLabel = Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
Metatile *prevMetatile = new Metatile(*tileset->metatileAt(i));
|
||||
MetatileHistoryItem *commit = new MetatileHistoryItem(metatileId,
|
||||
prevMetatile, new Metatile(*metatiles.at(i)),
|
||||
prevLabel, prevLabel);
|
||||
metatileHistory.push(commit);
|
||||
commit(new MetatileHistoryItem(metatileId,
|
||||
prevMetatile, new Metatile(*metatiles.at(i)),
|
||||
prevLabel, prevLabel));
|
||||
}
|
||||
|
||||
tileset->setMetatiles(metatiles);
|
||||
this->refresh();
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionShow_Unused_toggled(bool checked) {
|
||||
|
|
@ -1189,35 +1219,11 @@ void TilesetEditor::countTileUsage() {
|
|||
this->tileSelector->usedTiles.resize(Project::getNumTilesTotal());
|
||||
this->tileSelector->usedTiles.fill(0);
|
||||
|
||||
QSet<Tileset*> primaryTilesets;
|
||||
QSet<Tileset*> secondaryTilesets;
|
||||
|
||||
for (const auto &layoutId : this->project->layoutIds()) {
|
||||
Layout *layout = this->project->getLayout(layoutId);
|
||||
if (layout->tileset_primary_label == this->primaryTileset->name
|
||||
|| layout->tileset_secondary_label == this->secondaryTileset->name) {
|
||||
// need to check metatiles
|
||||
this->project->loadLayoutTilesets(layout);
|
||||
if (layout->tileset_primary && layout->tileset_secondary) {
|
||||
primaryTilesets.insert(layout->tileset_primary);
|
||||
secondaryTilesets.insert(layout->tileset_secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check primary tilesets that are used with this secondary tileset for
|
||||
// reference to secondary tiles in primary metatiles
|
||||
for (const auto &tileset : primaryTilesets) {
|
||||
for (const auto &metatile : tileset->metatiles()) {
|
||||
for (const auto &tile : metatile->tiles) {
|
||||
if (tile.tileId >= Project::getNumTilesPrimary())
|
||||
this->tileSelector->usedTiles[tile.tileId]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do the opposite for primary tiles in secondary metatiles
|
||||
for (Tileset *tileset : secondaryTilesets) {
|
||||
// Count usage of our primary tileset's tiles in the secondary tilesets it gets paired with.
|
||||
QSet<QString> tilesetNames = this->project->getPairedTilesetLabels(this->primaryTileset);
|
||||
for (const auto &tilesetName : tilesetNames) {
|
||||
Tileset *tileset = this->project->getTileset(tilesetName);
|
||||
if (!tileset) continue;
|
||||
for (const auto &metatile : tileset->metatiles()) {
|
||||
for (const auto &tile : metatile->tiles) {
|
||||
if (tile.tileId < Project::getNumTilesPrimary())
|
||||
|
|
@ -1226,17 +1232,16 @@ void TilesetEditor::countTileUsage() {
|
|||
}
|
||||
}
|
||||
|
||||
// check this primary tileset metatiles
|
||||
for (const auto &metatile : this->primaryTileset->metatiles()) {
|
||||
for (const auto &tile : metatile->tiles) {
|
||||
this->tileSelector->usedTiles[tile.tileId]++;
|
||||
}
|
||||
}
|
||||
|
||||
// and the secondary metatiles
|
||||
for (const auto &metatile : this->secondaryTileset->metatiles()) {
|
||||
for (const auto &tile : metatile->tiles) {
|
||||
this->tileSelector->usedTiles[tile.tileId]++;
|
||||
// Count usage of our secondary tileset's tiles in the primary tilesets it gets paired with.
|
||||
tilesetNames = this->project->getPairedTilesetLabels(this->secondaryTileset);
|
||||
for (const auto &tilesetName : tilesetNames) {
|
||||
Tileset *tileset = this->project->getTileset(tilesetName);
|
||||
if (!tileset) continue;
|
||||
for (const auto &metatile : tileset->metatiles()) {
|
||||
for (const auto &tile : metatile->tiles) {
|
||||
if (tile.tileId >= Project::getNumTilesPrimary())
|
||||
this->tileSelector->usedTiles[tile.tileId]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1304,3 +1309,118 @@ void TilesetEditor::redrawTileSelector() {
|
|||
this->ui->scrollArea_Tiles->ensureVisible(pos.x(), pos.y(), viewport->width() / 2, viewport->height() / 2);
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::commitMetatileSwap(uint16_t metatileIdA, uint16_t metatileIdB) {
|
||||
if (swapMetatiles(metatileIdA, metatileIdB)) {
|
||||
commit(new MetatileHistoryItem(metatileIdA, metatileIdB));
|
||||
}
|
||||
}
|
||||
|
||||
bool TilesetEditor::swapMetatiles(uint16_t metatileIdA, uint16_t metatileIdB) {
|
||||
this->metatileSelector->clearSwapSelection();
|
||||
|
||||
QList<Metatile*> metatiles;
|
||||
for (const auto &metatileId : {metatileIdA, metatileIdB}) {
|
||||
Metatile *metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
if (metatile) {
|
||||
metatiles.append(metatile);
|
||||
} else {
|
||||
logError(QString("Failed to load metatile %1 for swap.").arg(Metatile::getMetatileIdString(metatileId)));
|
||||
}
|
||||
}
|
||||
if (metatiles.length() < 2)
|
||||
return false;
|
||||
|
||||
// Swap the metatile data in the tileset
|
||||
Metatile tempMetatile = *metatiles.at(0);
|
||||
QString tempLabel = Tileset::getOwnedMetatileLabel(metatileIdA, this->primaryTileset, this->secondaryTileset);
|
||||
replaceMetatile(metatileIdA, *metatiles.at(1), Tileset::getOwnedMetatileLabel(metatileIdB, this->primaryTileset, this->secondaryTileset));
|
||||
replaceMetatile(metatileIdB, tempMetatile, tempLabel);
|
||||
|
||||
// Record this swap so that we can update the layouts later.
|
||||
// If this is the inverse of the most recent swap (e.g. from Undo), we instead remove that swap to save time.
|
||||
if (!this->metatileIdSwaps.isEmpty()) {
|
||||
auto recentSwapPair = this->metatileIdSwaps.constLast();
|
||||
if (recentSwapPair.first == metatileIdB && recentSwapPair.second == metatileIdA) {
|
||||
this->metatileIdSwaps.removeLast();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this->metatileIdSwaps.append(QPair<uint16_t,uint16_t>(metatileIdA, metatileIdB));
|
||||
return true;
|
||||
}
|
||||
|
||||
// If any metatiles swapped positions, apply the swap to all relevant layouts.
|
||||
// We only do this once changes in the Tileset Editor are saved.
|
||||
void TilesetEditor::applyMetatileSwapsToLayouts() {
|
||||
if (this->metatileIdSwaps.isEmpty())
|
||||
return;
|
||||
|
||||
QProgressDialog progress("", "", 0, this->metatileIdSwaps.length(), this);
|
||||
progress.setAutoClose(true);
|
||||
progress.setWindowModality(Qt::WindowModal);
|
||||
progress.setModal(true);
|
||||
progress.setMinimumDuration(1000);
|
||||
progress.setValue(progress.minimum());
|
||||
|
||||
for (const auto &swapPair : this->metatileIdSwaps) {
|
||||
progress.setLabelText(QString("Swapping metatiles %1 and %2 in map layouts...")
|
||||
.arg(Metatile::getMetatileIdString(swapPair.first))
|
||||
.arg(Metatile::getMetatileIdString(swapPair.second)));
|
||||
applyMetatileSwapToLayouts(swapPair.first, swapPair.second);
|
||||
progress.setValue(progress.value() + 1);
|
||||
}
|
||||
this->metatileIdSwaps.clear();
|
||||
}
|
||||
|
||||
void TilesetEditor::applyMetatileSwapToLayouts(uint16_t metatileIdA, uint16_t metatileIdB) {
|
||||
struct TilesetPair {
|
||||
Tileset* primary = nullptr;
|
||||
Tileset* secondary = nullptr;
|
||||
};
|
||||
TilesetPair tilesets;
|
||||
|
||||
// Get which tilesets our swapped metatiles belong to.
|
||||
auto addSourceTileset = [this](uint16_t metatileId, TilesetPair *tilesets) {
|
||||
if (this->primaryTileset->contains(metatileId)) {
|
||||
tilesets->primary = this->primaryTileset;
|
||||
} else if (this->secondaryTileset->contains(metatileId)) {
|
||||
tilesets->secondary = this->secondaryTileset;
|
||||
} else {
|
||||
// Invalid metatile, shouldn't happen
|
||||
this->metatileSelector->removeFromSwapSelection(metatileId);
|
||||
}
|
||||
};
|
||||
addSourceTileset(metatileIdA, &tilesets);
|
||||
addSourceTileset(metatileIdB, &tilesets);
|
||||
if (!tilesets.primary && !tilesets.secondary) {
|
||||
return;
|
||||
}
|
||||
|
||||
// In each layout that uses the appropriate tileset(s), swap the two metatiles.
|
||||
QSet<QString> layoutIds = this->project->getTilesetLayoutIds(tilesets.primary, tilesets.secondary);
|
||||
for (const auto &layoutId : layoutIds) {
|
||||
Layout *layout = this->project->loadLayout(layoutId);
|
||||
if (!layout) continue;
|
||||
// Perform swap(s) in layout's main data.
|
||||
for (int y = 0; y < layout->height; y++)
|
||||
for (int x = 0; x < layout->width; x++) {
|
||||
uint16_t metatileId = layout->getMetatileId(x, y);
|
||||
if (metatileId == metatileIdA) {
|
||||
layout->setMetatileId(x, y, metatileIdB);
|
||||
} else if (metatileId == metatileIdB) {
|
||||
layout->setMetatileId(x, y, metatileIdA);
|
||||
} else continue;
|
||||
layout->hasUnsavedDataChanges = true;
|
||||
}
|
||||
// Perform swap(s) in layout's border data.
|
||||
for (auto &borderBlock : layout->border) {
|
||||
if (borderBlock.metatileId() == metatileIdA) {
|
||||
borderBlock.setMetatileId(metatileIdB);
|
||||
} else if (borderBlock.metatileId() == metatileIdB) {
|
||||
borderBlock.setMetatileId(metatileIdA);
|
||||
} else continue;
|
||||
layout->hasUnsavedDataChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,15 @@ void TilesetEditorMetatileSelector::draw() {
|
|||
drawDivider();
|
||||
drawFilters();
|
||||
|
||||
drawSelection();
|
||||
if (this->inSwapMode) {
|
||||
for (const auto &metatileId : this->swapMetatileIds) {
|
||||
bool ok;
|
||||
QPoint pos = metatileIdToPos(metatileId, &ok);
|
||||
if (ok) drawSelectionRect(pos, QSize(1,1), Qt::DashLine);
|
||||
}
|
||||
} else if (isValidMetatileId(this->selectedMetatileId)) {
|
||||
drawSelection();
|
||||
}
|
||||
}
|
||||
|
||||
bool TilesetEditorMetatileSelector::select(uint16_t metatileId) {
|
||||
|
|
@ -103,39 +111,76 @@ void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset
|
|||
draw();
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::updateSelectedMetatile() {
|
||||
bool ok;
|
||||
uint16_t metatileId = posToMetatileId(getSelectionStart(), &ok);
|
||||
if (!ok)
|
||||
void TilesetEditorMetatileSelector::addToSwapSelection(uint16_t metatileId) {
|
||||
if (this->swapMetatileIds.contains(metatileId)) {
|
||||
return;
|
||||
}
|
||||
if (this->swapMetatileIds.length() >= 2) {
|
||||
this->swapMetatileIds.clear();
|
||||
}
|
||||
|
||||
this->swapMetatileIds.append(metatileId);
|
||||
draw();
|
||||
|
||||
if (this->swapMetatileIds.length() == 2) {
|
||||
emit swapRequested(this->swapMetatileIds.at(0), this->swapMetatileIds.at(1));
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::removeFromSwapSelection(uint16_t metatileId) {
|
||||
if (this->swapMetatileIds.removeOne(metatileId)) {
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::clearSwapSelection() {
|
||||
if (this->swapMetatileIds.isEmpty())
|
||||
return;
|
||||
this->swapMetatileIds.clear();
|
||||
draw();
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
||||
bool ok;
|
||||
uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok);
|
||||
if (!ok) return;
|
||||
|
||||
if (this->inSwapMode) {
|
||||
if (this->swapMetatileIds.contains(metatileId)) {
|
||||
this->removeFromSwapSelection(metatileId);
|
||||
} else {
|
||||
this->addToSwapSelection(metatileId);
|
||||
}
|
||||
}
|
||||
|
||||
SelectablePixmapItem::mousePressEvent(event);
|
||||
this->selectedMetatileId = metatileId;
|
||||
emit selectedMetatileChanged(this->selectedMetatileId);
|
||||
}
|
||||
|
||||
bool TilesetEditorMetatileSelector::shouldAcceptEvent(QGraphicsSceneMouseEvent *event) {
|
||||
bool ok;
|
||||
posToMetatileId(getCellPos(event->pos()), &ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
||||
if (!shouldAcceptEvent(event)) return;
|
||||
SelectablePixmapItem::mousePressEvent(event);
|
||||
this->updateSelectedMetatile();
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
|
||||
if (!shouldAcceptEvent(event)) return;
|
||||
if (this->inSwapMode) return;
|
||||
|
||||
bool ok;
|
||||
uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok);
|
||||
if (!ok) return;
|
||||
|
||||
SelectablePixmapItem::mouseMoveEvent(event);
|
||||
this->updateSelectedMetatile();
|
||||
this->selectedMetatileId = metatileId;
|
||||
emit selectedMetatileChanged(this->selectedMetatileId);
|
||||
emit hoveredMetatileChanged(this->selectedMetatileId);
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
|
||||
if (!shouldAcceptEvent(event)) return;
|
||||
if (this->inSwapMode) return;
|
||||
|
||||
bool ok;
|
||||
uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok);
|
||||
if (!ok) return;
|
||||
|
||||
SelectablePixmapItem::mouseReleaseEvent(event);
|
||||
this->updateSelectedMetatile();
|
||||
this->selectedMetatileId = metatileId;
|
||||
emit selectedMetatileChanged(this->selectedMetatileId);
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
|
||||
|
|
@ -195,6 +240,12 @@ QPoint TilesetEditorMetatileSelector::metatileIdToPos(uint16_t metatileId, bool
|
|||
return QPoint(0,0);
|
||||
}
|
||||
|
||||
bool TilesetEditorMetatileSelector::isValidMetatileId(uint16_t metatileId) const {
|
||||
bool ok;
|
||||
metatileIdToPos(metatileId, &ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
QPoint TilesetEditorMetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) const {
|
||||
QPoint pos = metatileIdToPos(metatileId);
|
||||
pos.rx() = (pos.x() * this->cellWidth) + (this->cellWidth / 2);
|
||||
|
|
@ -323,3 +374,19 @@ void TilesetEditorMetatileSelector::drawCounts() {
|
|||
|
||||
this->setPixmap(metatilesPixmap);
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::setSwapMode(bool enabled) {
|
||||
if (enabled == this->inSwapMode)
|
||||
return;
|
||||
this->inSwapMode = enabled;
|
||||
this->swapMetatileIds.clear();
|
||||
if (porymapConfig.prettyCursors) {
|
||||
if (enabled) {
|
||||
static const QCursor cursor = QCursor(QPixmap(":/icons/swap_cursor.ico"), 10, 10);
|
||||
setCursor(cursor);
|
||||
} else {
|
||||
unsetCursor();
|
||||
}
|
||||
}
|
||||
draw();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user